iOS: add update_translations.py script

Run with `python3 update_translations.py` to extract localizable strings from source files and merge them into base localizations (en.lproj)
This commit is contained in:
Andrej Mihajlov committed 2021-07-07 09:45:49 +02:00
1 parent 86bb6d0efb
commit f3ecfb5ecd
3 files changed
+250

No files matched your search

+14
View File
@@ -0,0 +1,14 @@
[FORMAT]
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
# tab).
indent-string=' '
# Number of spaces of indent required inside a hanging or continued line.
indent-after-paren=2
[MESSAGES CONTROL]
# Disable the message, report, category or checker with the given id(s). You
# can either give multiple identifier separated by comma (,) or put this option
# multiple time (only on the command line, not in the configuration file where
# it should appear only once).
+42
View File
@@ -0,0 +1,42 @@
nslocalized==0.2.0 \
--hash=sha256:46d6f776b26e9da0e0d0c759d99782884882cade59d5c935005a85cba654db9e
## The following requirements were added by pip freeze:
astroid==2.6.2
cffi==1.14.5 \
--hash=sha256:06d7cd1abac2ffd92e65c0609661866709b4b2d82dd15f611e602b9b188b0b69 \
--hash=sha256:0857f0ae312d855239a55c81ef453ee8fd24136eaba8e87a2eceba644c0d4c06 \
--hash=sha256:0f861a89e0043afec2a51fd177a567005847973be86f709bbb044d7f42fc4e05 \
--hash=sha256:2894f2df484ff56d717bead0a5c2abb6b9d2bf26d6960c4604d5c48bbc30ee73 \
--hash=sha256:35f27e6eb43380fa080dccf676dece30bef72e4a67617ffda586641cd4508d49 \
--hash=sha256:9cf8022fb8d07a97c178b02327b284521c7708d7c71a9c9c355c178ac4bbd3d4 \
--hash=sha256:9ff227395193126d82e60319a673a037d5de84633f11279e336f9c0f189ecc62 \
--hash=sha256:cc5a8e069b9ebfa22e26d0e6b97d6f9781302fe7f4f2b8776c3e1daea35f1adc \
--hash=sha256:cd2868886d547469123fadc46eac7ea5253ea7fcb139f12e1dfc2bbd406427d1 \
--hash=sha256:fd78e5fee591709f32ef6edb9a015b4aa1a5022598e36227500c8f4e02328d9c
chardet==4.0.0 \
--hash=sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa \
--hash=sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5
click==8.0.1 \
--hash=sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a \
--hash=sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6
GDAL==3.3.1 \
--hash=sha256:776ad8f499919d6824b7ba3ae8bd53b2df5d73a14dc047ebc8f33f9507c8dcc5
hashin==0.15.0
igenstrings==1.1.0
isort==5.9.1
lazy-object-proxy==1.6.0
mccabe==0.6.1
packaging==21.0
pip-api==0.0.20
pycparser==2.20
pylint==2.9.3
pyparsing==2.4.7
six==1.16.0 \
--hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \
--hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254
tinycss2==1.1.0 \
--hash=sha256:0353b5234bcaee7b1ac7ca3dea7e02cd338a9f8dcbb8f2dcd32a5795ec1e5f9a \
--hash=sha256:fbdcac3044d60eb85fdb2aa840ece43cf7dbe798e373e6ee0be545d4d134e18a
toml==0.10.2
webencodings==0.5.1
wrapt==1.12.1
+194
View File
@@ -0,0 +1,194 @@
#!/usr/bin/env python3
"""
A helper script to parse NSLocalizedString in Swift source files and extract and merge new
translations with the existing ones.
"""
import os
from os import path
from subprocess import Popen, PIPE
import shutil
from nslocalized import StringTable
# Current script dir
SCRIPT_DIR = path.dirname(path.realpath(__file__))
# Path to directory with source files (Swift)
SOURCE_PATH = path.join(SCRIPT_DIR, "MullvadVPN")
# Path to directory with base localizations
BASE_LANGUAGE_PATH = path.join(SOURCE_PATH, "en.lproj")
# Path to directory with the output of genstrings tool.
GENSTRINGS_OUTPUT_PATH = path.join(SCRIPT_DIR, "genstrings-out")
# Output encoding for strings files.
#
# By default genstrings tool outputs text in utf-16, which git recognizes as binary, therefore
# rendering diff capability useless.
#
# Store localization files in utf-8 to fix this. Xcode automatically transcodes localization
# files to utf-16 during the build phase.
OUTPUT_ENCOODING = "utf_8"
def check_file_extension(file, expected_extension):
"""
Returns True if the file extension matches the expected one.
"""
(_basename, ext) = os.path.splitext(file)
return ext == expected_extension
def get_source_files():
"""
Find all Swift source files recursively.
"""
results = []
for root, _dirs, files in os.walk(SOURCE_PATH):
for file in files:
if check_file_extension(file, ".swift"):
results.append(path.join(root, file))
return results
def get_strings_files(dir_path):
"""
Find all .strings files within the given directory.
"""
results = []
for file in os.listdir(dir_path):
if check_file_extension(file, ".strings"):
results.append(file)
return results
def create_empty_output_dir():
"""
Creates empty directory for output of genstrings tool.
"""
# Wipe out old files
delete_output_dir()
# Re-create out directory
os.mkdir(GENSTRINGS_OUTPUT_PATH)
def delete_output_dir():
"""
Delete directory used for genstrings output
"""
if path.exists(GENSTRINGS_OUTPUT_PATH):
shutil.rmtree(GENSTRINGS_OUTPUT_PATH)
def extract_translations():
"""
Extract translations from sources using genstrings tool.
"""
# Get Swift source files
source_files = get_source_files()
# Genstrings utility comes with Xcode and used for extracting the localizable strings from source
# files and producing string tables.
args = (
"genstrings", "-o", GENSTRINGS_OUTPUT_PATH,
*source_files
)
(exit_code, errors) = run_program(*args)
if exit_code == 0:
print("Genstrings finished without errors.")
else:
print("Genstrings exited with {}: {}".format(exit_code, errors.decode().strip()))
def merge_translations():
"""
Merge string tables, delete stale ones and copy new ones.
"""
# Existing string tables
existing_string_tables = get_strings_files(BASE_LANGUAGE_PATH)
# Newly generated string tables
new_string_tables = get_strings_files(GENSTRINGS_OUTPUT_PATH)
# String tables that will be merged
merge_string_tables = []
# Detect new string tables
for table_name in new_string_tables:
if table_name not in existing_string_tables:
src = path.join(GENSTRINGS_OUTPUT_PATH, table_name)
dst = path.join(BASE_LANGUAGE_PATH, table_name)
print("Copying {} to {}".format(src, dst))
new_table = StringTable.read(src)
new_table.write(dst, encoding=OUTPUT_ENCOODING)
# Detect removed string tables
for table_name in existing_string_tables:
if table_name in new_string_tables:
merge_string_tables.append(table_name)
else:
filepath = path.join(BASE_LANGUAGE_PATH, table_name)
print("Removing {}".format(filepath))
os.unlink(filepath)
# Merge remaining string tables
for table_name in merge_string_tables:
new_table_path = path.join(GENSTRINGS_OUTPUT_PATH, table_name)
base_table_path = path.join(BASE_LANGUAGE_PATH, table_name)
merge_two_tables(base_table_path, new_table_path)
def merge_two_tables(base_table_path, new_table_path):
"""
Merge new table into base table.
"""
# Existing string table previously generated from sources
base_table = StringTable.read(base_table_path)
# New string table generated from sources
new_table = StringTable.read(new_table_path)
print("Merging {} into {}".format(new_table_path, base_table_path))
# Iterate through newly generated table and preserve existing translations.
for new_key in new_table.strings:
base_entry = base_table.lookup(new_key)
new_entry = new_table.lookup(new_key)
if base_entry is not None:
new_entry.target = base_entry.target
print("Write {} on disk.".format(base_table_path))
new_table.write(base_table_path, encoding=OUTPUT_ENCOODING)
def run_program(*args):
"""
Run program and return a tuple with returncode and errors.
"""
with Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE) as subproc:
print("Run: {}".format(' '.join(args)))
errors = subproc.communicate()[1]
return (subproc.returncode, errors)
def main():
"""
Program entry
"""
create_empty_output_dir()
extract_translations()
merge_translations()
delete_output_dir()
main()