diff --git a/binaries/data/mods/public/l10n/messages.json b/binaries/data/mods/public/l10n/messages.json index db80eaf964..b83d1affaa 100644 --- a/binaries/data/mods/public/l10n/messages.json +++ b/binaries/data/mods/public/l10n/messages.json @@ -11,10 +11,13 @@ "simulation/data/civs/**.json" ], "options": { - "keywords": [ - "Special", - "AINames" - ] + "keywords": { + "Name": {}, + "Description": {}, + "History": {}, + "Special": {}, + "AINames": {} + } } }, { @@ -23,10 +26,10 @@ "simulation/templates/special/players/**.xml" ], "options": { - "keywords": [ - "GenericName", - "History" - ] + "keywords": { + "GenericName": {}, + "History": {} + } } } ] @@ -299,10 +302,10 @@ "campaigns/**.json" ], "options": { - "keywords": [ - "Name", - "Description" - ], + "keywords": { + "Name": {}, + "Description": {} + }, "context": "Campaign Template" } } @@ -382,10 +385,10 @@ "gui/credits/texts/**.json" ], "options": { - "keywords": [ - "Title", - "Subtitle" - ] + "keywords": { + "Title": {}, + "Subtitle": {} + } } }, { @@ -394,10 +397,10 @@ "gui/hotkeys/spec/**.json" ], "options": { - "keywords": [ - "name", - "desc" - ], + "keywords": { + "name": {}, + "desc": {} + }, "context": "hotkey metadata" } }, @@ -407,10 +410,10 @@ "gui/options/**.json" ], "options": { - "keywords": [ - "label", - "tooltip" - ] + "keywords": { + "label": {}, + "tooltip": {} + } } }, { @@ -419,9 +422,9 @@ "simulation/data/resources/**.json" ], "options": { - "keywords": [ - "description" - ] + "keywords": { + "description": {} + } } }, { @@ -430,10 +433,12 @@ "simulation/data/resources/**.json" ], "options": { - "keywords": [ - "name", - "subtypes" - ], + "keywords": { + "name": {}, + "subtypes": { + "extractFromInnerKeys": true + } + }, "comments": [ "Translation: Word as used at the beginning of a sentence or as a single-word sentence." ], @@ -446,10 +451,12 @@ "simulation/data/resources/**.json" ], "options": { - "keywords": [ - "name", - "subtypes" - ], + "keywords": { + "name": {}, + "subtypes": { + "extractFromInnerKeys": true + } + }, "comments": [ "Translation: Word as used in the middle of a sentence (which may require using lowercase for your language)." ], @@ -570,6 +577,7 @@ "simulation/templates/**.xml" ], "excludeMasks": [ + "simulation/templates/special/players/**.xml", "simulation/templates/structures/**.xml", "simulation/templates/template_structure_*.xml", "simulation/templates/template_unit_*.xml", @@ -603,10 +611,10 @@ "simulation/data/damage_types/*.json" ], "options": { - "keywords": [ - "name", - "description" - ], + "keywords": { + "name": {}, + "description": {} + }, "context": "damage type" } }, @@ -616,11 +624,11 @@ "simulation/data/status_effects/*.json" ], "options": { - "keywords": [ - "statusName", - "applierTooltip", - "receiverTooltip" - ], + "keywords": { + "statusName": {}, + "applierTooltip": {}, + "receiverTooltip": {} + }, "context": "status effect" } }, @@ -630,10 +638,10 @@ "simulation/data/attack_effects/*.json" ], "options": { - "keywords": [ - "name", - "description" - ], + "keywords": { + "name": {}, + "description": {} + }, "context": "effect caused by an attack" } } @@ -651,10 +659,10 @@ "simulation/data/auras/**.json" ], "options": { - "keywords": [ - "auraName", - "auraDescription" - ] + "keywords": { + "auraName": {}, + "auraDescription": {} + } } } ] @@ -671,13 +679,15 @@ "simulation/data/technologies/**.json" ], "options": { - "keywords": [ - "specificName", - "genericName", - "description", - "tooltip", - "requirementsTooltip" - ] + "keywords": { + "specificName": { + "extractFromInnerKeys": true + }, + "genericName": {}, + "description": {}, + "tooltip": {}, + "requirementsTooltip": {} + } } } ] @@ -717,9 +727,9 @@ "simulation/data/settings/player_defaults.json" ], "options": { - "keywords": [ - "Name" - ] + "keywords": { + "Name": {} + } } }, { @@ -728,7 +738,9 @@ "simulation/data/settings/game_speeds.json" ], "options": { - "keywords": ["Title"] + "keywords": { + "Title": {} + } } }, { @@ -737,7 +749,10 @@ "simulation/data/settings/victory_conditions/*.json" ], "options": { - "keywords": ["Title", "Description"] + "keywords": { + "Title": {}, + "Description": {} + } } }, { @@ -746,7 +761,9 @@ "simulation/data/settings/starting_resources.json" ], "options": { - "keywords": ["Title"], + "keywords": { + "Title": {} + }, "context": "startingResources" } }, @@ -756,7 +773,10 @@ "simulation/data/settings/trigger_difficulties.json" ], "options": { - "keywords": ["Title", "Tooltip"] + "keywords": { + "Title": {}, + "Tooltip": {} + } } }, { @@ -765,10 +785,10 @@ "simulation/data/settings/map_sizes.json" ], "options": { - "keywords": [ - "Name", - "Tooltip" - ] + "keywords": { + "Name": {}, + "Tooltip": {} + } } }, { @@ -777,10 +797,10 @@ "simulation/ai/**.json" ], "options": { - "keywords": [ - "name", - "description" - ] + "keywords": { + "name": {}, + "description": {} + } } } ] @@ -802,10 +822,10 @@ ] }, "options": { - "keywords": [ - "Name", - "Description" - ] + "keywords": { + "Name": {}, + "Description": {} + } } }, { @@ -838,10 +858,14 @@ "keywords": { "ScriptSettings": { "extractJson": { - "keywords": [ - "Name", - "Description" - ] + "keywords": { + "Name": { + "extractFromInnerKeys": true + }, + "Description": { + "extractFromInnerKeys": true + } + } } } } @@ -853,7 +877,11 @@ "maps/random/rmbiome/**.json" ], "options": { - "keywords": ["Description"], + "keywords": { + "Description": { + "extractFromInnerKeys": true + } + }, "context": "biome definition" } } @@ -891,10 +919,10 @@ "keywords": { "ScriptSettings": { "extractJson": { - "keywords": [ - "Name", - "Description" - ] + "keywords": { + "Name": {}, + "Description": {} + } } } } diff --git a/source/tools/i18n/extractors/extractors.py b/source/tools/i18n/extractors/extractors.py index 12d5cf854a..4fb8c65beb 100644 --- a/source/tools/i18n/extractors/extractors.py +++ b/source/tools/i18n/extractors/extractors.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 Wildfire Games. +# Copyright (C) 2022 Wildfire Games. # All rights reserved. # # Redistribution and use in source and binary forms, with or without modification, are permitted provided that the @@ -326,20 +326,20 @@ class json(Extractor): def extractFromFile(self, filepath): with codecs.open(filepath, "r", 'utf-8') as fileObject: - for message, breadcrumbs in self.extractFromString(fileObject.read()): - yield message, None, self.context, self.formatBreadcrumbs(breadcrumbs), None, self.comments + for message, context, breadcrumbs in self.extractFromString(fileObject.read()): + yield message, None, context, self.formatBreadcrumbs(breadcrumbs), None, self.comments def extractFromString(self, string): self.breadcrumbs = [] jsonDocument = jsonParser.loads(string) if isinstance(jsonDocument, list): - for message, breadcrumbs in self.parseList(jsonDocument): + for message, context, breadcrumbs in self.parseList(jsonDocument): if message: # Skip empty strings. - yield message, breadcrumbs + yield message, context, breadcrumbs elif isinstance(jsonDocument, dict): - for message, breadcrumbs in self.parseDictionary(jsonDocument): + for message, context, breadcrumbs in self.parseDictionary(jsonDocument): if message: # Skip empty strings. - yield message, breadcrumbs + yield message, context, breadcrumbs else: raise Exception("Unexpected JSON document parent structure (not a list or a dictionary). You must extend the JSON extractor to support it.") @@ -348,11 +348,11 @@ class json(Extractor): for listItem in itemsList: self.breadcrumbs.append(index) if isinstance(listItem, list): - for message, breadcrumbs in self.parseList(listItem): - yield message, breadcrumbs + for message, context, breadcrumbs in self.parseList(listItem): + yield message, context, breadcrumbs elif isinstance(listItem, dict): - for message, breadcrumbs in self.parseDictionary(listItem): - yield message, breadcrumbs + for message, context, breadcrumbs in self.parseDictionary(listItem): + yield message, context, breadcrumbs del self.breadcrumbs[-1] index += 1 @@ -361,35 +361,79 @@ class json(Extractor): self.breadcrumbs.append(keyword) if keyword in self.keywords: if isinstance(dictionary[keyword], str): - yield dictionary[keyword], self.breadcrumbs + yield self.extractString(dictionary[keyword], keyword) elif isinstance(dictionary[keyword], list): - for message, breadcrumbs in self.extractList(dictionary[keyword]): - yield message, breadcrumbs + for message, context, breadcrumbs in self.extractList(dictionary[keyword], keyword): + yield message, context, breadcrumbs elif isinstance(dictionary[keyword], dict): - for message, breadcrumbs in self.extractDictionary(dictionary[keyword]): - yield message, breadcrumbs + extract = None + if "extractFromInnerKeys" in self.keywords[keyword] and self.keywords[keyword]["extractFromInnerKeys"]: + for message, context, breadcrumbs in self.extractDictionaryInnerKeys(dictionary[keyword], keyword): + yield message, context, breadcrumbs + else: + extract = self.extractDictionary(dictionary[keyword], keyword) + if extract: + yield extract elif isinstance(dictionary[keyword], list): - for message, breadcrumbs in self.parseList(dictionary[keyword]): - yield message, breadcrumbs + for message, context, breadcrumbs in self.parseList(dictionary[keyword]): + yield message, context, breadcrumbs elif isinstance(dictionary[keyword], dict): - for message, breadcrumbs in self.parseDictionary(dictionary[keyword]): - yield message, breadcrumbs + for message, context, breadcrumbs in self.parseDictionary(dictionary[keyword]): + yield message, context, breadcrumbs del self.breadcrumbs[-1] - def extractList(self, itemsList): + def extractString(self, string, keyword): + context = None + if "tagAsContext" in self.keywords[keyword]: + context = keyword + elif "customContext" in self.keywords[keyword]: + context = self.keywords[keyword]["customContext"] + else: + context = self.context + return string, context, self.breadcrumbs + + def extractList(self, itemsList, keyword): index = 0 for listItem in itemsList: self.breadcrumbs.append(index) if isinstance(listItem, str): - yield listItem, self.breadcrumbs + yield self.extractString(listItem, keyword) + elif isinstance(listItem, dict): + extract = self.extractDictionary(dictionary[keyword], keyword) + if extract: + yield extract del self.breadcrumbs[-1] index += 1 - def extractDictionary(self, dictionary): - for keyword in dictionary: - self.breadcrumbs.append(keyword) - if isinstance(dictionary[keyword], str): - yield dictionary[keyword], self.breadcrumbs + def extractDictionary(self, dictionary, keyword): + message = dictionary.get("_string", None) + self.breadcrumbs.append("_string") + if message and isinstance(message, str): + context = None + if "context" in dictionary: + context = str(dictionary["context"]) + elif "tagAsContext" in self.keywords[keyword]: + context = keyword + elif "customContext" in self.keywords[keyword]: + context = self.keywords[keyword]["customContext"] + else: + context = self.context + return message, context, self.breadcrumbs + del self.breadcrumbs[-1] + return None + + def extractDictionaryInnerKeys(self, dictionary, keyword): + for innerKeyword in dictionary: + self.breadcrumbs.append(innerKeyword) + if isinstance(dictionary[innerKeyword], str): + yield self.extractString(dictionary[innerKeyword], keyword) + elif isinstance(dictionary[innerKeyword], list): + for message, context, breadcrumbs in self.extractList(dictionary[innerKeyword], keyword): + yield message, context, breadcrumbs + elif isinstance(dictionary[innerKeyword], dict): + extract = self.extractDictionary(dictionary[innerKeyword], keyword) + if extract: + yield extract del self.breadcrumbs[-1] @@ -416,14 +460,14 @@ class xml(Extractor): for element in xmlDocument.iter(keyword): position = element.sourceline if element.text is not None: - context = None comments = [] if "extractJson" in self.keywords[keyword]: jsonExtractor = self.getJsonExtractor() jsonExtractor.setOptions(self.keywords[keyword]["extractJson"]) - for message, breadcrumbs in jsonExtractor.extractFromString(element.text): + for message, context, breadcrumbs in jsonExtractor.extractFromString(element.text): yield message, None, context, json.formatBreadcrumbs(breadcrumbs), position, comments else: + context = None breadcrumb = None if "locationAttributes" in self.keywords[keyword]: attributes = [element.get(attribute) for attribute in self.keywords[keyword]["locationAttributes"] if attribute in element.attrib]