mirror of
https://github.com/lux-tel/session-desktop.git
synced 2026-10-30 20:33:24 +00:00
355 lines
11 KiB
Python
355 lines
11 KiB
Python
#!/bin/python3
|
|
import re
|
|
|
|
OUTPUT_FILE = "./ts/localization/locales.ts"
|
|
|
|
|
|
# Define the mapping of special fields to their types and corresponding "WithX"
|
|
with_map = {
|
|
("name", "string"): "WithName",
|
|
("group_name", "string"): "WithGroupName",
|
|
("community_name", "string"): "WithCommunityName",
|
|
("other_name", "string"): "WithOtherName",
|
|
("author", "string"): "WithAuthor",
|
|
("emoji", "string"): "WithEmoji",
|
|
("emoji_name", "string"): "WithEmojiName",
|
|
("admin_name", "string"): "WithAdminName",
|
|
("time", "string"): "WithTime",
|
|
("time_large", "string"): "WithTimeLarge",
|
|
("time_small", "string"): "WithTimeSmall",
|
|
("disappearing_messages_type", "string"): "WithDisappearingMessagesType",
|
|
("conversation_name", "string"): "WithConversationName",
|
|
("file_type", "string"): "WithFileType",
|
|
("date", "string"): "WithDate",
|
|
("date_time", "string"): "WithDateTime",
|
|
("message_snippet", "string"): "WithMessageSnippet",
|
|
("query", "string"): "WithQuery",
|
|
("version", "string"): "WithVersion",
|
|
("information", "string"): "WithInformation",
|
|
("device", "string"): "WithDevice",
|
|
("percent_loader", "string"): "WithPercentLoader",
|
|
("message_count", "string"): "WithMessageCount",
|
|
("conversation_count", "string"): "WithConversationCount",
|
|
("found_count", "number"): "WithFoundCount",
|
|
("hash", "string"): "WithHash",
|
|
("url", "string"): "WithUrl",
|
|
("account_id", "string"): "WithAccountId",
|
|
("count", "number"): "WithCount",
|
|
("service_node_id", "string"): "WithServiceNodeId",
|
|
("limit", "string"): "WithLimit",
|
|
("relative_time", "string"): "WithRelativeTime",
|
|
("icon", "string"): "WithIcon",
|
|
("storevariant", "string"): "WithStoreVariant",
|
|
("min", "string"): "WithMin",
|
|
("max", "string"): "WithMax",
|
|
}
|
|
|
|
|
|
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_str(value: str):
|
|
"""
|
|
Escapes some chars breaking the .ts otherwise: "\n" to "\\n" and "\"" to "\\\""
|
|
"""
|
|
return value.replace("\n", "\\n").replace("\"", "\\\"")
|
|
|
|
|
|
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_ts(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)
|
|
|
|
return arr
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
if not arr:
|
|
return ''
|
|
return "{" + ', '.join(arr) + "}"
|
|
|
|
|
|
|
|
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_no_args = "{\n"
|
|
js_object_with_args = "{\n"
|
|
js_plural_object_container_with_args = "{\n"
|
|
plural_pattern = r"(zero|one|two|few|many|other)\s*\[([^\]]+)\]"
|
|
|
|
tokens_simple_no_args = []
|
|
tokens_simple_with_args = {}
|
|
tokens_plurals_with_args = {}
|
|
|
|
for key, value_en in locales['en'].items():
|
|
if value_en.startswith("{count, plural, "):
|
|
extracted_vars_en = extract_vars(value_en)
|
|
plurals_other = [[locale, data.get(key, "")] for locale, data in locales.items()]
|
|
en_plurals_with_token = re.findall(plural_pattern, value_en)
|
|
|
|
if not en_plurals_with_token:
|
|
raise ValueError("invalid plural string")
|
|
|
|
all_locales_plurals = []
|
|
|
|
extracted_vars = extract_vars(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]
|
|
|
|
plurals_with_token = re.findall(plural_pattern, plural_str)
|
|
|
|
|
|
all_locales_strings = []
|
|
|
|
for token, localized_string in plurals_with_token:
|
|
if localized_string:
|
|
to_append = ""
|
|
to_append += token
|
|
to_append += f": \"{escape_str(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_str(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_with_args += f' {wrapValue(key)}: {{\n{"\n".join(all_locales_plurals)}\n }},\n'
|
|
tokens_plurals_with_args[key] = vars_to_record_ts(extracted_vars)
|
|
|
|
else:
|
|
extracted_vars_en = extract_vars(value_en)
|
|
other_locales_replaced_values = [[locale, 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_str(replaced_val)}"')
|
|
else:
|
|
all_locales_strings.append(f'{wrapValue(locale.replace("_","-"))}: "{escape_str(value_en)}"')
|
|
|
|
if extracted_vars_en:
|
|
js_object_with_args += f' {wrapValue(key)}: {{\n {",\n ".join(all_locales_strings)},\n }},\n'
|
|
tokens_simple_with_args[key] = vars_to_record_ts(extracted_vars_en)
|
|
else:
|
|
js_object_no_args += f' {wrapValue(key)}: {{\n {",\n ".join(all_locales_strings)},\n }},\n'
|
|
tokens_simple_no_args.append(key)
|
|
|
|
tokens_simple_no_args_str = "\n '" + "' |\n '".join(tokens_simple_no_args) + "'"
|
|
|
|
js_object_no_args += "}"
|
|
js_object_with_args += "}"
|
|
js_plural_object_container_with_args += "}"
|
|
|
|
dicts = {
|
|
"simple_no_args": js_object_no_args,
|
|
"simple_with_args": js_object_with_args,
|
|
"plurals_with_args": js_plural_object_container_with_args,
|
|
"tokens_simple_no_args_str": tokens_simple_no_args_str,
|
|
"tokens_simple_with_args": tokens_simple_with_args,
|
|
"tokens_plural_with_args": tokens_plurals_with_args,
|
|
}
|
|
return dicts
|
|
|
|
|
|
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 format_tokens_with_named_args(token_args_dict):
|
|
result = []
|
|
|
|
for token, args in token_args_dict.items():
|
|
extras = []
|
|
with_types = []
|
|
|
|
for arg_name, arg_type in args:
|
|
key = (arg_name, arg_type)
|
|
if key in with_map:
|
|
with_types.append(with_map[key])
|
|
else:
|
|
extras.append(f"{arg_name}: {arg_type}")
|
|
|
|
# Join parts
|
|
joined = " & ".join(with_types)
|
|
if extras:
|
|
extras_str = "{ " + ", ".join(extras) + " }"
|
|
joined = f"{joined} & {extras_str}" if joined else extras_str
|
|
|
|
result.append(f" {token}: {joined}")
|
|
|
|
return "{\n" + ",\n".join(result) +"\n}"
|
|
|
|
|
|
def generate_with_types(with_map):
|
|
lines = []
|
|
for (arg_name, arg_type), type_name in with_map.items():
|
|
lines.append(f"type {type_name} = {{{arg_name}: {arg_type}}};")
|
|
return "\n".join(lines)
|
|
|
|
|
|
|
|
|
|
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}"
|
|
)
|
|
|
|
ts_file.write("import type { CrowdinLocale } from './constants';\n")
|
|
|
|
dicts = generate_type_object(locales)
|
|
|
|
tokens_simple_with_args = dicts['tokens_simple_with_args']
|
|
tokens_plural_with_args = dicts['tokens_plural_with_args']
|
|
|
|
tokens_union_simple_args = format_tokens_with_named_args(tokens_simple_with_args)
|
|
tokens_union_plural_args = format_tokens_with_named_args(tokens_plural_with_args)
|
|
|
|
|
|
ts_file.write(f"""
|
|
{generate_with_types(with_map)}
|
|
|
|
export type TokenSimpleNoArgs = {dicts['tokens_simple_no_args_str']};
|
|
|
|
export type TokensSimpleAndArgs = {tokens_union_simple_args};
|
|
|
|
export type TokensPluralAndArgs = {tokens_union_plural_args};
|
|
|
|
export type TokenSimpleWithArgs = {"\n '" + "' |\n '".join(list(tokens_simple_with_args.keys())) + "'"}
|
|
|
|
export type TokenPluralWithArgs = {"\n '" + "' |\n '".join(list(tokens_plural_with_args.keys())) + "'"}
|
|
|
|
export const simpleDictionaryNoArgs: Record<
|
|
TokenSimpleNoArgs,
|
|
Record<CrowdinLocale, string>
|
|
> = {dicts['simple_no_args']} as const;
|
|
|
|
|
|
export const simpleDictionaryWithArgs: Record<
|
|
TokenSimpleWithArgs,
|
|
Record<CrowdinLocale, string>
|
|
> = {dicts['simple_with_args']} as const;
|
|
|
|
export const pluralsDictionaryWithArgs = {dicts['plurals_with_args']} as const;
|
|
|
|
""")
|
|
|
|
|
|
return f"Locales generated at: {OUTPUT_FILE}"
|
|
|