From 8f2f091d82920094696bc0838550e8b1b695576d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matjaz=CC=8C=20Horvat?= Date: Wed, 13 Jan 2016 19:06:00 +0100 Subject: [PATCH] Fix bug 1193860: escape quotes in some DTD files --- pontoon/sync/formats/silme.py | 33 +++++++++++++++++-- pontoon/sync/tests/formats/test_silme.py | 41 ++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 2 deletions(-) diff --git a/pontoon/sync/formats/silme.py b/pontoon/sync/formats/silme.py index c42856ffb2..300ffd015c 100644 --- a/pontoon/sync/formats/silme.py +++ b/pontoon/sync/formats/silme.py @@ -4,10 +4,11 @@ """ import codecs import os +import silme + from collections import OrderedDict from copy import copy -import silme from silme.format.dtd import FormatParser as DTDParser from silme.format.ini import FormatParser as IniParser from silme.format.inc import FormatParser as IncParser @@ -78,6 +79,9 @@ def __init__(self, parser, path, source_resource=None): self.source_resource = source_resource self.entities = OrderedDict() # Preserve entity order. + # Bug 1193860: unescape quotes in some files + self.escape_quotes_on = 'mobile/android/base' in path and parser is DTDParser + # Copy entities from the source_resource if it's available. if source_resource: for key, entity in source_resource.entities.items(): @@ -103,6 +107,10 @@ def __init__(self, parser, path, source_resource=None): current_order = 0 for obj in self.structure: if isinstance(obj, silme.core.entity.Entity): + + if self.escape_quotes_on: + obj.value = self.unescape_quotes(obj.value) + entity = SilmeEntity(obj, comments, current_order) self.entities[entity.key] = entity current_order += 1 @@ -118,6 +126,22 @@ def __init__(self, parser, path, source_resource=None): def translations(self): return self.entities.values() + def escape_quotes(self, value): + """ + DTD files can use single or double quotes for identifying strings, + so " and ' are the safe bet that will work in both cases. + """ + value = value.replace('"', '\\"') + value = value.replace("'", '\\'') + return value + + def unescape_quotes(self, value): + value = value.replace('\\"', '"') + value = value.replace('\\"', '"') + value = value.replace('\\'', "'") + value = value.replace("\\'", "'") + return value + def save(self, locale): """ Load the source resource, modify it with changes made to this @@ -144,7 +168,12 @@ def save(self, locale): translated_entity = self.entities.get(key) if translated_entity and None in translated_entity.strings: - new_structure.modify_entity(key, translated_entity.strings[None]) + translation = translated_entity.strings[None] + + if self.escape_quotes_on: + translation = self.escape_quotes(translation) + + new_structure.modify_entity(key, translation) else: # Remove untranslated entity and following newline pos = new_structure.entity_pos(key) diff --git a/pontoon/sync/tests/formats/test_silme.py b/pontoon/sync/tests/formats/test_silme.py index d269a76fd3..c1ec19472b 100644 --- a/pontoon/sync/tests/formats/test_silme.py +++ b/pontoon/sync/tests/formats/test_silme.py @@ -185,6 +185,47 @@ def test_save_translation_identical(self): self.run_save_translation_identical(source_string, input_string, expected_string) + def test_quotes(self): + input_strings = dedent(""" + + + + + """) + + # Make sure path contains 'mobile/android/base' + dirname = os.path.join(tempfile.mkdtemp(), 'mobile', 'android', 'base') + os.makedirs(dirname) + + fd, path = tempfile.mkstemp(dir=dirname) + with os.fdopen(fd, 'w') as f: + f.write(input_strings) + + resource = self.parse(path) + + # Unescape quotes when parsing + assert_attributes_equal(resource.translations[0], strings={None: "'"}) + assert_attributes_equal(resource.translations[1], strings={None: "'"}) + assert_attributes_equal(resource.translations[2], strings={None: '"'}) + assert_attributes_equal(resource.translations[3], strings={None: '"'}) + + # Escape quotes when saving + translated_resource = silme.SilmeResource( + DTDParser, path, source_resource=resource + ) + translated_resource.translations[0].strings[None] = "Single Quote '" + translated_resource.translations[1].strings[None] = 'Double Quote "' + + translated_resource.save(self.locale) + + expected_string = dedent(""" + + + + + """) + self.assert_file_content(path, expected_string) + BASE_PROPERTIES_FILE = """ # Sample comment