Skip to content

Commit 67e4f76

Browse files
committed
Fixed an issue where Xcode wouldn't recognise some languages and fail to build
1 parent 7b34b58 commit 67e4f76

File tree

1 file changed

+59
-21
lines changed

1 file changed

+59
-21
lines changed

crowdin/generate_ios_strings.py

Lines changed: 59 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,15 @@
88
from colorama import Fore, Style, init
99
from datetime import datetime
1010

11+
# It seems that Xcode uses different language codes and doesn't support all of the languages we get from Crowdin
12+
# (at least in the variants that Crowdin is specifying them in) so need to map/exclude them in order to build correctly
13+
LANGUAGE_MAPPING = {
14+
'kmr': 'ku-TR', # Explicitly Kurmanji in Türkiye, `ku-TR` is the general language code for Kurdish in Türkiye
15+
'no': 'nb-NO', # Norwegian general, `nb-NO` is Norwegian Bokmål in Norway and is apparently seen as the standard
16+
'sr-CS': 'sr-Latn', # Serbian (Latin)
17+
'tl': None, # Tagalog (not supported, we have Filipino which might have to be enough for now)
18+
}
19+
1120
# Parse command-line arguments
1221
parser = argparse.ArgumentParser(description='Convert a XLIFF translation files to Apple String Catalog.')
1322
parser.add_argument('raw_translations_directory', help='Directory which contains the raw translation files')
@@ -19,6 +28,19 @@
1928
TRANSLATIONS_OUTPUT_DIRECTORY = args.translations_output_directory
2029
NON_TRANSLATABLE_STRINGS_OUTPUT_PATH = args.non_translatable_strings_output_path
2130

31+
def filter_and_map_language_ids(target_languages):
32+
result = []
33+
for lang in target_languages:
34+
if lang['id'] in LANGUAGE_MAPPING:
35+
mapped_value = LANGUAGE_MAPPING[lang['id']]
36+
if mapped_value is not None:
37+
lang['mapped_id'] = mapped_value
38+
result.append(lang)
39+
else:
40+
lang['mapped_id'] = lang['id']
41+
result.append(lang)
42+
return result
43+
2244
def parse_xliff(file_path):
2345
tree = ET.parse(file_path)
2446
root = tree.getroot()
@@ -32,39 +54,53 @@ def parse_xliff(file_path):
3254
target_language = file_elem.get('target-language')
3355
if target_language is None:
3456
raise ValueError(f"Missing target-language in file: {file_path}")
57+
58+
if target_language in LANGUAGE_MAPPING:
59+
target_language = LANGUAGE_MAPPING[target_language]
3560

36-
for trans_unit in root.findall('.//ns:trans-unit', namespaces=namespace):
37-
resname = trans_unit.get('resname') or trans_unit.get('id')
38-
if resname is None:
39-
continue # Skip entries without a resname or id
40-
41-
target = trans_unit.find('ns:target', namespaces=namespace)
42-
source = trans_unit.find('ns:source', namespaces=namespace)
43-
44-
if target is not None and target.text:
45-
translations[resname] = target.text
46-
elif source is not None and source.text:
47-
# If target is missing or empty, use source as a fallback
48-
translations[resname] = source.text
49-
print(f"Warning: Using source text for '{resname}' as target is missing or empty")
50-
51-
# Handle plural groups
61+
# Handle plural groups first (want to make sure any warnings shown are correctly attributed to plurals or non-plurals)
5262
for group in root.findall('.//ns:group[@restype="x-gettext-plurals"]', namespaces=namespace):
5363
plural_forms = {}
5464
resname = None
5565
for trans_unit in group.findall('ns:trans-unit', namespaces=namespace):
5666
if resname is None:
5767
resname = trans_unit.get('resname') or trans_unit.get('id')
68+
5869
target = trans_unit.find('ns:target', namespaces=namespace)
70+
source = trans_unit.find('ns:source', namespaces=namespace)
5971
context_group = trans_unit.find('ns:context-group', namespaces=namespace)
72+
6073
if context_group is not None:
6174
plural_form = context_group.find('ns:context[@context-type="x-plural-form"]', namespaces=namespace)
62-
if target is not None and target.text and plural_form is not None:
75+
if plural_form is not None:
6376
form = plural_form.text.split(':')[-1].strip().lower()
64-
plural_forms[form] = target.text
77+
78+
if target is not None and target.text:
79+
plural_forms[form] = target.text
80+
elif source is not None and source.text:
81+
# If target is missing or empty, use source as a fallback
82+
plural_forms[form] = source.text
83+
print(f"Warning: Using source text for plural form '{form}' of '{resname}' in '{target_language}' as target is missing or empty")
84+
6585
if resname and plural_forms:
6686
translations[resname] = plural_forms
6787

88+
# Then handle non-plurals (ignore any existing values as they are plurals)
89+
for trans_unit in root.findall('.//ns:trans-unit', namespaces=namespace):
90+
resname = trans_unit.get('resname') or trans_unit.get('id')
91+
if resname is None or resname in translations:
92+
continue # Skip entries without a resname/id and entries which already exist (ie. plurals)
93+
94+
target = trans_unit.find('ns:target', namespaces=namespace)
95+
source = trans_unit.find('ns:source', namespaces=namespace)
96+
97+
if target is not None and target.text:
98+
translations[resname] = target.text
99+
elif source is not None and source.text:
100+
# If target is missing or empty, use source as a fallback
101+
translations[resname] = source.text
102+
print(f"Warning: Using source text for '{resname}' in '{target_language}' as target is missing or empty")
103+
68104
return translations, target_language
69105

70106
def clean_string(text):
@@ -87,11 +123,13 @@ def convert_xliff_to_string_catalog(input_dir, output_dir, source_language, targ
87123
"strings": {},
88124
"version": "1.0"
89125
}
126+
target_mapped_languages = filter_and_map_language_ids(target_languages)
127+
source_language['mapped_id'] = source_language['id']
90128

91129
# We need to sort the full language list (if the source language comes first rather than in alphabetical order
92130
# then the output will differ from what Xcode generates)
93-
all_languages = [source_language] + target_languages
94-
sorted_languages = sorted(all_languages, key=lambda x: x['id'])
131+
all_languages = [source_language] + target_mapped_languages
132+
sorted_languages = sorted(all_languages, key=lambda x: x['mapped_id'])
95133

96134
for language in sorted_languages:
97135
lang_locale = language['locale']
@@ -103,7 +141,7 @@ def convert_xliff_to_string_catalog(input_dir, output_dir, source_language, targ
103141
try:
104142
translations, target_language = parse_xliff(input_file)
105143
except Exception as e:
106-
raise ValueError(f"Error processing file {filename}: {str(e)}")
144+
raise ValueError(f"Error processing locale {lang_locale}: {str(e)}")
107145

108146
print(f"\033[2K{Fore.WHITE}⏳ Converting translations for {target_language} to target format...{Style.RESET_ALL}", end='\r')
109147
sorted_translations = sorted(translations.items())

0 commit comments

Comments
 (0)