173 lines
4.9 KiB
Python
Executable File
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()
|