diff --git a/openformats/formats/json.py b/openformats/formats/json.py index c1d4ea6..6679139 100644 --- a/openformats/formats/json.py +++ b/openformats/formats/json.py @@ -43,8 +43,8 @@ class JsonHandler(Handler): name = "KEYVALUEJSON" extension = "json" - PLURAL_ARG = 'plural' - PLURAL_KEYS_STR = ' '.join(six.iterkeys(Handler._RULES_ATOI)) + PLURAL_ARG = "plural" + PLURAL_KEYS_STR = " ".join(six.iterkeys(Handler._RULES_ATOI)) def parse(self, content, **kwargs): # Validate that content is JSON @@ -63,7 +63,7 @@ def parse(self, content, **kwargs): self._extract(parsed) if not self.stringset and self.name == "STRUCTURED_JSON": - raise ParseError('No strings could be extracted') + raise ParseError("No strings could be extracted") self.transcriber.copy_until(len(source)) @@ -80,33 +80,37 @@ def _extract(self, parsed, nest=None): if key in self.existing_keys: # Need this for line number self.transcriber.copy_until(key_position) - raise ParseError(u"Duplicate string key ('{}') in line {}". - format(key, self.transcriber.line_number)) + raise ParseError( + "Duplicate string key ('{}') in line {}".format( + key, self.transcriber.line_number + ) + ) self.existing_keys.add(key) if self.name == "STRUCTURED_JSON": try: - (string_value, _), = value.find_children( - self.STRING_KEY - ) + ((string_value, _),) = value.find_children(self.STRING_KEY) except Exception: # Ignore other types of values like lists pass else: if string_value: - if isinstance(string_value, (six.binary_type, - six.text_type)): + if isinstance( + string_value, (six.binary_type, six.text_type) + ): if string_value.strip(): - openstring = self._create_openstring( - key, value) + openstring = self._create_openstring(key, value) if openstring: self.stringset.append(openstring) else: # Need this for line number self.transcriber.copy_until(key_position) - raise ParseError(u"Invalid string value in line {}". - format(self.transcriber.line_number)) + raise ParseError( + "Invalid string value in line {}".format( + self.transcriber.line_number + ) + ) elif isinstance(value, DumbJson): self._extract(value, key) @@ -115,8 +119,7 @@ def _extract(self, parsed, nest=None): if not value.strip(): continue - openstring = self._create_openstring(key, value, - value_position) + openstring = self._create_openstring(key, value, value_position) if openstring: self.stringset.append(openstring) @@ -137,8 +140,7 @@ def _extract(self, parsed, nest=None): if not item.strip(): continue - openstring = self._create_openstring(key, item, - item_position) + openstring = self._create_openstring(key, item, item_position) if openstring: self.stringset.append(openstring) @@ -162,17 +164,16 @@ def _create_openstring(self, key, value, value_position): # e.g. a pluralized string. # If it cannot be parsed that way (returns None), parse it like # a regular string. - parser = ICUParser(allow_numeric_plural_values = False) + parser = ICUParser(allow_numeric_plural_values=False) icu_string = parser.parse(key, value) if icu_string: return self._create_pluralized_string(icu_string, value_position) - return self._create_regular_string( - key, value, value_position - ) + return self._create_regular_string(key, value, value_position) - def _create_pluralized_string(self, icu_string, value_position, - context_value="", description_value=""): + def _create_pluralized_string( + self, icu_string, value_position, context_value="", description_value="" + ): """Create a pluralized string based on the given information. Also updates the transcriber accordingly. @@ -199,8 +200,9 @@ def _create_pluralized_string(self, icu_string, value_position, return openstring - def _create_regular_string(self, key, value, value_position, - context_value="", description_value=""): + def _create_regular_string( + self, key, value, value_position, context_value="", description_value="" + ): """Return a new simple OpenString based on the given key and value and update the transcriber accordingly. @@ -208,9 +210,12 @@ def _create_regular_string(self, key, value, value_position, :param value: the translation string :return: an OpenString or None """ - openstring = OpenString(key, value, order=next(self._order), - context=context_value, - developer_comment=description_value, + openstring = OpenString( + key, + value, + order=next(self._order), + context=context_value, + developer_comment=description_value, ) self.transcriber.copy_until(value_position) self.transcriber.add(openstring.template_replacement) @@ -220,9 +225,10 @@ def _create_regular_string(self, key, value, value_position, @staticmethod def _escape_key(key): - key = key.replace(DumbJson.BACKSLASH, - u''.join([DumbJson.BACKSLASH, DumbJson.BACKSLASH])) - key = key.replace(u".", u''.join([DumbJson.BACKSLASH, '.'])) + key = key.replace( + DumbJson.BACKSLASH, "".join([DumbJson.BACKSLASH, DumbJson.BACKSLASH]) + ) + key = key.replace(".", "".join([DumbJson.BACKSLASH, "."])) return key def compile(self, template, stringset, **kwargs): @@ -238,15 +244,15 @@ def compile(self, template, stringset, **kwargs): stringset = list(stringset) fake_stringset = [ - OpenString(openstring.key, - openstring.template_replacement, - order=openstring.order, - pluralized=openstring.pluralized) + OpenString( + openstring.key, + openstring.template_replacement, + order=openstring.order, + pluralized=openstring.pluralized, + ) for openstring in stringset ] - new_template = self._replace_translations( - template, fake_stringset, False - ) + new_template = self._replace_translations(template, fake_stringset, False) new_template = self._clean_empties(new_template) return self._replace_translations(new_template, stringset, True) @@ -282,12 +288,12 @@ def _insert_item(self, value, value_position, is_real_stringset, key_position=0) string = self._get_next_string() string_exists = string is not None - templ_replacement = string.template_replacement \ - if string_exists else None + templ_replacement = ( + string.template_replacement if string_exists else None + ) # Pluralized string - if string_exists and string.pluralized \ - and templ_replacement in value: + if string_exists and string.pluralized and templ_replacement in value: at_least_one = True self._insert_plural_string( value, value_position, string, is_real_stringset @@ -296,15 +302,11 @@ def _insert_item(self, value, value_position, is_real_stringset, key_position=0) # Regular string elif string_exists and value == templ_replacement: at_least_one = True - self._insert_regular_string( - value, value_position, string.string - ) + self._insert_regular_string(value, value_position, string.string) else: # Anything else: just remove the current section - self._copy_until_and_remove_section( - value_position + len(value) + 1 - ) + self._copy_until_and_remove_section(value_position + len(value) + 1) else: # value is an empty string, add the key but don't update # stringset_index @@ -312,14 +314,10 @@ def _insert_item(self, value, value_position, is_real_stringset, key_position=0) if len(value) > 0: # Add whitespace back - self._insert_regular_string( - value, value_position, value, False - ) + self._insert_regular_string(value, value_position, value, False) else: at_least_one = True - self._insert_regular_string( - value, value_position, '', False - ) + self._insert_regular_string(value, value_position, "", False) elif isinstance(value, DumbJson): self.depth += 1 @@ -344,8 +342,14 @@ def _insert_from_dict(self, parsed, is_real_stringset): at_least_one = not bool(list(parsed)) for key, key_position, value, value_position in parsed: - if key.startswith("@") and key != ("@@locale") and isinstance(value, (six.binary_type, six.text_type)) : - self.metadata_blocks.append((key_position - 1, value_position + len(value) + 1)) + if ( + key.startswith("@") + and key != ("@@locale") + and isinstance(value, (six.binary_type, six.text_type)) + ): + self.metadata_blocks.append( + (key_position - 1, value_position + len(value) + 1) + ) self.transcriber.copy_until(key_position - 1) self.transcriber.mark_section_start() @@ -375,30 +379,25 @@ def _insert_from_list(self, parsed, is_real_stringset): return at_least_one - def _insert_plural_string(self, value, value_position, string, - is_real_stringset): + def _insert_plural_string(self, value, value_position, string, is_real_stringset): templ_replacement = string.template_replacement replacement_pos = value.find(templ_replacement) if is_real_stringset: - replacement = ICUCompiler().serialize_strings(string.string, - delimiter=' ') + replacement = ICUCompiler().serialize_strings(string.string, delimiter=" ") else: replacement = templ_replacement - self.transcriber.copy_until( - value_position + replacement_pos - ) + self.transcriber.copy_until(value_position + replacement_pos) self.transcriber.add(replacement) self.transcriber.skip(len(templ_replacement)) - self.transcriber.copy( - len(value) - replacement_pos - len(templ_replacement) - ) + self.transcriber.copy(len(value) - replacement_pos - len(templ_replacement)) self.stringset_index += 1 - def _insert_regular_string(self, value, value_position, string, - update_stringset_index=True): + def _insert_regular_string( + self, value, value_position, string, update_stringset_index=True + ): self.transcriber.copy_until(value_position) self.transcriber.add(string) self.transcriber.skip(len(value)) @@ -426,47 +425,52 @@ def validate_content(self, content): raise ParseError(six.text_type(e)) def _clean_empties(self, compiled): - """ If sections were removed, clean leftover commas, brackets etc. + """If sections were removed, clean leftover commas, brackets etc. - Eg: - '{"a": "b", ,"c": "d"}' -> '{"a": "b", "c": "d"}' - '{, "a": "b", "c": "d"}' -> '{"a": "b", "c": "d"}' - '["a", , "b"]' -> '["a", "b"]' + Eg: + '{"a": "b", ,"c": "d"}' -> '{"a": "b", "c": "d"}' + '{, "a": "b", "c": "d"}' -> '{"a": "b", "c": "d"}' + '["a", , "b"]' -> '["a", "b"]' """ while True: # First key-value of a dict was removed - match = re.search(r'{\s*,', compiled) + match = re.search(r"{\s*,", compiled) if match: - compiled = u"{}{{{}".format(compiled[:match.start()], - compiled[match.end():]) + compiled = "{}{{{}".format( + compiled[: match.start()], compiled[match.end() :] + ) continue # Last key-value of a dict was removed - match = re.search(r',\s*}', compiled) + match = re.search(r",\s*}", compiled) if match: - compiled = u"{}}}{}".format(compiled[:match.start()], - compiled[match.end():]) + compiled = "{}}}{}".format( + compiled[: match.start()], compiled[match.end() :] + ) continue # First item of a list was removed - match = re.search(r'\[\s*,', compiled) + match = re.search(r"\[\s*,", compiled) if match: - compiled = u"{}[{}".format(compiled[:match.start()], - compiled[match.end():]) + compiled = "{}[{}".format( + compiled[: match.start()], compiled[match.end() :] + ) continue # Last item of a list was removed - match = re.search(r',\s*\]', compiled) + match = re.search(r",\s*\]", compiled) if match: - compiled = u"{}]{}".format(compiled[:match.start()], - compiled[match.end():]) + compiled = "{}]{}".format( + compiled[: match.start()], compiled[match.end() :] + ) continue # Intermediate key-value of a dict or list was removed - match = re.search(r',\s*,', compiled) + match = re.search(r",\s*,", compiled) if match: - compiled = u"{},{}".format(compiled[:match.start()], - compiled[match.end():]) + compiled = "{},{}".format( + compiled[: match.start()], compiled[match.end() :] + ) continue # No substitutions happened, break @@ -515,7 +519,7 @@ def parse(self, content, **kwargs): self._extract(parsed) if not self.stringset: - raise ParseError('No strings could be extracted') + raise ParseError("No strings could be extracted") self.transcriber.copy_until(len(source)) @@ -531,8 +535,11 @@ def _find_keys(self, parsed, nest=None): if key in self.existing_keys: # Need this for line number self.transcriber.copy_until(key_position) - raise ParseError(u"Duplicate string key ('{}') in line {}". - format(key, self.transcriber.line_number)) + raise ParseError( + "Duplicate string key ('{}') in line {}".format( + key, self.transcriber.line_number + ) + ) if nest is None: # store all root-level keys in order to detect duplication self.existing_keys.add(key) elif key.startswith("@"): @@ -561,34 +568,43 @@ def _extract(self, parsed): continue context_key = f"@{key}.context" - context_value = self.metadata[context_key] \ - if context_key in self.metadata.keys() else "" + context_value = ( + self.metadata[context_key] + if context_key in self.metadata.keys() + else "" + ) description_key = f"@{key}.description" - description_value = self.metadata[description_key] \ - if description_key in self.metadata.keys() else "" + description_value = ( + self.metadata[description_key] + if description_key in self.metadata.keys() + else "" + ) - openstring = self._create_openstring(key, value, - value_position, - context_value, - description_value) + openstring = self._create_openstring( + key, value, value_position, context_value, description_value + ) if openstring: self.stringset.append(openstring) else: # Ignore other JSON types (bools, nulls, numbers) pass - def _create_openstring(self, key, value, value_position, - context_value, description_value): - parser = ICUParser(allow_numeric_plural_values = True) + def _create_openstring( + self, key, value, value_position, context_value, description_value + ): + parser = ICUParser(allow_numeric_plural_values=True) icu_string = parser.parse(key, value) + if icu_string and any( + (string.strip() == "" for string in icu_string.strings_by_rule.values()) + ): + return if icu_string: - return self._create_pluralized_string(icu_string, value_position, - context_value, - description_value) + return self._create_pluralized_string( + icu_string, value_position, context_value, description_value + ) return self._create_regular_string( - key, value, value_position, - context_value, description_value + key, value, value_position, context_value, description_value ) def compile(self, template, stringset, language_info=None, **kwargs): @@ -600,46 +616,46 @@ def compile(self, template, stringset, language_info=None, **kwargs): # from the template to clear out any `..., ,...` or `...{ ,...` # sequences left. The result will be used as the actual template for # the compilation process - self.keep_sections = kwargs.get('keep_sections', True) + self.keep_sections = kwargs.get("keep_sections", True) stringset = list(stringset) fake_stringset = [ - OpenString(openstring.key, - openstring.template_replacement, - order=openstring.order, - pluralized=openstring.pluralized) + OpenString( + openstring.key, + openstring.template_replacement, + order=openstring.order, + pluralized=openstring.pluralized, + ) for openstring in stringset ] - new_template = self._replace_translations( - template, fake_stringset, False - ) + new_template = self._replace_translations(template, fake_stringset, False) num_of_mdb = len(self.metadata_blocks) while num_of_mdb > 0: idx = num_of_mdb - 1 - new_template = u"{}{}".format( - new_template[:self.metadata_blocks[idx][0]], - new_template[self.metadata_blocks[idx][1]:] + new_template = "{}{}".format( + new_template[: self.metadata_blocks[idx][0]], + new_template[self.metadata_blocks[idx][1] :], ) num_of_mdb -= 1 # Remember whether the root JSON ends with "}" on a separate line - closed_on_new_line = (new_template[new_template.rfind("}") - 1] == "\n") + closed_on_new_line = new_template[new_template.rfind("}") - 1] == "\n" new_template = self._clean_empties(new_template) end_of_root_json = new_template.rfind("}") if closed_on_new_line and new_template[end_of_root_json - 1] != "\n": - new_template = u"{}{}{}".format( + new_template = "{}{}{}".format( new_template[:end_of_root_json], "\n", new_template[end_of_root_json:] ) if language_info is not None: - match = re.search(r'(\"@@locale\"\s*:\s*\")([A-Z_a-z]*)\"', new_template) + match = re.search(r"(\"@@locale\"\s*:\s*\")([A-Z_a-z]*)\"", new_template) if match: - new_template = u"{}{}{}".format( - new_template[:match.start(2)], + new_template = "{}{}{}".format( + new_template[: match.start(2)], language_info["code"], - new_template[match.end(2):] + new_template[match.end(2) :], ) return self._replace_translations(new_template, stringset, True) @@ -697,8 +713,7 @@ class StructuredJsonHandler(JsonHandler): CONTEXT_KEY = "context" DEVELOPER_COMMENT_KEY = "developer_comment" CHARACTER_LIMIT_KEY = "character_limit" - STRUCTURE_FIELDS = {CONTEXT_KEY, DEVELOPER_COMMENT_KEY, - CHARACTER_LIMIT_KEY} + STRUCTURE_FIELDS = {CONTEXT_KEY, DEVELOPER_COMMENT_KEY, CHARACTER_LIMIT_KEY} def compile(self, template, translations, **kwargs): self.translations = iter(translations) @@ -714,19 +729,18 @@ def _compile_value(self, value, template_value, value_position, skip=False): value = template_value if skip else value if value is not None: - if value == '' and template_value is None: - self.transcriber.add(u"null") + if value == "" and template_value is None: + self.transcriber.add("null") else: if template_value is None: - self.transcriber.add(f"\"{value}\"") + self.transcriber.add(f'"{value}"') else: self.transcriber.add(f"{value}") else: - self.transcriber.add(u"null") + self.transcriber.add("null") self.transcriber.skip(len(f"{template_value}")) - self.transcriber.copy_until(value_position + - len(f"{template_value}") + 1) + self.transcriber.copy_until(value_position + len(f"{template_value}") + 1) def _compile_recursively(self, current_part): if isinstance(current_part, DumbJson): @@ -736,21 +750,18 @@ def _compile_recursively(self, current_part): if current_part.type == dict: (value, _) = current_part.find_children(self.STRING_KEY)[0] if not value: - for (key, key_position, value, - value_position) in current_part: + for key, key_position, value, value_position in current_part: self.transcriber.copy_until(key_position - 1) self.transcriber.copy_until(value_position) self._compile_recursively(value) else: (value, _) = current_part.find_children(self.STRING_KEY)[0] if not value.strip(): - translation = OpenString( - "", value - ) - skip=True + translation = OpenString("", value) + skip = True else: translation = next(self.translations, None) - skip=False + skip = False context_added = False character_limit_added = False @@ -758,87 +769,94 @@ def _compile_recursively(self, current_part): line_separator = None key_value_separator = None - for (key, key_position, value, - value_position) in current_part: + for key, key_position, value, value_position in current_part: prev_position_end = self.transcriber.ptr line_separator = current_part.source[ - prev_position_end+1:key_position-1 + prev_position_end + 1 : key_position - 1 ] key_value_separator = current_part.source[ - key_position+len(key):value_position-1 + key_position + len(key) : value_position - 1 ] self.transcriber.copy_until(key_position - 1) self.transcriber.copy_until(value_position) if key == self.CONTEXT_KEY and translation: context = translation.context - self._compile_value(self.escape(context), - value, - value_position, - skip=skip) + self._compile_value( + self.escape(context), value, value_position, skip=skip + ) context_added = True elif key == self.DEVELOPER_COMMENT_KEY and translation: developer_comment = translation.developer_comment - self._compile_value(self.escape(developer_comment), - value, - value_position, - skip=skip) + self._compile_value( + self.escape(developer_comment), + value, + value_position, + skip=skip, + ) developer_comments_added = True elif key == self.CHARACTER_LIMIT_KEY and translation: character_limit = translation.character_limit - self._compile_value(character_limit, - value, - value_position, - skip=skip) + self._compile_value( + character_limit, value, value_position, skip=skip + ) character_limit_added = True elif key == self.STRING_KEY and translation: if translation.pluralized: - string_replacement = ICUCompiler().\ - serialize_strings(translation.string, - delimiter=' ') + string_replacement = ICUCompiler().serialize_strings( + translation.string, delimiter=" " + ) string_replacement = value.replace( translation.template_replacement, string_replacement, ) else: string_replacement = translation.string - self._compile_value(string_replacement, - value, - value_position) + self._compile_value( + string_replacement, value, value_position + ) elif not isinstance(value, DumbJson): self.transcriber.copy_until( value_position + len(f"{value}") + 1 ) extra_elements = [] - if (not context_added and - translation and - translation.context): - extra_elements.append(u"\"{}{}\"{}\"".format( - "context", - key_value_separator, - self.escape(translation.context), - )) - if (not character_limit_added and - translation and - translation.character_limit): - extra_elements.append(u"\"{}{}{}".format( - "character_limit", - key_value_separator, - translation.character_limit, - )) - if (not developer_comments_added and - translation and - translation.developer_comment): - extra_elements.append(u"\"{}{}\"{}\"".format( - "developer_comment", - key_value_separator, - self.escape(translation.developer_comment), - )) + if not context_added and translation and translation.context: + extra_elements.append( + '"{}{}"{}"'.format( + "context", + key_value_separator, + self.escape(translation.context), + ) + ) + if ( + not character_limit_added + and translation + and translation.character_limit + ): + extra_elements.append( + '"{}{}{}'.format( + "character_limit", + key_value_separator, + translation.character_limit, + ) + ) + if ( + not developer_comments_added + and translation + and translation.developer_comment + ): + extra_elements.append( + '"{}{}"{}"'.format( + "developer_comment", + key_value_separator, + self.escape(translation.developer_comment), + ) + ) if extra_elements: self.transcriber.add( - "," + - line_separator + - ("," + line_separator).join(extra_elements) + "," + + line_separator + + ("," + line_separator).join(extra_elements) ) def _create_openstring(self, key, payload_dict): @@ -855,14 +873,12 @@ def _create_openstring(self, key, payload_dict): # If it cannot be parsed that way (returns None), parse it like # a regular string. parser = ICUParser(allow_numeric_plural_values=False) - (string_value, _), = payload_dict.find_children(self.STRING_KEY) + ((string_value, _),) = payload_dict.find_children(self.STRING_KEY) icu_string = parser.parse(key, string_value) if icu_string: return self._create_pluralized_string(icu_string, payload_dict) - return self._create_regular_string( - key, payload_dict - ) + return self._create_regular_string(key, payload_dict) def _create_pluralized_string(self, icu_string, payload_dict): """Create a pluralized string based on the given information. @@ -875,9 +891,10 @@ def _create_pluralized_string(self, icu_string, payload_dict): :return: an OpenString object :rtype: OpenString """ - (_, string_position), = payload_dict.find_children(self.STRING_KEY) + ((_, string_position),) = payload_dict.find_children(self.STRING_KEY) payload_dict = json.loads( - payload_dict.source[payload_dict.start:payload_dict.end+1]) + payload_dict.source[payload_dict.start : payload_dict.end + 1] + ) comment_value = payload_dict.get(self.DEVELOPER_COMMENT_KEY) limit_value = payload_dict.get(self.CHARACTER_LIMIT_KEY) context_value = payload_dict.get(self.CONTEXT_KEY) @@ -887,9 +904,9 @@ def _create_pluralized_string(self, icu_string, payload_dict): icu_string.strings_by_rule, pluralized=icu_string.pluralized, order=next(self._order), - developer_comment=comment_value or '', + developer_comment=comment_value or "", character_limit=limit_value, - context=context_value or '' + context=context_value or "", ) current_pos = icu_string.current_position @@ -910,20 +927,21 @@ def _create_regular_string(self, key, payload_dict): :param value: the translation string :return: an OpenString or None """ - (string_value, string_position), = payload_dict.find_children( - self.STRING_KEY - ) + ((string_value, string_position),) = payload_dict.find_children(self.STRING_KEY) payload_dict = json.loads( - payload_dict.source[payload_dict.start:payload_dict.end+1]) + payload_dict.source[payload_dict.start : payload_dict.end + 1] + ) comment_value = payload_dict.get(self.DEVELOPER_COMMENT_KEY) limit_value = payload_dict.get(self.CHARACTER_LIMIT_KEY) context_value = payload_dict.get(self.CONTEXT_KEY) openstring = OpenString( - key, string_value, order=next(self._order), - developer_comment=comment_value or '', + key, + string_value, + order=next(self._order), + developer_comment=comment_value or "", character_limit=limit_value, - context=context_value or '' + context=context_value or "", ) self.transcriber.copy_until(string_position) self.transcriber.add(openstring.template_replacement) @@ -952,7 +970,7 @@ def _parse_key(self, key): return None # Remove the STRING_KEY part of the key as it is not needed. Add +1 # when calculating the length of the STRING_KEY, for the "." character - return key[:-(len(self.STRING_KEY)+1)] + return key[: -(len(self.STRING_KEY) + 1)] def _get_string_structure(self, key): """Given a key, find its corresponding value in a nested JSON object. @@ -980,11 +998,12 @@ def _get_string_structure(self, key): :rtype: dict """ json_dict = self._get_value_for_key(key) - return {field: json_dict[field] - if field in json_dict - else OpenString.DEFAULTS[field] - for field in self.STRUCTURE_FIELDS - } + return { + field: ( + json_dict[field] if field in json_dict else OpenString.DEFAULTS[field] + ) + for field in self.STRUCTURE_FIELDS + } def _get_value_for_key(self, key): """Given a key, find its value in a nested JSON object. @@ -1011,9 +1030,7 @@ def _get_value_for_key(self, key): """ # Go through all parts of the composite key keys = csv_reader_next( - csv.reader( - StringIO(key), delimiter='.', escapechar="\\" - ) + csv.reader(StringIO(key), delimiter=".", escapechar="\\") ) json_dict = self.json_dict for k in keys: @@ -1058,9 +1075,7 @@ def compile(self, template, stringset, **kwargs): :rtype: str """ stringset = list(stringset) - return self._replace_translations( - template, stringset, is_real_stringset=True - ) + return self._replace_translations(template, stringset, is_real_stringset=True) def _create_regular_string(self, key, value, value_position): """ @@ -1097,11 +1112,11 @@ def _get_description(self, key): :return: the description string :rtype: str """ - key_split = key.split('.') + key_split = key.split(".") try: - return self.json_dict[key_split[0]]['description'] + return self.json_dict[key_split[0]]["description"] except KeyError: - return '' + return "" def _copy_until_and_remove_section(self, pos): """ @@ -1127,34 +1142,35 @@ def validate_content(self, content): class ChromeI18nHandlerV3(Handler): - """ New version of chrome-json handler. - - Compared to v2, this handler: - - Only accepts a flat JSON structure, ie all strings must be at the top - level of the root object - - Does not escape keys; the same key that appears in the source file - will be the key of the extracted string - - Fixes a bug with parsing descriptions/comments; the previous version - would not detect the description if the key had a dot (`.`) in it - - Example source file: - - { - "a": {"message": "aaa"}, - "b": {"message": "bbb", "description": "description"}, - "c": {"message": "{plural, cnt, one {horse} other {horses}}"} - } + """New version of chrome-json handler. + + Compared to v2, this handler: + - Only accepts a flat JSON structure, ie all strings must be at the top + level of the root object + - Does not escape keys; the same key that appears in the source file + will be the key of the extracted string + - Fixes a bug with parsing descriptions/comments; the previous version + would not detect the description if the key had a dot (`.`) in it + + Example source file: + + { + "a": {"message": "aaa"}, + "b": {"message": "bbb", "description": "description"}, + "c": {"message": "{plural, cnt, one {horse} other {horses}}"} + } - Extracted strings: + Extracted strings: - |-----+-----------------------+-------------------| - | key | string(s) | developer_comment | - |-----+-----------------------+-------------------| - | a | aaa | | - | b | bbb | description | - | c | {1: horse, 5: horses} | | - |-----+-----------------------+-------------------| + |-----+-----------------------+-------------------| + | key | string(s) | developer_comment | + |-----+-----------------------+-------------------| + | a | aaa | | + | b | bbb | description | + | c | {1: horse, 5: horses} | | + |-----+-----------------------+-------------------| """ + name = "CHROME_V3" extension = "json" @@ -1180,14 +1196,15 @@ def parse(self, content, **kwargs): except ValueError as e: raise ParseError(six.text_type(e)) if parsed.type != dict: - raise ParseError(u"Source file must be a JSON object") + raise ParseError("Source file must be a JSON object") # Main loop - for (outer_key, - outer_key_position, outer_value, outer_value_position) in parsed: + for outer_key, outer_key_position, outer_value, outer_value_position in parsed: if outer_key in existing_keys: transcriber.copy_until(outer_key_position) - raise ParseError(f"Key '{outer_key}' appears multiple times (line {transcriber.line_number})") + raise ParseError( + f"Key '{outer_key}' appears multiple times (line {transcriber.line_number})" + ) existing_keys.add(outer_key) if not isinstance(outer_value, DumbJson): @@ -1196,8 +1213,9 @@ def parse(self, content, **kwargs): continue # Figure out message and description - (message, message_position), (description, _) = outer_value.\ - find_children('message', 'description') + (message, message_position), (description, _) = outer_value.find_children( + "message", "description" + ) if not isinstance(message, six.string_types): continue if not isinstance(description, six.string_types): @@ -1207,23 +1225,27 @@ def parse(self, content, **kwargs): icu_string = icu_parser.parse(outer_key, message) if icu_string: # Pluralized - openstring = OpenString(icu_string.key, - icu_string.strings_by_rule, - pluralized=icu_string.pluralized, - order=next(_order), - developer_comment=description or '') + openstring = OpenString( + icu_string.key, + icu_string.strings_by_rule, + pluralized=icu_string.pluralized, + order=next(_order), + developer_comment=description or "", + ) # Preserve ICU formatting: # '{cnt, plural, one {foo} other {foos}}' -> # '{cnt, plural, }' - transcriber.copy_until(message_position + - icu_string.current_position) + transcriber.copy_until(message_position + icu_string.current_position) transcriber.add(openstring.template_replacement) transcriber.skip(len(icu_string.string_to_replace)) else: # Singular - openstring = OpenString(outer_key, message, - order=next(_order), - developer_comment=description or '') + openstring = OpenString( + outer_key, + message, + order=next(_order), + developer_comment=description or "", + ) transcriber.copy_until(message_position) transcriber.add(openstring.template_replacement) transcriber.skip(len(message)) @@ -1242,8 +1264,7 @@ def compile(self, template, stringset, **kwargs): parsed = DumbJson(source) # Main loop - for (outer_key, - outer_key_position, outer_value, outer_value_position) in parsed: + for outer_key, outer_key_position, outer_value, outer_value_position in parsed: # Mark section start in case we want to delete this section transcriber.copy_until(outer_key_position - 1) transcriber.mark_section_start() @@ -1255,8 +1276,7 @@ def compile(self, template, stringset, **kwargs): continue # Find message - (message_hash, message_position), = outer_value.\ - find_children('message') + ((message_hash, message_position),) = outer_value.find_children("message") # Message not found, skip if not isinstance(message_hash, six.string_types): @@ -1266,8 +1286,10 @@ def compile(self, template, stringset, **kwargs): # Message hash doesn't not match next string from stringset, # delete. Section start was marked at the top of this loop - if (openstring is None or - openstring.template_replacement not in message_hash): + if ( + openstring is None + or openstring.template_replacement not in message_hash + ): try: # If this is not the last key-value pair, delete up to # (including) the next ',' @@ -1275,7 +1297,7 @@ def compile(self, template, stringset, **kwargs): # ^ ^ # | | # start end - delete_until = source.index(',', outer_value.end) + 1 + delete_until = source.index(",", outer_value.end) + 1 except ValueError: # If this is the last key-value pair, delete up to (not @@ -1284,7 +1306,7 @@ def compile(self, template, stringset, **kwargs): # ^ ^ # | | # start end - delete_until = source.index('}', outer_value.end) + delete_until = source.index("}", outer_value.end) transcriber.copy_until(delete_until + 1) transcriber.mark_section_end() transcriber.remove_section() @@ -1292,18 +1314,25 @@ def compile(self, template, stringset, **kwargs): if openstring.key != outer_key: # pragma: no cover # This should never happen - raise ParseError(u"Key '{}' from the database does not " - u"match key '{}' from the template". - format(openstring.key, outer_key)) + raise ParseError( + "Key '{}' from the database does not " + "match key '{}' from the template".format( + openstring.key, outer_key + ) + ) - if (message_hash == openstring.template_replacement and - not openstring.pluralized): + if ( + message_hash == openstring.template_replacement + and not openstring.pluralized + ): # Singular transcriber.copy_until(message_position) transcriber.add(openstring._strings[5]) transcriber.skip(len(openstring.template_replacement)) - elif (openstring.template_replacement in message_hash and - openstring.pluralized): + elif ( + openstring.template_replacement in message_hash + and openstring.pluralized + ): # Pluralized, preserve ICU formatting # '{cnt, plural, }' -> # '{cnt, plural, one {foo} other {foos}}' @@ -1311,22 +1340,23 @@ def compile(self, template, stringset, **kwargs): openstring.template_replacement ) transcriber.copy_until(message_position + replacement_position) - transcriber.add(icu_compiler.serialize_strings( - openstring.string, delimiter=' ' - )) + transcriber.add( + icu_compiler.serialize_strings(openstring.string, delimiter=" ") + ) transcriber.skip(len(openstring.template_replacement)) else: # pragma: no cover # This should never happen - raise ParseError(u"Pluralized status of the string in the " - u"template does not match the string's " - u"status from the database, key: '{}'". - format(openstring.key)) + raise ParseError( + "Pluralized status of the string in the " + "template does not match the string's " + "status from the database, key: '{}'".format(openstring.key) + ) openstring = next(stringset_iter, None) transcriber.copy_to_end() compiled = transcriber.get_destination() # Remove trailing ',', in case we deleted the last section - compiled = re.sub(r',(\s*)}(\s*)$', r'\1}\2', compiled) + compiled = re.sub(r",(\s*)}(\s*)$", r"\1}\2", compiled) return compiled