pomerge/pomerge/pomerge.py

173 lines
4.9 KiB
Python
Executable File

#!/usr/bin/env python3
"""Merge translations from one or many po files into one or many others.
"""
import json
import os
import sys
from difflib import SequenceMatcher as SM
import polib
from tqdm import tqdm
def find_best_match(possibilities, to_find):
best_match = (0, None)
for possibility in [
possib for possib in possibilities if possib[:5] == to_find[:5]
]:
match = SM(None, possibility, to_find).ratio()
if match > best_match[0]:
best_match = (match, possibility)
if best_match[0] > 0.9:
return best_match[1]
def find_translations(files):
known_translations = {}
# Aggregate all known translations
for po_file in tqdm(files, desc="Searching translations"):
try:
po_file = polib.pofile(po_file)
except OSError as err:
print("Skipping {}: {}".format(po_file, str(err)), sys.stderr)
continue
for entry in po_file:
if "fuzzy" not in entry.flags and entry.msgstr != "":
known_translations[entry.msgid] = entry.msgstr
return known_translations
def read_memory(memory_file):
with open(memory_file) as memory:
return json.load(memory)
def write_memory(translations, memory_file):
with open(memory_file, "w") as memory:
json.dump(translations, memory)
def write_translations(
translations, files, fuzzy_matching=False, mark_as_fuzzy=False, overwrite=True
):
for po_file in tqdm(files, desc="Updating translations"):
po_file = polib.pofile(po_file)
for entry in po_file:
if not overwrite and entry.msgstr and "fuzzy" not in entry.flags:
continue
if (
entry.msgid in translations
and entry.msgstr != translations[entry.msgid]
):
entry.msgstr = translations[entry.msgid]
if "fuzzy" in entry.flags:
entry.flags.remove("fuzzy")
if mark_as_fuzzy:
entry.flags.append("fuzzy")
elif fuzzy_matching:
candidate = find_best_match(list(translations.keys()), entry.msgid)
if candidate:
entry.msgstr = translations[candidate]
entry.flags.append("fuzzy")
po_file.save()
def merge_po_files(
from_files, to_files, fuzzy_matching=False, mark_as_fuzzy=False, overwrite=True
):
"""Find known translations from each given files in from_files,
and update them in files in to_files.
"""
memory_file = os.path.expanduser("~/.pomerge.json")
if from_files:
translations = find_translations(from_files)
else:
translations = read_memory(memory_file)
if to_files:
write_translations(
translations, to_files, fuzzy_matching, mark_as_fuzzy, overwrite
)
else:
write_memory(translations, memory_file)
def main():
import argparse
parser = argparse.ArgumentParser(
description="""Replicate known translations between sets of po files.
To propagate known translation in a single set of po files,
give it as a source and a destination, like:
pomerge --from *.po --to *.po
Translations already existing in the destinations po files will be
updated by translations from the source po files.
To find po files recursively, use the globstar option of bash, or your
shell equivalent, like:
shopt -s globstar
pomerge --from **/*.po --to **/*.po
Giving only --from or only --to works by using a temporary file, so:
pomerge --from a/*.po --to b/*.po
is equivalent to:
pomerge --from a/*.po
pomerge --to b/*.po
"""
)
parser.add_argument(
"--fuzzy-matching",
action="store_true",
help="Also replicate nearly identical strings, "
"but when doing so, add a fuzzy flag.",
)
parser.add_argument(
"--mark-as-fuzzy",
action="store_true",
help="Mark all new translations as fuzzy.",
)
parser.add_argument(
"--no-overwrite",
"-n",
action="store_true",
help="When applying translation, "
"do not overwrite existing translations (apply only to untranslated or fuzzy ones).",
)
parser.add_argument(
"--from-files",
"-f",
nargs="+",
help="File in which known translations are searched",
)
parser.add_argument(
"--to-files",
"-t",
nargs="+",
help="File in which translations will be added or updated",
)
args = parser.parse_args()
if not args.from_files and not args.to_files:
parser.print_help()
exit(1)
merge_po_files(
args.from_files,
args.to_files,
args.fuzzy_matching,
args.mark_as_fuzzy,
overwrite=not args.no_overwrite,
)
if args.to_files:
print("Successfully merged, you probably want to run `powrap -m` now.")
if __name__ == "__main__":
main()