mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-04-20 12:49:38 +08:00
211 lines
9.3 KiB
Python
211 lines
9.3 KiB
Python
# Copyright (c) 2021 Ultimaker B.V.
|
|
# Cura is released under the terms of the LGPLv3 or higher.
|
|
|
|
import argparse # To get the source directory from command line arguments.
|
|
import io # To fix encoding issues in Windows
|
|
import os # To find files from the source.
|
|
import os.path # To find files from the source and the destination path.
|
|
|
|
cura_files = {"cura", "fdmprinter.def.json", "fdmextruder.def.json"}
|
|
uranium_files = {"uranium"}
|
|
|
|
def lionbridge_import(source: str) -> None:
|
|
"""Imports translation files from Lionbridge.
|
|
|
|
Lionbridge has a bit of a weird export feature. It exports it to the same
|
|
file type as what we imported, so that's a .pot file. However this .pot file
|
|
only contains the translations, so the header is completely empty. We need
|
|
to merge those translations into our existing files so that the header is
|
|
preserved.
|
|
"""
|
|
|
|
print("Importing from:", source)
|
|
print("Importing to Cura:", destination_cura())
|
|
print("Importing to Uranium:", destination_uranium())
|
|
|
|
for language in (directory for directory in os.listdir(source) if os.path.isdir(os.path.join(source, directory))):
|
|
print("================ Processing language:", language, "================")
|
|
directory = os.path.join(source, language)
|
|
for file_pot in (file for file in os.listdir(directory) if file.endswith(".pot")):
|
|
source_file = file_pot[:-4] #Strip extension.
|
|
if source_file in cura_files:
|
|
destination_file = os.path.join(destination_cura(), language.replace("-", "_"), source_file + ".po")
|
|
print("Merging", source_file, "(Cura) into", destination_file)
|
|
elif source_file in uranium_files:
|
|
destination_file = os.path.join(destination_uranium(), language.replace("-", "_"), source_file + ".po")
|
|
print("Merging", source_file, "(Uranium) into", destination_file)
|
|
else:
|
|
raise Exception("Unknown file: " + source_file + "... Is this Cura or Uranium?")
|
|
|
|
with io.open(os.path.join(directory, file_pot), encoding = "utf8") as f:
|
|
source_str = f.read()
|
|
with io.open(destination_file, encoding = "utf8") as f:
|
|
destination_str = f.read()
|
|
result = merge(source_str, destination_str)
|
|
with io.open(destination_file, "w", encoding = "utf8") as f:
|
|
f.write(result)
|
|
|
|
|
|
def destination_cura() -> str:
|
|
"""Gets the destination path to copy the translations for Cura to.
|
|
|
|
:return: Destination path for Cura.
|
|
"""
|
|
return os.path.abspath(os.path.join(__file__, "..", "..", "resources", "i18n"))
|
|
|
|
|
|
def destination_uranium() -> str:
|
|
"""Gets the destination path to copy the translations for Uranium to.
|
|
|
|
:return: Destination path for Uranium.
|
|
"""
|
|
try:
|
|
import UM
|
|
except ImportError:
|
|
relative_path = os.path.join(__file__, "..", "..", "..", "Uranium", "resources", "i18n", "uranium.pot")
|
|
absolute_path = os.path.abspath(relative_path)
|
|
if os.path.exists(absolute_path):
|
|
absolute_path = os.path.abspath(os.path.join(absolute_path, ".."))
|
|
print("Uranium is at:", absolute_path)
|
|
return absolute_path
|
|
else:
|
|
raise Exception("Can't find Uranium. Please put UM on the PYTHONPATH or put the Uranium folder next to the Cura folder. Looked for: " + absolute_path)
|
|
return os.path.abspath(os.path.join(UM.__file__, "..", "..", "resources", "i18n"))
|
|
|
|
|
|
def merge(source: str, destination: str) -> str:
|
|
"""Merges translations from the source file into the destination file if they
|
|
|
|
were missing in the destination file.
|
|
:param source: The contents of the source .po file.
|
|
:param destination: The contents of the destination .po file.
|
|
"""
|
|
result_lines = []
|
|
last_destination = {
|
|
"msgctxt": "\"\"\n",
|
|
"msgid": "\"\"\n",
|
|
"msgstr": "\"\"\n",
|
|
"msgid_plural": "\"\"\n"
|
|
}
|
|
|
|
current_state = "none"
|
|
for line in destination.split("\n"):
|
|
if line.startswith("msgctxt \""):
|
|
current_state = "msgctxt"
|
|
line = line[8:]
|
|
last_destination[current_state] = ""
|
|
elif line.startswith("msgid \""):
|
|
current_state = "msgid"
|
|
line = line[6:]
|
|
last_destination[current_state] = ""
|
|
elif line.startswith("msgstr \""):
|
|
current_state = "msgstr"
|
|
line = line[7:]
|
|
last_destination[current_state] = ""
|
|
elif line.startswith("msgid_plural \""):
|
|
current_state = "msgid_plural"
|
|
line = line[13:]
|
|
last_destination[current_state] = ""
|
|
|
|
if line.startswith("\"") and line.endswith("\""):
|
|
last_destination[current_state] += line + "\n"
|
|
else: #White lines or comment lines trigger us to search for the translation in the source file.
|
|
if last_destination["msgstr"] == "\"\"\n" and last_destination["msgid"] != "\"\"\n": #No translation for this yet!
|
|
last_destination["msgstr"] = find_translation(source, last_destination["msgctxt"], last_destination["msgid"]) #Actually place the translation in.
|
|
if last_destination["msgctxt"] != "\"\"\n" or last_destination["msgid"] != "\"\"\n" or last_destination["msgid_plural"] != "\"\"\n" or last_destination["msgstr"] != "\"\"\n":
|
|
if last_destination["msgctxt"] != "\"\"\n":
|
|
result_lines.append("msgctxt {msgctxt}".format(msgctxt = last_destination["msgctxt"][:-1])) #The [:-1] to strip the last newline.
|
|
result_lines.append("msgid {msgid}".format(msgid = last_destination["msgid"][:-1]))
|
|
if last_destination["msgid_plural"] != "\"\"\n":
|
|
result_lines.append("msgid_plural {msgid_plural}".format(msgid_plural = last_destination["msgid_plural"][:-1]))
|
|
else:
|
|
result_lines.append("msgstr {msgstr}".format(msgstr = last_destination["msgstr"][:-1]))
|
|
last_destination = {
|
|
"msgctxt": "\"\"\n",
|
|
"msgid": "\"\"\n",
|
|
"msgstr": "\"\"\n",
|
|
"msgid_plural": "\"\"\n"
|
|
}
|
|
|
|
result_lines.append(line) #This line itself.
|
|
return "\n".join(result_lines)
|
|
|
|
|
|
def find_translation(source: str, msgctxt: str, msgid: str) -> str:
|
|
"""Finds a translation in the source file.
|
|
|
|
:param source: The contents of the source .po file.
|
|
:param msgctxt: The ctxt of the translation to find.
|
|
:param msgid: The id of the translation to find.
|
|
"""
|
|
last_source = {
|
|
"msgctxt": "\"\"\n",
|
|
"msgid": "\"\"\n",
|
|
"msgstr": "\"\"\n",
|
|
"msgid_plural": "\"\"\n"
|
|
}
|
|
|
|
current_state = "none"
|
|
for line in source.split("\n"):
|
|
if line.startswith("msgctxt \""):
|
|
current_state = "msgctxt"
|
|
line = line[8:]
|
|
last_source[current_state] = ""
|
|
elif line.startswith("msgid \""):
|
|
current_state = "msgid"
|
|
line = line[6:]
|
|
last_source[current_state] = ""
|
|
elif line.startswith("msgstr \""):
|
|
current_state = "msgstr"
|
|
line = line[7:]
|
|
last_source[current_state] = ""
|
|
elif line.startswith("msgid_plural \""):
|
|
current_state = "msgid_plural"
|
|
line = line[13:]
|
|
last_source[current_state] = ""
|
|
|
|
if line.startswith("\"") and line.endswith("\""):
|
|
last_source[current_state] += line + "\n"
|
|
else: #White lines trigger us to process this translation. Is it the correct one?
|
|
#Process the source and destination keys for comparison independent of newline technique.
|
|
source_ctxt = "".join((line.strip()[1:-1] for line in last_source["msgctxt"].split("\n")))
|
|
source_id = "".join((line.strip()[1:-1] for line in last_source["msgid"].split("\n")))
|
|
dest_ctxt = "".join((line.strip()[1:-1] for line in msgctxt.split("\n")))
|
|
dest_id = "".join((line.strip()[1:-1] for line in msgid.split("\n")))
|
|
|
|
if source_ctxt == dest_ctxt and source_id == dest_id:
|
|
if last_source["msgstr"] == "\"\"\n" and last_source["msgid_plural"] == "\"\"\n":
|
|
print("!!! Empty translation for {" + dest_ctxt + "}", dest_id, "!!!")
|
|
return last_source["msgstr"]
|
|
|
|
last_source = {
|
|
"msgctxt": "\"\"\n",
|
|
"msgid": "\"\"\n",
|
|
"msgstr": "\"\"\n",
|
|
"msgid_plural": "\"\"\n"
|
|
}
|
|
|
|
#Still here? Then the entire msgctxt+msgid combination was not found at all.
|
|
print("!!! Missing translation for {" + msgctxt.strip() + "}", msgid.strip(), "!!!")
|
|
return "\"\"\n"
|
|
|
|
if __name__ == "__main__":
|
|
print("""Usage instructions:
|
|
|
|
1. In Smartling, in the Cura project go to the "Files" tab.
|
|
2. Select all four .pot files.
|
|
3. In the expando above the file list, choose "Download Selected".
|
|
4. In the pop-up, select:
|
|
- Current translations
|
|
- Select all languages
|
|
- Organize files: Folders for languages.
|
|
5. Download that and extract the .zip archive somewhere.
|
|
6. Start this script, with the location you extracted to as a parameter, e.g.:
|
|
python3 /path/to/lionbridge_import.py /home/username/Desktop/cura_translations
|
|
""")
|
|
|
|
argparser = argparse.ArgumentParser(description = "Import translation files from Lionbridge.")
|
|
argparser.add_argument("source")
|
|
args = argparser.parse_args()
|
|
lionbridge_import(args.source)
|