Skip to content

Instantly share code, notes, and snippets.

@flodolo
Created September 23, 2022 07:49
Show Gist options
  • Save flodolo/3bfa3cc4adb75ac94336007f95371dba to your computer and use it in GitHub Desktop.
Save flodolo/3bfa3cc4adb75ac94336007f95371dba to your computer and use it in GitHub Desktop.
Extract and inject iOS translations
#! /usr/bin/env python3
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
"""
Extract translations for a specific string ID in XLIFF file, and store them in
a local JSON file.
"""
from glob import glob
from lxml import etree
import argparse
import os
import json
import sys
NS = {"x": "urn:oasis:names:tc:xliff:document:1.2"}
def main():
parser = argparse.ArgumentParser()
parser.add_argument(
"--reference",
required=True,
dest="reference_locale",
help="Reference locale code",
)
parser.add_argument(
"--path",
required=True,
dest="base_folder",
help="Path to folder including subfolders for all locales",
)
parser.add_argument(
"--id",
required=True,
dest="string_id",
help="String ID to extract",
)
args = parser.parse_args()
reference_locale = args.reference_locale
string_id = args.string_id
# Get a list of files to update (absolute paths)
base_folder = os.path.realpath(args.base_folder)
reference_path = os.path.join(base_folder, reference_locale)
# Get a list of all the reference XLIFF files
reference_files = []
for xliff_path in glob(reference_path + "/**/*.xliff", recursive=True):
reference_files.append(os.path.relpath(xliff_path, reference_path))
if not reference_files:
sys.exit(
f"No reference file found in {os.path.join(base_folder, reference_locale)}"
)
# Get the list of locales, including reference
locales = [
d
for d in os.listdir(base_folder)
if os.path.isdir(os.path.join(base_folder, d)) and not d.startswith(".")
]
locales.sort()
translations = {}
for filename in reference_files:
for locale in locales:
l10n_file = os.path.join(base_folder, locale, filename)
if not os.path.isfile(l10n_file):
continue
# Read XML file
try:
locale_tree = etree.parse(l10n_file)
locale_root = locale_tree.getroot()
except Exception as e:
print(f"ERROR: Can't parse {l10n_file}")
print(e)
continue
for trans_node in locale_root.xpath("//x:trans-unit", namespaces=NS):
for child in trans_node.xpath("./x:target", namespaces=NS):
original_id = trans_node.get("id")
if original_id == args.string_id:
translations[locale] = child.text
# Save translations in a JSON file
with open("translations.json", "w") as fp:
json.dump(translations, fp, indent=2, sort_keys=True, ensure_ascii=False)
if not translations:
print(f"No translations found for ID: {string_id}")
if __name__ == "__main__":
main()
#! /usr/bin/env python3
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
"""
Inject translations into localized XLIFF files
"""
from glob import glob
from lxml import etree
from translate.misc.xml_helpers import reindent
import argparse
import json
import os
import sys
NS = {"x": "urn:oasis:names:tc:xliff:document:1.2"}
def main():
parser = argparse.ArgumentParser()
parser.add_argument(
"--reference",
required=True,
dest="reference_locale",
help="Reference locale code",
)
parser.add_argument(
"--path",
required=True,
dest="base_folder",
help="Path to folder including subfolders for all locales",
)
parser.add_argument(
"--id",
required=True,
dest="string_id",
help="String ID to create",
)
parser.add_argument(
"--file",
required=True,
dest="file_id",
help="File element where to inject the ID",
)
parser.add_argument("locales", nargs="*", help="Locales to process")
args = parser.parse_args()
reference_locale = args.reference_locale
string_id = args.string_id
# Get a list of files to update (absolute paths)
base_folder = os.path.realpath(args.base_folder)
reference_path = os.path.join(base_folder, reference_locale)
# Get a list of all the reference XLIFF files
reference_files = []
for xliff_path in glob(reference_path + "/**/*.xliff", recursive=True):
reference_files.append(os.path.relpath(xliff_path, reference_path))
if not reference_files:
sys.exit(
f"No reference file found in {os.path.join(base_folder, reference_locale)}"
)
# Get the list of locales, including reference
locales = [
d
for d in os.listdir(base_folder)
if os.path.isdir(os.path.join(base_folder, d)) and not d.startswith(".")
]
locales.sort()
# Read translations.json file
with open("translations.json", "r") as fp:
translations = json.load(fp)
for filename in reference_files:
for locale in locales:
l10n_file = os.path.join(base_folder, locale, filename)
if not os.path.isfile(l10n_file):
continue
print(f"Updating {l10n_file}")
# Read localized XML file
try:
locale_tree = etree.parse(l10n_file)
locale_root = locale_tree.getroot()
except Exception as e:
print(f"ERROR: Can't parse {l10n_file}")
print(e)
continue
for body_node in locale_root.xpath("//x:file/x:body", namespaces=NS):
# Continue if it's not the requested file ID
if body_node.getparent().get("original") != args.file_id:
continue
# Check if the ID already exists in the file
existing_node = locale_root.xpath(
f"//x:file/x:body/x:trans-unit[@id='{string_id}']", namespaces=NS
)
if existing_node:
print(f"The string {string_id} already exists in the file.")
continue
# Create translation unit
# <trans-unit id="STRING ID" xml:space="preserve">
# <source>REFERENCE TEXT</source>
# <target>TRANSLATED TEXT</target>
# <note/>
# </trans-unit>
trans_unit = etree.Element("trans-unit")
trans_unit.set("id", string_id)
trans_unit.set(
"{http://www.w3.org/XML/1998/namespace}space", "preserve"
)
source = etree.SubElement(trans_unit, "source")
source.text = translations[reference_locale]
target = etree.SubElement(trans_unit, "target")
target.text = translations.get(locale, "")
etree.SubElement(trans_unit, "note")
body_node.append(trans_unit)
# Replace the existing locale file with the new XML content
with open(l10n_file, "w") as fp:
# Fix identation of XML file
reindent(locale_root)
xliff_content = etree.tostring(
locale_root,
encoding="UTF-8",
xml_declaration=True,
pretty_print=True,
)
fp.write(xliff_content.decode("utf-8"))
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment