diff --git a/babel_godot.py b/babel_godot.py index d14265b..6f9f98f 100644 --- a/babel_godot.py +++ b/babel_godot.py @@ -5,7 +5,8 @@ _godot_node = re.compile(r'^\[node name="([^"]+)" (?:type="([^"]+)")?') -_godot_property_str = re.compile(r'^([A-Za-z0-9_]+)\s*=\s*(".+)$') +_godot_property_str = re.compile(r'^([A-Za-z0-9_]+)\s*=\s*([\[|"].+)$') +_godot_escaped_tr = re.compile(r'^.*[^A-Za-z0-9_]tr\(\\"([^\\"]+)\\"\)?') def _godot_unquote(string): @@ -30,6 +31,43 @@ def _godot_unquote(string): result.append(c) return ''.join(result) +def _assemble_multiline_string(line, multiline): + to_yield = [] + + if '", "' in line: + # Multiline string ends within an array of strings + line_parts = line.split('", "') + multiline['value'] += line_parts[0] + + value = _godot_unquote('"' + multiline['value'] + '"') + if value is not None: + to_yield.append([multiline['keyword'], value]) + + # Take care of intermediate strings in array (not multiline) + for line_part in line_parts[1:-1]: + value = _godot_unquote('"' + line_part + '"') + if value is not None: + to_yield.append([multiline['keyword'], value]) + + # Continue with the last array item normally + multiline['value'] = '' + line = line_parts[-1] + + if not line.endswith('"\n') and not line.endswith(']\n'): + # Continuation of multiline string + multiline['value'] += line + else: + # Multiline string ends + multiline['value'] += line.strip('"]\n') + + value = _godot_unquote('"' + multiline['value'] + '"') + if value is not None: + to_yield.append([multiline['keyword'], value]) + + multiline['keyword'] = '' + multiline['value'] = '' + + return to_yield def extract_godot_scene(fileobj, keywords, comment_tags, options): """Extract messages from Godot scene files (.tscn). @@ -48,6 +86,10 @@ def extract_godot_scene(fileobj, keywords, comment_tags, options): current_node_type = None + multiline = {'keyword': '', 'value': ''} + + look_for_builtin_tr = 'tr' in keywords + properties_to_translate = {} for keyword in keywords: if '/' in keyword: @@ -63,6 +105,15 @@ def check_translate_property(property): for lineno, line in enumerate(fileobj, start=1): line = line.decode(encoding) + + # Handle multiline strings + if multiline['keyword']: + to_yield = _assemble_multiline_string(line, multiline) + for item in to_yield: + yield (lineno, item[0], [item[1]], []) + + continue + match = _godot_node.match(line) if match: # Store which kind of node we're in @@ -82,10 +133,30 @@ def check_translate_property(property): value = match.group(2) keyword = check_translate_property(property) if keyword: - value = _godot_unquote(value) - if value is not None: - yield (lineno, keyword, [value], []) - + # Beginning of multiline string + if not value.endswith('"') and not value.endswith(']'): + multiline['keyword'] = keyword + multiline['value'] = value.strip('[ "') + '\n' + continue + + # Handle array of strings + if value.startswith('[ "'): + values = value.strip('[ "]').split('", "') + for value in values: + value = _godot_unquote('"' + value + '"') + if value is not None: + yield (lineno, keyword, [value], []) + else: + value = _godot_unquote(value) + if value is not None: + yield (lineno, keyword, [value], []) + elif look_for_builtin_tr: + # Handle Godot's tr() for built-in scripts + match = _godot_escaped_tr.match(line) + if match: + value = _godot_unquote('"' + match.group(1) + '"') + if value is not None: + yield (lineno, keyword, [value], []) def extract_godot_resource(fileobj, keywords, comment_tags, options): """Extract messages from Godot resource files (.res, .tres). @@ -102,10 +173,15 @@ def extract_godot_resource(fileobj, keywords, comment_tags, options): """ encoding = options.get('encoding', 'utf-8') + multiline = {'keyword': '', 'value': ''} + properties_to_translate = {} for keyword in keywords: if keyword.startswith('Resource/'): properties_to_translate[keyword[9:]] = keyword + else: + # Without this else-case, any '' properties (not starting with 'Resource/') would be ignored + properties_to_translate[keyword] = keyword def check_translate_property(property): return properties_to_translate.get(property) @@ -115,12 +191,34 @@ def check_translate_property(property): if line.startswith('['): continue + # Handle multiline strings + if multiline['keyword']: + to_yield = _assemble_multiline_string(line, multiline) + for item in to_yield: + yield (lineno, item[0], [item[1]], []) + + continue + match = _godot_property_str.match(line) if match: property = match.group(1) value = match.group(2) keyword = check_translate_property(property) if keyword: - value = _godot_unquote(value) - if value is not None: - yield (lineno, keyword, [value], []) + # Beginning of multiline string + if not value.endswith('"') and not value.endswith(']'): + multiline['keyword'] = keyword + multiline['value'] = value.strip('[ "') + '\n' + continue + + # Handle array of strings + if value.startswith('[ "'): + values = value.strip('[ "]').split('", "') + for value in values: + value = _godot_unquote('"' + value + '"') + if value is not None: + yield (lineno, keyword, [value], []) + else: + value = _godot_unquote(value) + if value is not None: + yield (lineno, keyword, [value], [])