11import os
2- import json
32import xml .etree .ElementTree as ET
43import sys
54import argparse
65import re
76from pathlib import Path
87from colorama import Fore , Style
8+ from generate_shared import load_glossary_dict , clean_string , setup_generation
99
1010# Variables that should be treated as numeric (using %d)
1111NUMERIC_VARIABLES = ['count' , 'found_count' , 'total_count' ]
1212
13+ AUTO_REPLACE_STATIC_STRINGS = False
14+
1315
1416# Parse command-line arguments
1517parser = argparse .ArgumentParser (description = 'Convert a XLIFF translation files to Android XML.' )
@@ -66,22 +68,8 @@ def repl(match):
6668
6769 return re .sub (r'\{([^}]+)\}' , repl , text )
6870
69- def clean_string (text ):
70- # Note: any changes done for all platforms needs most likely to be done on crowdin side.
71- # So we don't want to replace -> with → for instance, we want the crowdin strings to not have those at all.
72- # We can use standard XML escaped characters for most things (since XLIFF is an XML format) but
73- # want the following cases escaped in a particular way
74- text = text .replace ("'" , r"\'" )
75- text = text .replace (""" , "\" " )
76- text = text .replace ("\" " , "\\ \" " )
77- text = text .replace ("<b>" , "<b>" )
78- text = text .replace ("</b>" , "</b>" )
79- text = text .replace ("</br>" , "\\ n" )
80- text = text .replace ("<br/>" , "\\ n" )
81- text = text .replace ("&" , "&" ) # Assume any remaining ampersands are desired
82- return text .strip () # Strip whitespace
83-
84- def generate_android_xml (translations , app_name ):
71+
72+ def generate_android_xml (translations , app_name , glossary_dict ):
8573 sorted_translations = sorted (translations .items ())
8674 result = '<?xml version="1.0" encoding="utf-8"?>\n '
8775 result += '<resources>\n '
@@ -93,25 +81,26 @@ def generate_android_xml(translations, app_name):
9381 if isinstance (target , dict ): # It's a plural group
9482 result += f' <plurals name="{ resname } ">\n '
9583 for form , value in target .items ():
96- escaped_value = clean_string (convert_placeholders (value ))
84+ escaped_value = clean_string (convert_placeholders (value ), True , glossary_dict , {} )
9785 result += f' <item quantity="{ form } ">{ escaped_value } </item>\n '
9886 result += ' </plurals>\n '
9987 else : # It's a regular string (for these we DON'T want to convert the placeholders)
100- escaped_target = clean_string (target )
88+ escaped_target = clean_string (target , True , glossary_dict , {} )
10189 result += f' <string name="{ resname } ">{ escaped_target } </string>\n '
10290
10391 result += '</resources>'
10492
10593 return result
10694
107- def convert_xliff_to_android_xml (input_file , output_dir , source_locale , locale , app_name ):
95+ def convert_xliff_to_android_xml (input_file , output_dir , source_locale , locale , glossary_dict ):
10896 if not os .path .exists (input_file ):
10997 raise FileNotFoundError (f"Could not find '{ input_file } ' in raw translations directory" )
11098
11199 # Parse the XLIFF and convert to XML (only include the 'app_name' entry in the source language)
112100 is_source_language = locale == source_locale
113101 translations = parse_xliff (input_file )
114- output_data = generate_android_xml (translations , app_name if is_source_language else None )
102+ app_name = glossary_dict ['app_name' ]
103+ output_data = generate_android_xml (translations , app_name if is_source_language else None , glossary_dict if AUTO_REPLACE_STATIC_STRINGS else {})
115104
116105 # android is pretty smart to resolve resources for translations, see the example here:
117106 # https://developer.android.com/guide/topics/resources/multilingual-support#resource-resolution-examples
@@ -131,17 +120,10 @@ def convert_xliff_to_android_xml(input_file, output_dir, source_locale, locale,
131120
132121
133122def convert_non_translatable_strings_to_kotlin (input_file , output_path ):
134- if not os .path .exists (input_file ):
135- raise FileNotFoundError (f"Could not find '{ input_file } ' in raw translations directory" )
123+ glossary_dict = load_glossary_dict (input_file )
136124
137- # Process the non-translatable string input
138- non_translatable_strings_data = {}
139- with open (input_file , 'r' , encoding = "utf-8" ) as file :
140- non_translatable_strings_data = json .load (file )
141-
142- entries = non_translatable_strings_data ['data' ]
143- max_key_length = max (len (entry ['data' ]['note' ].upper ()) for entry in entries )
144- app_name = None
125+ max_key_length = max (len (key ) for key in glossary_dict )
126+ app_name = glossary_dict ['app_name' ]
145127
146128 # Output the file in the desired format
147129 Path (output_path ).parent .mkdir (parents = True , exist_ok = True )
@@ -151,42 +133,24 @@ def convert_non_translatable_strings_to_kotlin(input_file, output_path):
151133 file .write ('\n ' )
152134 file .write ('// Non-translatable strings for use with the UI\n ' )
153135 file .write ("object NonTranslatableStringConstants {\n " )
154- for entry in entries :
155- key = entry [ 'data' ][ 'note' ] .upper ()
156- text = entry [ 'data' ][ 'text' ]
136+ for key_lowercase in glossary_dict :
137+ key = key_lowercase .upper ()
138+ text = glossary_dict [ key_lowercase ]
157139 file .write (f' const val { key :<{max_key_length }} = "{ text } "\n ' )
158140
159- if key == 'APP_NAME' :
160- app_name = text
161-
162141 file .write ('}\n ' )
163142 file .write ('\n ' )
164143
165- return app_name
166-
167- def convert_all_files (input_directory ):
168- # Extract the project information
169- print (f"\033 [2K{ Fore .WHITE } ⏳ Processing project info...{ Style .RESET_ALL } " , end = '\r ' )
170- project_info_file = os .path .join (input_directory , "_project_info.json" )
171- if not os .path .exists (project_info_file ):
172- raise FileNotFoundError (f"Could not find '{ project_info_file } ' in raw translations directory" )
173-
174- project_details = {}
175- with open (project_info_file , 'r' , encoding = "utf-8" ) as file :
176- project_details = json .load (file )
177-
178- # Extract the language info and sort the target languages alphabetically by locale
179- source_language = project_details ['data' ]['sourceLanguage' ]
180- target_languages = project_details ['data' ]['targetLanguages' ]
181- target_languages .sort (key = lambda x : x ['locale' ])
182- num_languages = len (target_languages )
183- print (f"\033 [2K{ Fore .GREEN } ✅ Project info processed, { num_languages } languages will be converted{ Style .RESET_ALL } " )
184-
185- # Convert the non-translatable strings to the desired format
186- print (f"\033 [2K{ Fore .WHITE } ⏳ Generating static strings file...{ Style .RESET_ALL } " , end = '\r ' )
187- non_translatable_strings_file = os .path .join (input_directory , "_non_translatable_strings.json" )
188- app_name = convert_non_translatable_strings_to_kotlin (non_translatable_strings_file , NON_TRANSLATABLE_STRINGS_OUTPUT_PATH )
144+ if not app_name :
145+ raise ValueError ("could not find app_name in glossary_dict" )
146+
147+ def convert_all_files (input_directory : str ):
148+ setup_values = setup_generation (input_directory )
149+ source_language , rtl_languages , non_translatable_strings_file , target_languages = setup_values .values ()
150+
151+ convert_non_translatable_strings_to_kotlin (non_translatable_strings_file , NON_TRANSLATABLE_STRINGS_OUTPUT_PATH )
189152 print (f"\033 [2K{ Fore .GREEN } ✅ Static string generation complete{ Style .RESET_ALL } " )
153+ glossary_dict = load_glossary_dict (non_translatable_strings_file )
190154
191155 # Convert the XLIFF data to the desired format
192156 print (f"\033 [2K{ Fore .WHITE } ⏳ Converting translations to target format...{ Style .RESET_ALL } " , end = '\r ' )
@@ -199,7 +163,7 @@ def convert_all_files(input_directory):
199163 continue
200164 print (f"\033 [2K{ Fore .WHITE } ⏳ Converting translations for { lang_locale } to target format...{ Style .RESET_ALL } " , end = '\r ' )
201165 input_file = os .path .join (input_directory , f"{ lang_locale } .xliff" )
202- convert_xliff_to_android_xml (input_file , TRANSLATIONS_OUTPUT_DIRECTORY , source_locale , lang_locale , app_name )
166+ convert_xliff_to_android_xml (input_file , TRANSLATIONS_OUTPUT_DIRECTORY , source_locale , lang_locale , glossary_dict )
203167 print (f"\033 [2K{ Fore .GREEN } ✅ All conversions complete{ Style .RESET_ALL } " )
204168
205169if __name__ == "__main__" :
0 commit comments