kiwix-apple/localizations.py
2025-01-26 10:51:05 +01:00

139 lines
5.2 KiB
Python

import os
import argparse
import re
import glob
from pathlib import Path
# example usages:
# python localizations.py generate
# python localizations.py validate
parser = argparse.ArgumentParser()
parser.add_argument('command', type=str, help="Possible options: generate | validate")
arguments = parser.parse_args()
command = arguments.command
if command not in ['generate', 'validate']:
parser.print_help()
exit()
input_file_name = Path("Support/en.lproj/Localizable.strings")
comment_file_name = Path("Support/qqq.lproj/Localizable.strings")
enum_name = "LocalString"
target_dir = "Support"
template_file_name = "StringLocalExtension.swift_temp"
class Generate:
def __init__(self, input_file_name, template_file, enum_name, target_dir):
self.enum_name = enum_name
reader = Reader(input_file_name)
vars = self.__variables(reader)
with open(os.path.join(target_dir, template_file)) as template_file:
template_content = template_file.read()
output_path = os.path.join(target_dir, enum_name + ".swift")
file = open(output_path, 'w')
file.write("""//
// !! DO NOT EDIT THIS FILE DIRECTLY !!
// !! IT HAS BEEN AUTO-GENERATED !!
//
""")
file.write(template_content)
file.write(self.__code_for("\n\t".join(vars)))
file.close()
def __variables(self, reader):
vars = list()
for key, has_arguments in sorted(reader.keys()):
if has_arguments:
vars.append(self.__static_func_for(key))
else:
vars.append(self.__static_let_for(key))
return vars
def __code_for(self, variables):
return """enum {} {{
{}
}}
""".format(self.enum_name, variables)
def __static_let_for(self, key):
return """static let {} = "{}".localized""".format(self.__get_var_name(key), key)
def __static_func_for(self, key):
return """static func {}(withArgs args: CVarArg...) -> String {{ "{}".localizedWithFormat(withArgs: args) }}""".format(self.__get_var_name(key), key)
def __get_var_name(self, key):
return re.sub('[^a-z0-9]', '_', key.lower())
class Reader:
def __init__(self, input_file_name):
self.input_file_name = input_file_name
def keys(self):
pattern = re.compile(r'"(?P<key>.+)"\s{1,}=\s{1,}"(?P<value>.+)"')
with open(self.input_file_name) as input_file:
for line in input_file:
match = pattern.match(line)
if match:
groups = match.groupdict()
key = groups.get('key')
value = groups.get('value')
has_arguments = "%@" in value
yield key, has_arguments
class Validate:
def __init__(self, input_file_name, comment_file_name, enum_name, search_directory=os.getcwd()):
reader = Reader(input_file_name)
vars = list()
for key, _ in reader.keys():
assert "." in key, "Invalid translation key: {}, it should contain at least one '.' (dot)".format(key)
vars.append(key)
vars = sorted(vars)
matches = dict()
counter = dict()
for var in vars:
swift_var = self.__get_var_name(var)
counter[swift_var] = 0
for swift_file_name in glob.iglob(os.path.join(search_directory, '**/*.swift'), recursive=True):
if Path(swift_file_name).name != "{}.swift".format(enum_name):
with open(swift_file_name, 'r') as swift_file:
content = swift_file.read()
for var in vars:
if var in content:
if var in matches:
matches[var].append(swift_file_name)
else:
matches[var] = [swift_file_name]
swift_var = self.__get_var_name(var)
if swift_var in content:
counter[swift_var] += 1
assert len(matches.keys()) == 0, "localization strings cannot not be directly used in swift (use LocalString instead): {}".format(matches)
unused_swift_vars = {k: v for k, v in counter.items() if v == 0 }.keys()
assert len(unused_swift_vars) == 0, "unused localizations entries (delete them from localizations, and run: python localizations.py generate): {}".format(sorted(unused_swift_vars))
comment_keys = list()
comment_reader = Reader(comment_file_name)
for comment_key, _ in comment_reader.keys():
assert comment_key in vars, "extra qqq key found: {}".format(comment_key)
comment_keys.append(comment_key)
missing = sorted(set(vars).difference(comment_keys))
assert len(missing) == 0, "undocumented keys (please add them to qqq): {}".format(missing)
def __get_var_name(self, key):
return re.sub('[^a-z0-9]', '_', key.lower())
match command:
case "generate":
Generate(input_file_name, template_file_name, enum_name, target_dir)
case "validate":
Validate(input_file_name, comment_file_name, enum_name)
case _:
exit(-1)