You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			231 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			Python
		
	
			
		
		
	
	
			231 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			Python
		
	
| #!/bin/python3
 | |
| import re
 | |
| from typing import List, Tuple
 | |
| 
 | |
| OUTPUT_FILE = "./ts/localization/locales.ts"
 | |
| 
 | |
| 
 | |
| def wrapValue(value):
 | |
|     """
 | |
|     Wraps the given value in single quotes if it contains any characters other than letters, digits, or underscores.
 | |
| 
 | |
|     Args:
 | |
|       value (str): The value to be wrapped.
 | |
| 
 | |
|     Returns:
 | |
|       str: The wrapped value if it contains any special characters, otherwise the original value.
 | |
|     """
 | |
|     if re.search(r"[^a-zA-Z0-9_]", value):
 | |
|         return f"'{value}'"
 | |
|     return value
 | |
| 
 | |
| 
 | |
| def parseValue(value):
 | |
|     """
 | |
|     Parses the given value by replacing single quotes with escaped single quotes.
 | |
| 
 | |
|     Args:
 | |
|       value (str): The value to be parsed.
 | |
| 
 | |
|     Returns:
 | |
|       str: The parsed value with escaped single quotes.
 | |
|     """
 | |
|     return value.replace("'", "\\'")
 | |
| 
 | |
| 
 | |
| def generate_js_object(data):
 | |
|     """
 | |
|     Generate a JavaScript object from a dictionary.
 | |
| 
 | |
|     Args:
 | |
|       data (dict): The dictionary containing key-value pairs.
 | |
| 
 | |
|     Returns:
 | |
|       str: A string representation of the JavaScript object.
 | |
|     """
 | |
|     js_object = "{\n"
 | |
|     for key, value in data.items():
 | |
|         js_object += f"  {wrapValue(key)}: '{parseValue(value)}',\n"
 | |
|     js_object += "}"
 | |
|     return js_object
 | |
| 
 | |
| def escape_new_lines(value):
 | |
|     """
 | |
|     Escapes new lines, from "\n" to "\\n".
 | |
|     """
 | |
|     return value.replace("\n", "\\n")
 | |
| 
 | |
| 
 | |
| def extract_vars(text):
 | |
|     # Use a regular expression to find all strings inside curly braces
 | |
|     vars = re.findall(r'\{(.*?)\}', text)
 | |
|     return vars
 | |
| 
 | |
| 
 | |
| def vars_to_record(vars):
 | |
|     arr = []
 | |
|     for var in vars:
 | |
|         to_append = '' + var + ': ' + ('"number"' if var == 'count' or var == 'found_count' else '"string"')
 | |
|         if to_append not in arr:
 | |
|           arr.append(to_append)
 | |
| 
 | |
|     # print(arr)
 | |
|     if not arr:
 | |
|         return ''
 | |
|     return "{" + ', '.join(arr) + "}"
 | |
| 
 | |
| 
 | |
| def replace_static_strings(str):
 | |
|     # todo make those come from the glossary
 | |
|     replaced =  str.replace("{app_name}", "Session")\
 | |
|       .replace("{session_download_url}", "https://getsession.org/download")\
 | |
|       .replace("{session_download_url}", "GIF")\
 | |
|       .replace("{oxen_foundation}", "Oxen Foundation")\
 | |
|       .replace("\"", "\\\"")
 | |
|     return replaced
 | |
| 
 | |
| 
 | |
| def args_to_type(args):
 | |
|    return args if args else 'undefined,'
 | |
| 
 | |
| 
 | |
| def generate_type_object(locales):
 | |
|     """
 | |
|     Generate a JavaScript type from a dictionary.
 | |
| 
 | |
|     Args:
 | |
|       data (dict): The dictionary containing key-value pairs.
 | |
| 
 | |
|     Returns:
 | |
|       str: A string representation of the JavaScript object.
 | |
|     """
 | |
|     js_object = "{\n"
 | |
|     js_plural_object_container = "{\n"
 | |
|     plural_pattern = r"(zero|one|two|few|many|other)\s*\[([^\]]+)\]"
 | |
| 
 | |
|     for key, value_en in locales['en'].items():
 | |
|         if value_en.startswith("{count, plural, "):
 | |
|             extracted_vars_en = extract_vars(replaced_en)
 | |
|             plurals_other = [[locale, replace_static_strings(data.get(key, ""))] for locale, data in locales.items()]
 | |
|             en_plurals_with_token = re.findall(plural_pattern, value_en.replace('#', '{count}'))
 | |
| 
 | |
|             if not en_plurals_with_token:
 | |
|                raise ValueError("invalid plural string")
 | |
| 
 | |
|             all_locales_plurals = []
 | |
| 
 | |
|             extracted_vars = extract_vars(replace_static_strings(en_plurals_with_token[0][1]))
 | |
|             if('count' not in extracted_vars):
 | |
|                 extracted_vars.append('count')
 | |
| 
 | |
|             for plural in plurals_other:
 | |
|               js_plural_object = ""
 | |
| 
 | |
|               locale_key = plural[0].replace("_","-") # 'lo', 'th', 'zh-CN', ....
 | |
