Last active
February 13, 2024 11:57
-
-
Save DmytroLisitsyn/088a3a0cae7c179d33b2633726e052da to your computer and use it in GitHub Desktop.
Kassandra - localization constants generator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
import sys | |
import re | |
import os.path | |
def main(): | |
strings_file = None | |
dict_file = None | |
output_file = None | |
for arg in sys.argv: | |
match = re.match(".+[.]strings$", arg) | |
if match: | |
strings_file = match.group(0) | |
continue | |
match = re.match(".+[.]stringsdict$", arg) | |
if match: | |
dict_file = match.group(0) | |
continue | |
match = re.match(".+[.]swift$", arg) | |
if match: | |
output_file = match.group(0) | |
continue | |
keys = [] | |
keys += _parse_strings_file(strings_file) | |
keys += _parse_dict_file(dict_file) | |
keys.sort() | |
entries_string = _make_entries_string(keys, 1) | |
output_string = _make_output_string(entries_string) | |
_write_to_file(output_string, output_file) | |
def _write_to_file(string, file): | |
if file == None: | |
return | |
old_content = "" | |
if os.path.isfile(file): | |
content = open(file, "r") | |
if content: | |
old_content = content.read() | |
if old_content == string: | |
return | |
content = open(file, "w+") | |
if content: | |
if content.read() != string: | |
content.write(string) | |
def _parse_strings_file(file): | |
""" | |
Makes array of keys from Localizable.strings file. | |
Arguments: | |
file (str): the path to file | |
Returns: | |
list: array of keys retieved from file | |
""" | |
if file == None: | |
return [] | |
keys = [] | |
content = open(file, "r") | |
for line in content: | |
match = re.match("^[ ]*\".+\"[ ]?=", line) | |
if match: | |
key = re.sub("[$'\n'$'\t'\"= ]", "", match.group(0)) | |
keys.append(key) | |
return keys | |
def _parse_dict_file(file): | |
if file == None: | |
return [] | |
keys = [] | |
content = open(file, "r").read() | |
match = re.findall("<key>[\\s\\S]*?<[/]dict>[$'\n'$'\t' ]*<[/]dict>", content) | |
if match == None: | |
return keys | |
for raw_key in match: | |
key = re.sub("<key>|<[/]key>[\\s\\S]*", "", raw_key) | |
keys.append(key) | |
return keys | |
def _make_output_string(entries): | |
boilerplate_prefix = """// | |
// This file is generated by Kassandra - localization constants generator. | |
// | |
import Foundation | |
enum Localizable { | |
""" | |
boilerplate_suffix = """} | |
private extension String { | |
func localized(arguments: CVarArg..., comment: String = \"\") -> String { | |
if arguments.isEmpty { | |
return NSLocalizedString(self, tableName: nil, bundle: Bundle(for: Localizable_BundleRef.self), value: self, comment: comment) | |
} else { | |
return withVaList(arguments) { arguments -> String in | |
let format = self.localized(comment: comment) | |
return NSString(format: format, arguments: arguments) as String | |
} | |
} | |
} | |
} | |
private final class Localizable_BundleRef { | |
} | |
""" | |
return "{}{}{}".format(boilerplate_prefix, entries, boilerplate_suffix) | |
def _make_entries_string(keys, depth): | |
entries_string = "" | |
hierarchy = [] | |
for key in keys: | |
key_components = key.split(".") | |
new_hierarchy_depth = len(key_components) - 1 | |
new_hierarchy = key_components[:new_hierarchy_depth] | |
common_hierarchy_depth = 0 | |
for iteration in range(min(len(new_hierarchy), len(hierarchy))): | |
if new_hierarchy[iteration] == hierarchy[iteration]: | |
common_hierarchy_depth += 1 | |
else: | |
break | |
entries_string += _make_closing_brackets(hierarchy, depth, common_hierarchy_depth) | |
entries_string += _make_opening_brackets(new_hierarchy, depth, common_hierarchy_depth) | |
signature = key_components[new_hierarchy_depth] | |
entries_string += _make_entry(signature, key, depth + new_hierarchy_depth) | |
hierarchy = new_hierarchy | |
entries_string += _make_closing_brackets(hierarchy, depth, 0) | |
return entries_string | |
def _make_indentation(depth): | |
""" | |
Makes indentation string giving its depth. | |
Arguments: | |
depth (int): level of indentation | |
Returns: | |
str: indentation string | |
""" | |
spacing = "" | |
for _ in range(depth): | |
spacing += " " | |
return spacing | |
def _make_opening_brackets(hierarchy, general_depth, common_hierarchy_depth): | |
brackets = "" | |
for iteration in range(len(hierarchy) - common_hierarchy_depth): | |
index = common_hierarchy_depth + iteration | |
indentation = _make_indentation(index + general_depth) | |
brackets += "{}enum {} {{\n".format(indentation, hierarchy[index]) | |
return brackets | |
def _make_closing_brackets(hierarchy, general_depth, common_hierarchy_depth): | |
brackets = "" | |
for iteration in range(len(hierarchy) - common_hierarchy_depth): | |
index = len(hierarchy) - iteration - 1 | |
indentation = _make_indentation(index + general_depth) | |
brackets += "{}}}\n".format(indentation) | |
return brackets | |
def _make_entry(signature, key, depth): | |
name = re.sub("%[@d]", "", signature) | |
name = _lowercase_first_letter(name) | |
name = _handle_keyword(name) | |
indentation = _make_indentation(depth) | |
match = re.findall("[A-Z]*[a-z0-9]*%[@d]", signature) | |
if match: | |
external_signature = "" | |
internal_signature = "" | |
for index in range(len(match)): | |
parameter_name = re.sub("%[@d]", "", _lowercase_first_letter(match[index])) | |
parameter_type = _make_parameter_type(match[index]) | |
external_signature += "{}: {}".format(parameter_name, parameter_type) | |
internal_signature += _handle_keyword(parameter_name) | |
if index < len(match) - 1: | |
external_signature += ", " | |
internal_signature += ", " | |
entry = "{}static func {}({}) -> String {{\n".format(indentation, name, external_signature) | |
entry += "{}return \"{}\".localized(arguments: {})\n{}}}\n".format(_make_indentation(depth + 1), key, internal_signature, indentation) | |
return entry | |
else: | |
entry = "{}static var {}: String {{ return \"{}\".localized() }}\n".format(indentation, name, key) | |
return entry | |
def _lowercase_first_letter(string): | |
if len(string) == 0: | |
return string | |
else: | |
return string[0].lower() + string[1:] | |
def _handle_keyword(string): | |
keywords = ["continue", "switch", "default", "static", "final", "class","struct", "import", "extension", "return", "try", "let", "break", "case", "super", "private", "public", "internal", "guard", "self", "while", "do", "catch", "as", "true", "false", "override", "lazy", "get", "set"] | |
for keyowrd in keywords: | |
if string == keyowrd: | |
return "`" + string + "`" | |
return string | |
def _make_parameter_type(string): | |
if "%@" in string: | |
return "String" | |
elif "%d" in string: | |
return "Int" | |
elif "%f" in string: | |
return "Double" | |
else: | |
return "Undefined" | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment