diff --git a/Resources/Locale/ru-RU/_CP14/_PROTO/entities/objects.ftl b/Resources/Locale/ru-RU/_CP14/_PROTO/entities/objects.ftl index d50fb497fb..db179e93c8 100644 --- a/Resources/Locale/ru-RU/_CP14/_PROTO/entities/objects.ftl +++ b/Resources/Locale/ru-RU/_CP14/_PROTO/entities/objects.ftl @@ -63,20 +63,6 @@ ent-CP14Pestle = пестик ent-CP14Mortar = ступка .desc = Алхимическая прочная тарелка для измельчения реагентов -ent-CP14VialTiny = крохотный флакон - .desc = Прочный стеклянный флакон для хранения небольшого количества жидкости. -ent-CP14BaseVialFragile = маленький флакон - .desc = Хрупкий стеклянный флакон для хранения небольшого количества жидкости. -ent-CP14VialSmallBloodgrassSap = { ent-CP14VialTiny } - .desc = { ent-CP14VialTiny.desc } -ent-CP14VialSmallAgaricMushroom = { ent-CP14VialTiny } - .desc = { ent-CP14VialTiny.desc } -ent-CP14VialSmallWildSage = { ent-CP14VialTiny } - .desc = { ent-CP14VialTiny.desc } -ent-CP14VialSmallChromiumSlime = { ent-CP14VialTiny } - .desc = { ent-CP14VialTiny.desc } - - # Tools ent-CP14OldLantern = Старая Лампа .desc = Пережиток прошлого техномагии. Большой, тяжелый, непрактичный. Таким приятно разве что бить по голове. diff --git a/Tools/_CP14/LocalizationHelper/Locale.lnk b/Tools/_CP14/LocalizationHelper/Locale.lnk new file mode 100644 index 0000000000..ede56c4052 Binary files /dev/null and b/Tools/_CP14/LocalizationHelper/Locale.lnk differ diff --git a/Tools/_CP14/LocalizationHelper/base_parser.py b/Tools/_CP14/LocalizationHelper/base_parser.py new file mode 100644 index 0000000000..f44b5c4fec --- /dev/null +++ b/Tools/_CP14/LocalizationHelper/base_parser.py @@ -0,0 +1,38 @@ +import os +import json + + +class BaseParser: + """ + BaseParser, contains the basic functions for the yml_parser module in the yml_parser package + and for the ftl_parser module in the ftl_parser package + """ + def __init__(self, paths: tuple): + self.path, self.errors_path = paths + + def get_files_paths(self) -> list: + """ + The method gets the path to the yml folder of localization prototypes/files, e.g. "ftl", + then with the help of os library goes through each file in + the folder and creates a path for it, e.g. "ftl/objects.ftl". + """ + files_paths_lst = [] + + for dirpath, _, filenames in os.walk(self.path): + for filename in filenames: + path = f"{dirpath}\\{filename}" + files_paths_lst.append(path) + + return files_paths_lst + + @staticmethod + def save_to_json(prototypes: dict, path: str) -> None: + with open(path, 'w') as json_file: + json.dump(prototypes, json_file, indent=4) + + @staticmethod + def check_file_extension(path: str, extension: str) -> bool: + if path.endswith(extension): + return True + return False + diff --git a/Tools/_CP14/LocalizationHelper/config.json b/Tools/_CP14/LocalizationHelper/config.json index ae130c1fbf..9e2e0f6d81 100644 --- a/Tools/_CP14/LocalizationHelper/config.json +++ b/Tools/_CP14/LocalizationHelper/config.json @@ -1,6 +1,8 @@ { "paths": { "prototypes": "../../../Resources/Prototypes/_CP14/Entities", - "localization": "../../../Resources/Locale/ru-RU/_CP14/_PROTO/entities" + "localization": "../../../Resources/Locale/ru-RU/_CP14/_PROTO/entities", + "error_log_path": "logs/errors.log", + "yml_parser_last_launch": "last_launch/yml_parser.json" } } \ No newline at end of file diff --git a/Tools/_CP14/LocalizationHelper/fluent/ftl_reader.py b/Tools/_CP14/LocalizationHelper/fluent/ftl_reader.py index 691ee66200..b6cdd82f09 100644 --- a/Tools/_CP14/LocalizationHelper/fluent/ftl_reader.py +++ b/Tools/_CP14/LocalizationHelper/fluent/ftl_reader.py @@ -1,4 +1,4 @@ -def read_ftl(path: str) -> dict: +def read_ftl(paths: tuple) -> dict: """ The function looks at each line of the ftl file and determines by the indentation in the line whether @@ -9,7 +9,7 @@ def read_ftl(path: str) -> dict: } last_prototype = "" - + path, error_log_path = paths try: with open(path, encoding="utf-8") as file: for line in file.readlines(): @@ -20,7 +20,8 @@ def read_ftl(path: str) -> dict: proto_id, proto_name = line.split(" = ") proto_id = proto_id.replace("ent-", "") last_prototype = proto_id - prototypes[proto_id] = {"name": proto_name.strip(), + prototypes[proto_id] = { + "name": proto_name.strip(), "desc": None, "suffix": None } @@ -32,7 +33,7 @@ def read_ftl(path: str) -> dict: prototypes[last_prototype][attr] = line.split(" = ")[-1].strip() except Exception as e: - with open("logs/errors_log.txt", "a") as file: + with open(error_log_path, "a") as file: file.write(f"FTL-ERROR:\nAn error occurred while reading a file {path}, error - {e}\n") return prototypes \ No newline at end of file diff --git a/Tools/_CP14/LocalizationHelper/ftl_parser/__init__.py b/Tools/_CP14/LocalizationHelper/ftl_parser/__init__.py index e69de29bb2..1e22c86d9d 100644 --- a/Tools/_CP14/LocalizationHelper/ftl_parser/__init__.py +++ b/Tools/_CP14/LocalizationHelper/ftl_parser/__init__.py @@ -0,0 +1 @@ +from .ftl_parser import FTLParser diff --git a/Tools/_CP14/LocalizationHelper/ftl_parser/ftl_parser.py b/Tools/_CP14/LocalizationHelper/ftl_parser/ftl_parser.py index 33ccd9b186..1b0a2165e2 100644 --- a/Tools/_CP14/LocalizationHelper/ftl_parser/ftl_parser.py +++ b/Tools/_CP14/LocalizationHelper/ftl_parser/ftl_parser.py @@ -1,23 +1,26 @@ from fluent import ftl_reader -import os +from base_parser import BaseParser -def ftl_parser(path: str) -> dict: +class FTLParser(BaseParser): """ - The function gets the path, then with the help of the os library - goes through each file,checks that the file extension is "ftl", - then reads it through the "ftl_reader" module of the "fluent" package. + The class inherits from the "BaseParser" class, parses ftl files of localization. """ - prototypes = {} - for dirpath, _, filenames in os.walk(path): - for filename in filenames: - path = f"{dirpath}\\{filename}" + def ftl_parser(self) -> dict: + """ + The function gets the path, then with the help of the os library + goes through each file,checks that the file extension is "ftl", + then reads it through the "ftl_reader" module of the "fluent" package. + """ + prototypes = {} - if not filename.endswith(".ftl"): + for path in self.get_files_paths(): + + if not self.check_file_extension(path, ".ftl"): continue - file = ftl_reader.read_ftl(path) + file = ftl_reader.read_ftl((path, self.errors_path)) prototypes.update(file) - return prototypes + return prototypes diff --git a/Tools/_CP14/LocalizationHelper/main.py b/Tools/_CP14/LocalizationHelper/main.py index 1ad5cec839..6b1dc7e984 100644 --- a/Tools/_CP14/LocalizationHelper/main.py +++ b/Tools/_CP14/LocalizationHelper/main.py @@ -1,6 +1,7 @@ import json -from yml_parser import yml_parser -from ftl_parser import ftl_parser +import os +from yml_parser import YMLParser +from ftl_parser import FTLParser from fluent import ftl_writer @@ -12,30 +13,91 @@ def read_config(): def get_paths(config: dict): prototypes_path = config["paths"]["prototypes"] localization_path = config["paths"]["localization"] - return prototypes_path, localization_path + errors_log_path = config["paths"]["error_log_path"] + yml_parser_last_launch = config["paths"]["yml_parser_last_launch"] + return prototypes_path, localization_path, errors_log_path, yml_parser_last_launch +def print_errors_log_info(errors_log_path: str, all_prototypes: dict) -> None: + with open(errors_log_path, "r") as file: + errors = file.read() + + successful_count = len(all_prototypes) - errors.count("ERROR") + print(f"""Of the {len(all_prototypes)} prototypes, {successful_count} were successfully processed. + +Errors can be found in {errors_log_path} +Number of errors during YML processing - {errors.count("YML-ERROR")} +Number of errors during FTL processing - {errors.count("FTL-ERROR")} +Number of errors during data extraction and creation of new FTL - {errors.count("RETRIEVING-ERROR")}""") + + +def check_changed_attrs(yml_parser_last_launch: str, prototypes_dict: dict, localization_dict: dict): + """ + + What error it fixes - without this function, changed attributes of prototypes that have not been changed in + localization files will simply not be added to the original ftl file, because the script first of all takes data + from localization files, if they exist, of course + + The function gets the data received during the last run of the script, and checks if some attribute from + the last run has been changed,then simply replaces with this attribute the attribute + of the prototype received during parsing of localization files. + """ + if os.path.isfile(yml_parser_last_launch): + with open(yml_parser_last_launch, 'r', encoding='utf-8') as file: + last_launch_prototypes = json.load(file) + + for prototype, proto_attrs_in_prototypes in prototypes_dict.items(): + if prototype in last_launch_prototypes and prototype in localization_dict: + attrs = localization_dict[prototype] + last_launch_prototype_attrs = last_launch_prototypes[prototype] + + for key, value in proto_attrs_in_prototypes.items(): + if value != last_launch_prototype_attrs[key]: + attrs[key] = value + + localization_dict[prototype] = attrs + + +def save_result(entities: str, file_name: str) -> None: + with open(file_name, "w", encoding="utf-8") as file: + file.write(entities) + + print(f"{file_name} has been created\n") + def main(): """ - The function reads the config, gets paths to prototypes and localization files, - and through parsers gets two dictionaries with information about prototypes, - and creates one common vocabulary. + The function gets paths, creates dictionaries with the help of parsers, + performs various checks, and finally creates ftl file. """ - with open("logs/errors_log.txt", "w") as file: + config = read_config() + prototypes_path, localization_path, errors_log_path, yml_parser_last_launch = get_paths(config) + + if not os.path.isdir("last_launch"): + os.mkdir("last_launch") + + with open(errors_log_path, "w") as file: file.write("") - config = read_config() - prototypes_path, localization_path = get_paths(config) - prototypes_dict, localization_dict = yml_parser.yml_parser(prototypes_path), ftl_parser.ftl_parser(localization_path) + yml_parser = YMLParser((prototypes_path, errors_log_path)) + prototypes_dict = yml_parser.yml_parser() + ftl_parser = FTLParser((localization_path, errors_log_path)) + localization_dict = ftl_parser.ftl_parser() + + check_changed_attrs(yml_parser_last_launch, prototypes_dict, localization_dict) + + with open(yml_parser_last_launch, 'w') as json_file: + json.dump(prototypes_dict, json_file, indent=4) + + # This is where the two dictionaries are merged, and prototypes from + # the localization_dict dictionary are preferably selected. all_prototypes = {**prototypes_dict, **localization_dict} - entities_ftl = "" """ - The function traverses each prototype from the dictionary, checks if it has a parent, - and performs certain checks on the attributes of the parent if the prototype does not have its own attributes. + The function traverses each prototype from the dictionary, checks if it has a parent, + and performs certain checks on the attributes of the parent if the prototype does not have its own attributes. """ for prototype in all_prototypes: @@ -44,38 +106,31 @@ def main(): try: if prototype in prototypes_dict: prototype_attrs["parent"] = prototypes_dict[prototype]["parent"] - parent = prototype_attrs["parent"] - if not prototype_attrs.get("name"): - prototype_attrs["name"] = f"{{ ent-{prototype_attrs["parent"]} }}" - if not prototype_attrs["desc"]: + if not prototype_attrs.get("name"): + prototype_attrs["name"] = f"{{ ent-{parent} }}" + + if not prototype_attrs.get("desc"): if parent and not isinstance(parent, list) and prototypes_dict.get(parent): prototype_attrs["desc"] = f"{{ ent-{parent}.desc }}" + if not prototype_attrs.get("suffix"): + if prototypes_dict[prototype].get("suffix"): + prototype_attrs["suffix"] = prototypes_dict[prototype]["suffix"] + proto_ftl = ftl_writer.create_ftl(prototype, all_prototypes[prototype]) entities_ftl += proto_ftl except Exception as e: - with open("logs/errors_log.txt", "a") as file: + with open(errors_log_path, "a") as file: + print(prototype, prototype_attrs) file.write(f"RETRIEVING-ERROR:\nAn error occurred while retrieving data to be written to the file - {e}\n") - file_name = "entities.ftl" - with open(file_name, "w", encoding="utf-8") as file: - file.write(entities_ftl) + save_result(entities_ftl, file_name) - print(f"{file_name} has been created\n") - - with open("logs/errors_log.txt", "r") as file: - errors = file.read() - - successful_count = len(all_prototypes) - errors.count("ERROR") - print(f"""Of the {len(all_prototypes)} prototypes, {successful_count} were successfully processed. -Errors can be found in 'logs/errors_log.txt'. -Number of errors during YML processing - {errors.count("YML-ERROR")} -Number of errors during FTL processing - {errors.count("FTL-ERROR")} -Number of errors during data extraction and creation of new FTL - {errors.count("RETRIEVING-ERROR")}""") + print_errors_log_info(errors_log_path, all_prototypes) if __name__ == '__main__': diff --git a/Tools/_CP14/LocalizationHelper/yml_parser/__init__.py b/Tools/_CP14/LocalizationHelper/yml_parser/__init__.py index e69de29bb2..0fbb5dfb7f 100644 --- a/Tools/_CP14/LocalizationHelper/yml_parser/__init__.py +++ b/Tools/_CP14/LocalizationHelper/yml_parser/__init__.py @@ -0,0 +1 @@ +from .yml_parser import YMLParser diff --git a/Tools/_CP14/LocalizationHelper/yml_parser/yml_parser.py b/Tools/_CP14/LocalizationHelper/yml_parser/yml_parser.py index 0b0f2289c7..001691b9af 100644 --- a/Tools/_CP14/LocalizationHelper/yml_parser/yml_parser.py +++ b/Tools/_CP14/LocalizationHelper/yml_parser/yml_parser.py @@ -1,52 +1,69 @@ import yaml -import os +from base_parser import BaseParser -def check_proto_attrs(prototype: dict) -> bool: - return any(prototype.get(attr) is not None for attr in ["name", "description", "suffix"]) - - -def get_proto_attrs(prototypes: dict, prototype: dict) -> None: - prototypes[prototype.get("id")] = { - "parent": prototype.get("parent"), - "name": prototype.get("name"), - "desc": prototype.get("description"), - "suffix": prototype.get("suffix") - } - - -def yml_parser(path: str) -> dict: +class YMLParser(BaseParser): """ - The function gets the path, then with the help of the os library - goes through each file,checks that the file extension is "ftl", - then processes the file using the "PyYaml" library + The class inherits from the "BaseParser" class, parses yml prototypes. """ - prototypes = {} - for dirpath, _, filenames in os.walk(path): - for filename in filenames: - path = f"{dirpath}\\{filename}" + @staticmethod + def check_proto_attrs(prototype: dict) -> bool: + """ + The function checks that the prototype at least has some attribute from the "attrs_lst". + """ + attrs_lst = ["name", "description", "suffix"] + # In some cases a parent can be a list (because of multiple parents), + # the game will not be able to handle such cases in ftl files. + if not isinstance(prototype.get("parent"), list): + attrs_lst.append("parent") - if not filename.endswith(".yml"): + return any(prototype.get(attr) is not None for attr in attrs_lst) + + @staticmethod + def get_proto_attrs(prototypes: dict, prototype: dict) -> None: + prototypes[prototype.get("id")] = { + "parent": prototype.get("parent"), + "name": prototype.get("name"), + "desc": prototype.get("description"), + "suffix": prototype.get("suffix") + } + + @staticmethod + def create_proto(file) -> str: + proto = "" + for line in file.readlines(): + # The PyYaml library cannot handle the following SpaceStation 14 prototype syntax - !type: ... + # We need to fix this :( + if "!type" in line: + continue + proto += line + + return proto + + def yml_parser(self) -> dict: + """ + The function gets the path, then with the help of the os library + goes through each file,checks that the file extension is "yml", + then processes the file using the "PyYaml" library + """ + prototypes = {} + + for path in self.get_files_paths(): + if not self.check_file_extension(path, ".yml"): continue try: with open(path, encoding="utf-8") as file: - proto = "" - for line in file.readlines(): - # The PyYaml library cannot handle the following SpaceStation 14 prototype syntax - !type: ... - if "!type" in line: - continue - proto += line - + proto = self.create_proto(file) data = yaml.safe_load(proto) except Exception as e: - with open("logs/errors_log.txt", "a") as file: + with open(self.errors_path, "a") as file: file.write(f"YML-ERROR:\nAn error occurred during prototype processing {path}, error - {e}\n") else: if data is not None: for prototype in data: - if check_proto_attrs(prototype): - get_proto_attrs(prototypes, prototype) + if self.check_proto_attrs(prototype): + self.get_proto_attrs(prototypes, prototype) - return prototypes + return prototypes