diff --git a/scripts/translations/export_translations.py b/scripts/translations/export_translations.py index 5c10b58e20..ffb96bcdc0 100644 --- a/scripts/translations/export_translations.py +++ b/scripts/translations/export_translations.py @@ -1,5 +1,6 @@ # coding=UTF-8 +import math import os import re import sys @@ -9,6 +10,54 @@ PREFIXED_NAME_START = 'name="fui_' UNPREFIXED_NAME_START = 'name="' +CHAR_LIMIT_PATTERN = re.compile(r'\[CHAR_LIMIT=\d+\]') +TRANSLATION_DESC_PATTERN = re.compile(r'(translation_description="[^"]*?)(")') +XML_TAG_PATTERN = re.compile(r'<[^>]+>') +STRING_VALUE_PATTERN = re.compile(r'>([^<]*(?:]*>[^<]*[^<]*)*)') +ITEM_VALUE_PATTERN = re.compile(r'>([^<]*(?:]*>[^<]*[^<]*)*)') + +XML_ENTITIES = {'&': '&', '<': '<', '>': '>', '"': '"', ''': "'"} +ESCAPE_PATTERN = re.compile(r'\\(u[0-9A-Fa-f]{4}|n|t|\'|")') + + +def _decode_entities(text): + """Decode XML entities and Android escape sequences to get true visible length.""" + for entity, char in XML_ENTITIES.items(): + text = text.replace(entity, char) + text = ESCAPE_PATTERN.sub('X', text) + return text + + +def _extract_visible_text(value): + """Strip XML tags and decode entities to get the visible text length.""" + text = XML_TAG_PATTERN.sub('', value).strip() + return _decode_entities(text) + + +def _calculate_char_limit(english_text): + """Return ~1.5x the English text length, rounded up to the nearest 5.""" + length = len(english_text) + limit = math.ceil(length * 1.5) + return int(math.ceil(limit / 5.0) * 5) + + +def _add_char_limit(text, value_pattern): + """Add [CHAR_LIMIT=xxx] to translation_description if missing.""" + if 'translation_description=' not in text: + return text + if CHAR_LIMIT_PATTERN.search(text): + return text + match = value_pattern.search(text) + if not match: + return text + visible = _extract_visible_text(match.group(1)) + if not visible: + return text + limit = _calculate_char_limit(visible) + return TRANSLATION_DESC_PATTERN.sub( + r'\1 [CHAR_LIMIT=%d]\2' % limit, text, count=1) + + class ExportTranslationsScript(BaseStringScript): def ProcessTag(self, line, type): @@ -16,13 +65,21 @@ def ProcessTag(self, line, type): if PREFIXED_NAME_START in joined: joined = joined.replace(PREFIXED_NAME_START, UNPREFIXED_NAME_START) - return joined.split('\n') - else: - return line + + if type == self.TYPE_STR: + joined = _add_char_limit(joined, STRING_VALUE_PATTERN) + elif type == self.TYPE_PLUR: + result_lines = [] + for l in joined.split('\n'): + if '' in l: + l = _add_char_limit(l, ITEM_VALUE_PATTERN) + result_lines.append(l) + joined = '\n'.join(result_lines) + + return joined.split('\n') def WriteFile(self, file_name, file_contents): - # Override to just print the contents - print file_contents + print(file_contents) if __name__ == '__main__': ets = ExportTranslationsScript()