|               plural_str = plural[1].replace('#', '{count}')
 | |
| 
 | |
|               plurals_with_token = re.findall(plural_pattern, plural_str)
 | |
| 
 | |
| 
 | |
|               all_locales_strings = []
 | |
|               as_record_type_en = vars_to_record(extracted_vars)
 | |
| 
 | |
|               for token, localized_string in plurals_with_token:
 | |
|                 if localized_string:
 | |
|                   to_append = ""
 | |
|                   to_append += token
 | |
|                   to_append += f": \"{escape_new_lines(localized_string)}\""
 | |
|                   all_locales_strings.append(to_append)
 | |
| 
 | |
|               # if that locale doesn't have translation in plurals, add the english hones
 | |
|               if not len(all_locales_strings):
 | |
|                  for plural_en_token, plural_en_str in en_plurals_with_token:
 | |
|                     all_locales_strings.append(f"{plural_en_token}: \"{escape_new_lines(plural_en_str)}\"")
 | |
|               js_plural_object += f"    {wrapValue(locale_key)}:"
 | |
|               js_plural_object += "{\n      "
 | |
|               js_plural_object += ",\n      ".join(all_locales_strings)
 | |
|               js_plural_object += "\n    },"
 | |
| 
 | |
|               all_locales_plurals.append(js_plural_object)
 | |
|             js_plural_object_container += f'  {wrapValue(key)}: {{\n{"\n".join(all_locales_plurals)}\n    args: {args_to_type(as_record_type_en)}\n  }},\n'
 | |
| 
 | |
|         else:
 | |
|           replaced_en = replace_static_strings(value_en)
 | |
|           extracted_vars_en = extract_vars(replaced_en)
 | |
|           as_record_type_en = vars_to_record(extracted_vars_en)
 | |
|           other_locales_replaced_values = [[locale, replace_static_strings(data.get(key, ""))] for locale, data in locales.items()]
 | |
| 
 | |
|           all_locales_strings = []
 | |
|           for locale, replaced_val in other_locales_replaced_values:
 | |
|             if replaced_val:
 | |
|               all_locales_strings.append(f'{wrapValue(locale.replace("_","-"))}: "{escape_new_lines(replaced_val)}"')
 | |
|             else:
 | |
|               all_locales_strings.append(f'{wrapValue(locale.replace("_","-"))}: "{escape_new_lines(replaced_en)}"')
 | |
| 
 | |
|           # print('key',key, " other_locales_replaced_values:", other_locales_replaced_values)
 | |
|           js_object += f'  {wrapValue(key)}: {{\n      {",\n      ".join(all_locales_strings)},\n      args: {args_to_type(as_record_type_en)}\n  }},\n'
 | |
| 
 | |
|     js_object += "}"
 | |
|     js_plural_object_container += "}"
 | |
|     return js_object,js_plural_object_container
 | |
| 
 | |
| 
 | |
| DISCLAIMER = """
 | |
| // This file was generated by a script. Do not modify this file manually.
 | |
| // To make changes, modify the corresponding JSON file and re-run the script.
 | |
| 
 | |
| """
 | |
| 
 | |
| 
 | |
| def generateLocalesType(locale, data):
 | |
|     """
 | |
|     Generate the locales type and write it to a file.
 | |
| 
 | |
|     Args:
 | |
|       locale: The locale dictionary containing the localization data.
 | |
|     """
 | |
|     # write the locale_dict to a file
 | |
|     with open(OUTPUT_FILE, "w", encoding='utf-8') as ts_file:
 | |
|         ts_file.write(
 | |
|             f"{DISCLAIMER}"
 | |
|         )
 | |
|         ts_file.write(
 | |
|             f"export const {locale} = {generate_js_object(data)} as const;\n"
 | |
|         )
 | |
|         ts_file.write(
 | |
|             f"\nexport type Dictionary = typeof en;\n"
 | |
|         )
 | |
| 
 | |
| 
 | |
|     return f"Locales generated at: {OUTPUT_FILE}"
 | |
| 
 | |
| 
 | |
| def generateLocalesMergedType(locales):
 | |
|     """
 | |
|     Generate the locales type and write it to a file.
 | |
| 
 | |
|     Args:
 | |
|       locale: The locale dictionary containing the localization data.
 | |
|     """
 | |
| 
 | |
|     # write the locale_dict to a file
 | |
|     with open(OUTPUT_FILE, "w", encoding='utf-8') as ts_file:
 | |
|         ts_file.write(
 | |
|             f"{DISCLAIMER}"
 | |
|         )
 | |
| 
 | |
|         dicts = generate_type_object(locales)
 | |
| 
 | |
|         dictVar = "simpleDictionary"
 | |
|         pluralDictVar = "pluralsDictionary"
 | |
| 
 | |
| 
 | |
|         ts_file.write(f"""
 | |
| export const {dictVar} = {dicts[0]} as const;
 | |
| 
 | |
| export const {pluralDictVar} = {dicts[1]} as const;
 | |
| """)
 | |
| 
 | |
|     return f"Locales generated at: {OUTPUT_FILE}"
 | |
| 
 |