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.
session-desktop/tools/localization/localeTypes.py

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}"