Pier Angelo Vendrame pushed to branch tor-browser-128.7.0esr-14.5-1 at The Tor Project / Applications / Tor Browser
Commits:
-
d9fe34de
by Henry Wilkes at 2025-02-18T10:08:15+00:00
-
34838694
by Henry Wilkes at 2025-02-18T10:10:32+00:00
-
6fa642ea
by Henry Wilkes at 2025-02-18T10:10:33+00:00
13 changed files:
- .gitlab/ci/jobs/update-translations.yml
- browser/branding/tb-alpha/locales/en-US/brand.ftl
- browser/branding/tb-alpha/locales/en-US/brand.properties
- browser/branding/tb-nightly/locales/en-US/brand.ftl
- browser/branding/tb-nightly/locales/en-US/brand.properties
- browser/branding/tb-release/locales/en-US/brand.ftl
- browser/branding/tb-release/locales/en-US/brand.properties
- tools/base-browser/l10n/combine-translation-versions.py
- tools/base-browser/l10n/combine/combine.py
- tools/base-browser/l10n/combine/tests/test_android.py
- tools/base-browser/l10n/combine/tests/test_dtd.py
- tools/base-browser/l10n/combine/tests/test_fluent.py
- tools/base-browser/l10n/combine/tests/test_properties.py
Changes:
| ... | ... | @@ -17,12 +17,48 @@ |
| 17 | 17 | {
|
| 18 | 18 | "name": "brand.ftl",
|
| 19 | 19 | "where": ["browser/branding/tb-release", "toolkit/torbutton"],
|
| 20 | + "branding": {
|
|
| 21 | + "versions": [
|
|
| 22 | + {
|
|
| 23 | + "name": "Alpha",
|
|
| 24 | + "suffix": "_alpha",
|
|
| 25 | + "where": ["browser/branding/tb-alpha"]
|
|
| 26 | + },
|
|
| 27 | + {
|
|
| 28 | + "name": "Nightly",
|
|
| 29 | + "suffix": "_nightly",
|
|
| 30 | + "where": ["browser/branding/tb-nightly"]
|
|
| 31 | + }
|
|
| 32 | + ],
|
|
| 33 | + "ids": [
|
|
| 34 | + "-brand-short-name",
|
|
| 35 | + "-brand-full-name"
|
|
| 36 | + ]
|
|
| 37 | + },
|
|
| 20 | 38 | "branch": "tor-browser",
|
| 21 | 39 | "directory": "branding"
|
| 22 | 40 | },
|
| 23 | 41 | {
|
| 24 | 42 | "name": "brand.properties",
|
| 25 | 43 | "where": ["browser/branding/tb-release", "toolkit/torbutton"],
|
| 44 | + "branding": {
|
|
| 45 | + "versions": [
|
|
| 46 | + {
|
|
| 47 | + "name": "Alpha",
|
|
| 48 | + "suffix": "_alpha",
|
|
| 49 | + "where": ["browser/branding/tb-alpha"]
|
|
| 50 | + },
|
|
| 51 | + {
|
|
| 52 | + "name": "Nightly",
|
|
| 53 | + "suffix": "_nightly",
|
|
| 54 | + "where": ["browser/branding/tb-nightly"]
|
|
| 55 | + }
|
|
| 56 | + ],
|
|
| 57 | + "ids": [
|
|
| 58 | + "brandShortName",
|
|
| 59 | + "brandFullName"
|
|
| 60 | + ]
|
|
| 61 | + },
|
|
| 26 | 62 | "branch": "tor-browser"
|
| 27 | 63 | },
|
| 28 | 64 | { "name": "tor-browser.ftl", "branch": "tor-browser" },
|
| ... | ... | @@ -2,12 +2,16 @@ |
| 2 | 2 | # that is used by Firefox) to avoid picking up the -brand-short-name values
|
| 3 | 3 | # that Mozilla includes in the Firefox language packs.
|
| 4 | 4 | |
| 5 | +# The shortened application name for Tor Browser. Shared between versions.
|
|
| 5 | 6 | -brand-shorter-name = Tor Browser
|
| 6 | --brand-short-name = Tor Browser
|
|
| 7 | --brand-full-name = Tor Browser
|
|
| 7 | +# The default application name for the "alpha" release.
|
|
| 8 | +-brand-short-name = Tor Browser Alpha
|
|
| 9 | +# The full application name for the "alpha" release.
|
|
| 10 | +-brand-full-name = Tor Browser Alpha
|
|
| 8 | 11 | # This brand name can be used in messages where the product name needs to
|
| 9 | 12 | # remain unchanged across different versions (Nightly, Beta, etc.).
|
| 10 | 13 | -brand-product-name = Tor Browser
|
| 14 | +# The name of the Tor Project organisation.
|
|
| 11 | 15 | -vendor-short-name = Tor Project
|
| 12 | 16 | # "Tor" is a trademark names, so should not be translated (not including the quote marks, which can be localized).
|
| 13 | 17 | # "The Tor Project, Inc." is an organisation name.
|
| ... | ... | @@ -3,6 +3,9 @@ |
| 3 | 3 | # License, v. 2.0. If a copy of the MPL was not distributed with this
|
| 4 | 4 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
| 5 | 5 | |
| 6 | +# The shortened application name for Tor Browser. Shared between versions.
|
|
| 6 | 7 | brandShorterName=Tor Browser
|
| 7 | -brandShortName=Tor Browser
|
|
| 8 | -brandFullName=Tor Browser |
|
| 8 | +# The default application name for the "alpha" release.
|
|
| 9 | +brandShortName=Tor Browser Alpha
|
|
| 10 | +# The full application name for the "alpha" release.
|
|
| 11 | +brandFullName=Tor Browser Alpha |
| ... | ... | @@ -2,12 +2,16 @@ |
| 2 | 2 | # that is used by Firefox) to avoid picking up the -brand-short-name values
|
| 3 | 3 | # that Mozilla includes in the Firefox language packs.
|
| 4 | 4 | |
| 5 | +# The shortened application name for Tor Browser. Shared between versions.
|
|
| 5 | 6 | -brand-shorter-name = Tor Browser
|
| 6 | --brand-short-name = Tor Browser
|
|
| 7 | --brand-full-name = Tor Browser
|
|
| 7 | +# The default application name for the "nightly" release.
|
|
| 8 | +-brand-short-name = Tor Browser Nightly
|
|
| 9 | +# The full application name for the "nightly" release.
|
|
| 10 | +-brand-full-name = Tor Browser Nightly
|
|
| 8 | 11 | # This brand name can be used in messages where the product name needs to
|
| 9 | 12 | # remain unchanged across different versions (Nightly, Beta, etc.).
|
| 10 | 13 | -brand-product-name = Tor Browser
|
| 14 | +# The name of the Tor Project organisation.
|
|
| 11 | 15 | -vendor-short-name = Tor Project
|
| 12 | 16 | # "Tor" is a trademark names, so should not be translated (not including the quote marks, which can be localized).
|
| 13 | 17 | # "The Tor Project, Inc." is an organisation name.
|
| ... | ... | @@ -3,6 +3,9 @@ |
| 3 | 3 | # License, v. 2.0. If a copy of the MPL was not distributed with this
|
| 4 | 4 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
| 5 | 5 | |
| 6 | +# The shortened application name for Tor Browser. Shared between versions.
|
|
| 6 | 7 | brandShorterName=Tor Browser
|
| 7 | -brandShortName=Tor Browser
|
|
| 8 | -brandFullName=Tor Browser |
|
| 8 | +# The default application name for the "nightly" release.
|
|
| 9 | +brandShortName=Tor Browser Nightly
|
|
| 10 | +# The full application name for the "nightly" release.
|
|
| 11 | +brandFullName=Tor Browser Nightly |
| ... | ... | @@ -2,12 +2,16 @@ |
| 2 | 2 | # that is used by Firefox) to avoid picking up the -brand-short-name values
|
| 3 | 3 | # that Mozilla includes in the Firefox language packs.
|
| 4 | 4 | |
| 5 | +# The shortened application name for Tor Browser. Shared between versions.
|
|
| 5 | 6 | -brand-shorter-name = Tor Browser
|
| 7 | +# The default application name for the stable release.
|
|
| 6 | 8 | -brand-short-name = Tor Browser
|
| 9 | +# The full application name for the stable release.
|
|
| 7 | 10 | -brand-full-name = Tor Browser
|
| 8 | 11 | # This brand name can be used in messages where the product name needs to
|
| 9 | 12 | # remain unchanged across different versions (Nightly, Beta, etc.).
|
| 10 | 13 | -brand-product-name = Tor Browser
|
| 14 | +# The name of the Tor Project organisation.
|
|
| 11 | 15 | -vendor-short-name = Tor Project
|
| 12 | 16 | # "Tor" is a trademark names, so should not be translated (not including the quote marks, which can be localized).
|
| 13 | 17 | # "The Tor Project, Inc." is an organisation name.
|
| ... | ... | @@ -3,6 +3,9 @@ |
| 3 | 3 | # License, v. 2.0. If a copy of the MPL was not distributed with this
|
| 4 | 4 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
| 5 | 5 | |
| 6 | +# The shortened application name for Tor Browser. Shared between versions.
|
|
| 6 | 7 | brandShorterName=Tor Browser
|
| 8 | +# The default application name for the stable release.
|
|
| 7 | 9 | brandShortName=Tor Browser
|
| 10 | +# The full application name for the stable release.
|
|
| 8 | 11 | brandFullName=Tor Browser |
| ... | ... | @@ -306,9 +306,34 @@ for file_dict in json.loads(args.files): |
| 306 | 306 | f"{current_file.path} : {stable_file.path}"
|
| 307 | 307 | )
|
| 308 | 308 | |
| 309 | + content = None if current_file is None else current_file.content
|
|
| 310 | + |
|
| 311 | + # If we have a branding file, we want to also include strings from the other
|
|
| 312 | + # branding directories that differ from the stable release.
|
|
| 313 | + # The strings that *differ* per release should be specified in
|
|
| 314 | + # file_dict["branding"]["ids"]. These strings will be copied from the other
|
|
| 315 | + # release's branding directory, with an addition suffix added to their ID,
|
|
| 316 | + # as specified in the version_dict["suffix"].
|
|
| 317 | + branding = file_dict.get("branding", None)
|
|
| 318 | + if branding:
|
|
| 319 | + include_ids = branding["ids"]
|
|
| 320 | + for version_dict in branding["versions"]:
|
|
| 321 | + branding_dirs = version_dict.get("where", None)
|
|
| 322 | + branding_file = current_branch.get_file(name, branding_dirs)
|
|
| 323 | + if branding_file is None:
|
|
| 324 | + raise Exception(f"{name} does not exist in {branding_dirs}")
|
|
| 325 | + content = combine_files(
|
|
| 326 | + name,
|
|
| 327 | + content,
|
|
| 328 | + branding_file.content,
|
|
| 329 | + f'{version_dict["name"]} Release.',
|
|
| 330 | + include_ids,
|
|
| 331 | + version_dict["suffix"],
|
|
| 332 | + )
|
|
| 333 | + |
|
| 309 | 334 | content = combine_files(
|
| 310 | 335 | name,
|
| 311 | - None if current_file is None else current_file.content,
|
|
| 336 | + content,
|
|
| 312 | 337 | None if stable_file is None else stable_file.content,
|
| 313 | 338 | f"Will be unused in {current_branch.browser_version_name}!",
|
| 314 | 339 | )
|
| ... | ... | @@ -14,26 +14,32 @@ if TYPE_CHECKING: |
| 14 | 14 | |
| 15 | 15 | def combine_files(
|
| 16 | 16 | filename: str,
|
| 17 | - new_content: str | None,
|
|
| 18 | - old_content: str | None,
|
|
| 17 | + primary_content: str | None,
|
|
| 18 | + alternative_content: str | None,
|
|
| 19 | 19 | comment_prefix: str,
|
| 20 | + include_ids: list[str] | None = None,
|
|
| 21 | + alternative_suffix: str = "",
|
|
| 20 | 22 | ) -> str | None:
|
| 21 | 23 | """Combine two translation files into one to include all strings from both.
|
| 22 | - The new content is presented first, and any strings only found in the old
|
|
| 23 | - content are placed at the end with an additional comment.
|
|
| 24 | + The primary content is presented first, followed by the alternative content
|
|
| 25 | + at the end with an additional comment.
|
|
| 24 | 26 | |
| 25 | 27 | :param filename: The filename for the file, determines the format.
|
| 26 | - :param new_content: The new content for the file, or None if it has been
|
|
| 27 | - deleted.
|
|
| 28 | - :param old_content: The old content for the file, or None if it did not
|
|
| 29 | - exist before.
|
|
| 30 | - :comment_prefix: A comment to include for any strings that are only found in
|
|
| 31 | - the old content. This will be placed before any other comments for the
|
|
| 32 | - string.
|
|
| 28 | + :param primary_content: The primary content for the file, or None if it does
|
|
| 29 | + not exist.
|
|
| 30 | + :param alternative_content: The alternative content for the file, or None if
|
|
| 31 | + it does not exist.
|
|
| 32 | + :param comment_prefix: A comment to include for any strings that are
|
|
| 33 | + appended to the content. This will be placed before any other comments for
|
|
| 34 | + the string.
|
|
| 35 | + :param include_ids: String IDs from `alternative_content` we want to
|
|
| 36 | + include. If this is `None` then we include all strings that do not already
|
|
| 37 | + have a matching ID in `primary_content`.
|
|
| 38 | + :param duplicate_suffix: The suffix to apply to the alternative IDs.
|
|
| 33 | 39 | |
| 34 | 40 | :returns: The combined content, or None if both given contents are None.
|
| 35 | 41 | """
|
| 36 | - if new_content is None and old_content is None:
|
|
| 42 | + if primary_content is None and alternative_content is None:
|
|
| 37 | 43 | return None
|
| 38 | 44 | |
| 39 | 45 | # getParser from compare_locale returns the same instance for the same file
|
| ... | ... | @@ -41,7 +47,7 @@ def combine_files( |
| 41 | 47 | parser = getParser(filename)
|
| 42 | 48 | |
| 43 | 49 | is_android = filename.endswith(".xml")
|
| 44 | - if new_content is None:
|
|
| 50 | + if primary_content is None:
|
|
| 45 | 51 | if is_android:
|
| 46 | 52 | # File was deleted, add some document parts.
|
| 47 | 53 | content_start = (
|
| ... | ... | @@ -54,7 +60,7 @@ def combine_files( |
| 54 | 60 | content_end = ""
|
| 55 | 61 | existing_keys = []
|
| 56 | 62 | else:
|
| 57 | - parser.readUnicode(new_content)
|
|
| 63 | + parser.readUnicode(primary_content)
|
|
| 58 | 64 | |
| 59 | 65 | # Start with the same content as the current file.
|
| 60 | 66 | # For android strings, we want to keep the final "</resources>" until after.
|
| ... | ... | @@ -96,8 +102,8 @@ def combine_files( |
| 96 | 102 | |
| 97 | 103 | entry_iter: Iterable[Any] = ()
|
| 98 | 104 | # If the file does not exist in the old branch, don't make any additions.
|
| 99 | - if old_content is not None:
|
|
| 100 | - parser.readUnicode(old_content)
|
|
| 105 | + if alternative_content is not None:
|
|
| 106 | + parser.readUnicode(alternative_content)
|
|
| 101 | 107 | entry_iter = parser.walk(only_localizable=False)
|
| 102 | 108 | for entry in entry_iter:
|
| 103 | 109 | if isinstance(entry, Junk):
|
| ... | ... | @@ -134,13 +140,19 @@ def combine_files( |
| 134 | 140 | if not isinstance(entry, Entity):
|
| 135 | 141 | raise ValueError(f"Unexpected type: {entry.__class__.__name__}")
|
| 136 | 142 | |
| 137 | - if entry.key in existing_keys:
|
|
| 138 | - # Already included this string in the new translation file.
|
|
| 143 | + if include_ids is None:
|
|
| 144 | + # We include the entry if it is not already included.
|
|
| 145 | + include_entry = entry.key not in existing_keys
|
|
| 146 | + else:
|
|
| 147 | + # We include the entry if it is in our list.
|
|
| 148 | + include_entry = entry.key in include_ids
|
|
| 149 | + if not include_entry:
|
|
| 139 | 150 | # Drop the gathered comments for this Entity.
|
| 140 | 151 | stacked_comments.clear()
|
| 141 | 152 | continue
|
| 142 | 153 | |
| 143 | 154 | if isinstance(entry, FluentEntity):
|
| 155 | + id_regex = rf"^({re.escape(entry.key)})( *=)"
|
|
| 144 | 156 | if fluent_group_comment is not None:
|
| 145 | 157 | # We have a found GroupComment which has not been included yet.
|
| 146 | 158 | # All following Entity's will be under its scope, until the next
|
| ... | ... | @@ -149,12 +161,15 @@ def combine_files( |
| 149 | 161 | # Added GroupComment, so don't need to add again.
|
| 150 | 162 | fluent_group_comment = None
|
| 151 | 163 | elif isinstance(entry, DTDEntity):
|
| 164 | + id_regex = rf"^(\s*<!ENTITY\s*{re.escape(entry.key)})(\s)"
|
|
| 152 | 165 | # Include our additional comment before we print the rest for this
|
| 153 | 166 | # Entity.
|
| 154 | 167 | additions.append(f"<!-- LOCALIZATION NOTE: {comment_prefix} -->")
|
| 155 | 168 | elif isinstance(entry, PropertiesEntity):
|
| 169 | + id_regex = rf"^({re.escape(entry.key)})( *=)"
|
|
| 156 | 170 | additions.append(f"# {comment_prefix}")
|
| 157 | 171 | elif isinstance(entry, AndroidEntity):
|
| 172 | + id_regex = rf'^(\s*<string\s[^>]*name="{re.escape(entry.key)})(")'
|
|
| 158 | 173 | additions.append(f"<!-- {comment_prefix} -->")
|
| 159 | 174 | else:
|
| 160 | 175 | raise ValueError(f"Unexpected Entity type: {entry.__class__.__name__}")
|
| ... | ... | @@ -162,7 +177,17 @@ def combine_files( |
| 162 | 177 | # Add any other comment lines that came directly before this Entity.
|
| 163 | 178 | additions.extend(stacked_comments)
|
| 164 | 179 | stacked_comments.clear()
|
| 165 | - additions.append(entry.all)
|
|
| 180 | + entry_content = entry.all
|
|
| 181 | + if alternative_suffix:
|
|
| 182 | + # NOTE: compare_locales does not allow us to set the entry.key
|
|
| 183 | + # value. Instead we use a regular expression to append the suffix to
|
|
| 184 | + # the expected key.
|
|
| 185 | + entry_content, count = re.subn(
|
|
| 186 | + id_regex, rf"\1{alternative_suffix}\2", entry_content, flags=re.M
|
|
| 187 | + )
|
|
| 188 | + if count != 1:
|
|
| 189 | + raise ValueError(f"Failed to substitute the ID for {entry.key}")
|
|
| 190 | + additions.append(entry_content)
|
|
| 166 | 191 | |
| 167 | 192 | content_middle = ""
|
| 168 | 193 |
| ... | ... | @@ -24,6 +24,20 @@ def assert_result(new_content, old_content, expect): |
| 24 | 24 | )
|
| 25 | 25 | |
| 26 | 26 | |
| 27 | +def assert_alternative(content, alternative_content, alternative_ids, expect):
|
|
| 28 | + content = wrap_in_xml(content)
|
|
| 29 | + alternative_content = wrap_in_xml(alternative_content)
|
|
| 30 | + expect = wrap_in_xml(expect)
|
|
| 31 | + assert expect == combine_files(
|
|
| 32 | + "test_strings.xml",
|
|
| 33 | + content,
|
|
| 34 | + alternative_content,
|
|
| 35 | + "ALTERNATIVE STRING",
|
|
| 36 | + alternative_ids,
|
|
| 37 | + "_alt",
|
|
| 38 | + )
|
|
| 39 | + |
|
| 40 | + |
|
| 27 | 41 | def test_combine_empty():
|
| 28 | 42 | assert_result(None, None, None)
|
| 29 | 43 | |
| ... | ... | @@ -328,3 +342,74 @@ def test_removed_string_with_comment(): |
| 328 | 342 | <string name="removed_4">Fourth removed</string>
|
| 329 | 343 | """,
|
| 330 | 344 | )
|
| 345 | + |
|
| 346 | + |
|
| 347 | +def test_alternatives():
|
|
| 348 | + assert_alternative(
|
|
| 349 | + """\
|
|
| 350 | + <string name="string_1">First string</string>
|
|
| 351 | + """,
|
|
| 352 | + """\
|
|
| 353 | + <string name="string_1">Alternative string</string>
|
|
| 354 | + """,
|
|
| 355 | + ["string_1"],
|
|
| 356 | + """\
|
|
| 357 | + <string name="string_1">First string</string>
|
|
| 358 | + |
|
| 359 | + <!-- ALTERNATIVE STRING -->
|
|
| 360 | + <string name="string_1_alt">Alternative string</string>
|
|
| 361 | + """,
|
|
| 362 | + )
|
|
| 363 | + assert_alternative(
|
|
| 364 | + """\
|
|
| 365 | + <!-- Comment 1 -->
|
|
| 366 | + <string name="string_1">First string</string>
|
|
| 367 | + <!-- Comment 2 -->
|
|
| 368 | + <string name="string_2">Second string</string>
|
|
| 369 | + <string name="string_3">Third string</string>
|
|
| 370 | + """,
|
|
| 371 | + """\
|
|
| 372 | + <string name="string_1">First string</string>
|
|
| 373 | + <!-- Alt comment -->
|
|
| 374 | + <string name="string_2">Alternative string</string>
|
|
| 375 | + <string name="string_3">Third string different</string>
|
|
| 376 | + <string name="string_4">Other string</string>
|
|
| 377 | + """,
|
|
| 378 | + ["string_2"],
|
|
| 379 | + """\
|
|
| 380 | + <!-- Comment 1 -->
|
|
| 381 | + <string name="string_1">First string</string>
|
|
| 382 | + <!-- Comment 2 -->
|
|
| 383 | + <string name="string_2">Second string</string>
|
|
| 384 | + <string name="string_3">Third string</string>
|
|
| 385 | + |
|
| 386 | + <!-- ALTERNATIVE STRING -->
|
|
| 387 | + <!-- Alt comment -->
|
|
| 388 | + <string name="string_2_alt">Alternative string</string>
|
|
| 389 | + """,
|
|
| 390 | + )
|
|
| 391 | + assert_alternative(
|
|
| 392 | + """\
|
|
| 393 | + <string name="string_1">First string</string>
|
|
| 394 | + <string name="string_2">Second string</string>
|
|
| 395 | + <string name="string_3">Third string</string>
|
|
| 396 | + """,
|
|
| 397 | + """\
|
|
| 398 | + <string name="string_1">Alternative string</string>
|
|
| 399 | + <string name="string_3">Third string</string>
|
|
| 400 | + <!-- comment -->
|
|
| 401 | + <string name="string_4">Other string</string>
|
|
| 402 | + """,
|
|
| 403 | + ["string_1", "string_4"],
|
|
| 404 | + """\
|
|
| 405 | + <string name="string_1">First string</string>
|
|
| 406 | + <string name="string_2">Second string</string>
|
|
| 407 | + <string name="string_3">Third string</string>
|
|
| 408 | + |
|
| 409 | + <!-- ALTERNATIVE STRING -->
|
|
| 410 | + <string name="string_1_alt">Alternative string</string>
|
|
| 411 | + <!-- ALTERNATIVE STRING -->
|
|
| 412 | + <!-- comment -->
|
|
| 413 | + <string name="string_4_alt">Other string</string>
|
|
| 414 | + """,
|
|
| 415 | + ) |
| ... | ... | @@ -16,6 +16,23 @@ def assert_result(new_content, old_content, expect): |
| 16 | 16 | )
|
| 17 | 17 | |
| 18 | 18 | |
| 19 | +def assert_alternative(content, alternative_content, alternative_ids, expect):
|
|
| 20 | + if content is not None:
|
|
| 21 | + content = textwrap.dedent(content)
|
|
| 22 | + if alternative_content is not None:
|
|
| 23 | + alternative_content = textwrap.dedent(alternative_content)
|
|
| 24 | + if expect is not None:
|
|
| 25 | + expect = textwrap.dedent(expect)
|
|
| 26 | + assert expect == combine_files(
|
|
| 27 | + "test.dtd",
|
|
| 28 | + content,
|
|
| 29 | + alternative_content,
|
|
| 30 | + "ALTERNATIVE STRING",
|
|
| 31 | + alternative_ids,
|
|
| 32 | + ".alt",
|
|
| 33 | + )
|
|
| 34 | + |
|
| 35 | + |
|
| 19 | 36 | def test_combine_empty():
|
| 20 | 37 | assert_result(None, None, None)
|
| 21 | 38 | |
| ... | ... | @@ -323,3 +340,74 @@ def test_removed_string_with_comment(): |
| 323 | 340 | <!ENTITY removed.4 "Fourth removed">
|
| 324 | 341 | """,
|
| 325 | 342 | )
|
| 343 | + |
|
| 344 | + |
|
| 345 | +def test_alternatives():
|
|
| 346 | + assert_alternative(
|
|
| 347 | + """\
|
|
| 348 | + <!ENTITY string.1 "First string">
|
|
| 349 | + """,
|
|
| 350 | + """\
|
|
| 351 | + <!ENTITY string.1 "Alternative string">
|
|
| 352 | + """,
|
|
| 353 | + ["string.1"],
|
|
| 354 | + """\
|
|
| 355 | + <!ENTITY string.1 "First string">
|
|
| 356 | + |
|
| 357 | + <!-- LOCALIZATION NOTE: ALTERNATIVE STRING -->
|
|
| 358 | + <!ENTITY string.1.alt "Alternative string">
|
|
| 359 | + """,
|
|
| 360 | + )
|
|
| 361 | + assert_alternative(
|
|
| 362 | + """\
|
|
| 363 | + <!-- LOCALIZATION NOTE: Comment 1 -->
|
|
| 364 | + <!ENTITY string.1 "First string">
|
|
| 365 | + <!-- LOCALIZATION NOTE: Comment 2 -->
|
|
| 366 | + <!ENTITY string.2 "Second string">
|
|
| 367 | + <!ENTITY string.3 "Third string">
|
|
| 368 | + """,
|
|
| 369 | + """\
|
|
| 370 | + <!ENTITY string.1 "First string">
|
|
| 371 | + <!-- LOCALIZATION NOTE: Alt comment -->
|
|
| 372 | + <!ENTITY string.2 "Alternative string">
|
|
| 373 | + <!ENTITY string.3 "Third string different">
|
|
| 374 | + <!ENTITY string.4 "Other string">
|
|
| 375 | + """,
|
|
| 376 | + ["string.2"],
|
|
| 377 | + """\
|
|
| 378 | + <!-- LOCALIZATION NOTE: Comment 1 -->
|
|
| 379 | + <!ENTITY string.1 "First string">
|
|
| 380 | + <!-- LOCALIZATION NOTE: Comment 2 -->
|
|
| 381 | + <!ENTITY string.2 "Second string">
|
|
| 382 | + <!ENTITY string.3 "Third string">
|
|
| 383 | + |
|
| 384 | + <!-- LOCALIZATION NOTE: ALTERNATIVE STRING -->
|
|
| 385 | + <!-- LOCALIZATION NOTE: Alt comment -->
|
|
| 386 | + <!ENTITY string.2.alt "Alternative string">
|
|
| 387 | + """,
|
|
| 388 | + )
|
|
| 389 | + assert_alternative(
|
|
| 390 | + """\
|
|
| 391 | + <!ENTITY string.1 "First string">
|
|
| 392 | + <!ENTITY string.2 "Second string">
|
|
| 393 | + <!ENTITY string.3 "Third string">
|
|
| 394 | + """,
|
|
| 395 | + """\
|
|
| 396 | + <!ENTITY string.1 "Alternative string">
|
|
| 397 | + <!ENTITY string.3 "Third string">
|
|
| 398 | + <!-- LOCALIZATION NOTE: comment -->
|
|
| 399 | + <!ENTITY string.4 "Other string">
|
|
| 400 | + """,
|
|
| 401 | + ["string.1", "string.4"],
|
|
| 402 | + """\
|
|
| 403 | + <!ENTITY string.1 "First string">
|
|
| 404 | + <!ENTITY string.2 "Second string">
|
|
| 405 | + <!ENTITY string.3 "Third string">
|
|
| 406 | + |
|
| 407 | + <!-- LOCALIZATION NOTE: ALTERNATIVE STRING -->
|
|
| 408 | + <!ENTITY string.1.alt "Alternative string">
|
|
| 409 | + <!-- LOCALIZATION NOTE: ALTERNATIVE STRING -->
|
|
| 410 | + <!-- LOCALIZATION NOTE: comment -->
|
|
| 411 | + <!ENTITY string.4.alt "Other string">
|
|
| 412 | + """,
|
|
| 413 | + ) |
| ... | ... | @@ -16,6 +16,23 @@ def assert_result(new_content, old_content, expect): |
| 16 | 16 | )
|
| 17 | 17 | |
| 18 | 18 | |
| 19 | +def assert_alternative(content, alternative_content, alternative_ids, expect):
|
|
| 20 | + if content is not None:
|
|
| 21 | + content = textwrap.dedent(content)
|
|
| 22 | + if alternative_content is not None:
|
|
| 23 | + alternative_content = textwrap.dedent(alternative_content)
|
|
| 24 | + if expect is not None:
|
|
| 25 | + expect = textwrap.dedent(expect)
|
|
| 26 | + assert expect == combine_files(
|
|
| 27 | + "test.ftl",
|
|
| 28 | + content,
|
|
| 29 | + alternative_content,
|
|
| 30 | + "ALTERNATIVE STRING",
|
|
| 31 | + alternative_ids,
|
|
| 32 | + "-alt",
|
|
| 33 | + )
|
|
| 34 | + |
|
| 35 | + |
|
| 19 | 36 | def test_combine_empty():
|
| 20 | 37 | assert_result(None, None, None)
|
| 21 | 38 | |
| ... | ... | @@ -342,3 +359,119 @@ def test_removed_string_with_comment(): |
| 342 | 359 | removed-4 = Fourth removed
|
| 343 | 360 | """,
|
| 344 | 361 | )
|
| 362 | + |
|
| 363 | + |
|
| 364 | +def test_alternatives():
|
|
| 365 | + assert_alternative(
|
|
| 366 | + """\
|
|
| 367 | + string-1 = First string
|
|
| 368 | + .title = hello
|
|
| 369 | + """,
|
|
| 370 | + """\
|
|
| 371 | + string-1 = Alternative string
|
|
| 372 | + .title = different
|
|
| 373 | + """,
|
|
| 374 | + ["string-1"],
|
|
| 375 | + """\
|
|
| 376 | + string-1 = First string
|
|
| 377 | + .title = hello
|
|
| 378 | + |
|
| 379 | + |
|
| 380 | + ## ALTERNATIVE STRING
|
|
| 381 | + |
|
| 382 | + string-1-alt = Alternative string
|
|
| 383 | + .title = different
|
|
| 384 | + """,
|
|
| 385 | + )
|
|
| 386 | + assert_alternative(
|
|
| 387 | + """\
|
|
| 388 | + string-1 = First string
|
|
| 389 | + .title = hello
|
|
| 390 | + """,
|
|
| 391 | + """\
|
|
| 392 | + string-1 = Alternative string
|
|
| 393 | + """,
|
|
| 394 | + ["string-1"],
|
|
| 395 | + """\
|
|
| 396 | + string-1 = First string
|
|
| 397 | + .title = hello
|
|
| 398 | + |
|
| 399 | + |
|
| 400 | + ## ALTERNATIVE STRING
|
|
| 401 | + |
|
| 402 | + string-1-alt = Alternative string
|
|
| 403 | + """,
|
|
| 404 | + )
|
|
| 405 | + assert_alternative(
|
|
| 406 | + """\
|
|
| 407 | + -term-1 = First string
|
|
| 408 | + """,
|
|
| 409 | + """\
|
|
| 410 | + -term-1 = Alternative string
|
|
| 411 | + """,
|
|
| 412 | + ["-term-1"],
|
|
| 413 | + """\
|
|
| 414 | + -term-1 = First string
|
|
| 415 | + |
|
| 416 | + |
|
| 417 | + ## ALTERNATIVE STRING
|
|
| 418 | + |
|
| 419 | + -term-1-alt = Alternative string
|
|
| 420 | + """,
|
|
| 421 | + )
|
|
| 422 | + assert_alternative(
|
|
| 423 | + """\
|
|
| 424 | + # Comment 1
|
|
| 425 | + string-1 = First string
|
|
| 426 | + # Comment 2
|
|
| 427 | + string-2 = Second string
|
|
| 428 | + string-3 = Third string
|
|
| 429 | + """,
|
|
| 430 | + """\
|
|
| 431 | + string-1 = First string
|
|
| 432 | + # Alt comment
|
|
| 433 | + string-2 = Alternative string
|
|
| 434 | + string-3 = Third string different
|
|
| 435 | + string-4 = Other string
|
|
| 436 | + """,
|
|
| 437 | + ["string-2"],
|
|
| 438 | + """\
|
|
| 439 | + # Comment 1
|
|
| 440 | + string-1 = First string
|
|
| 441 | + # Comment 2
|
|
| 442 | + string-2 = Second string
|
|
| 443 | + string-3 = Third string
|
|
| 444 | + |
|
| 445 | + |
|
| 446 | + ## ALTERNATIVE STRING
|
|
| 447 | + |
|
| 448 | + # Alt comment
|
|
| 449 | + string-2-alt = Alternative string
|
|
| 450 | + """,
|
|
| 451 | + )
|
|
| 452 | + assert_alternative(
|
|
| 453 | + """\
|
|
| 454 | + string-1 = First string
|
|
| 455 | + string-2 = Second string
|
|
| 456 | + string-3 = Third string
|
|
| 457 | + """,
|
|
| 458 | + """\
|
|
| 459 | + string-1 = Alternative string
|
|
| 460 | + string-3 = Third string
|
|
| 461 | + # comment
|
|
| 462 | + -string-4 = Other string
|
|
| 463 | + """,
|
|
| 464 | + ["string-1", "-string-4"],
|
|
| 465 | + """\
|
|
| 466 | + string-1 = First string
|
|
| 467 | + string-2 = Second string
|
|
| 468 | + string-3 = Third string
|
|
| 469 | + |
|
| 470 | + |
|
| 471 | + ## ALTERNATIVE STRING
|
|
| 472 | + |
|
| 473 | + string-1-alt = Alternative string
|
|
| 474 | + # comment
|
|
| 475 | + -string-4-alt = Other string
|
|
| 476 | + """,
|
|
| 477 | + ) |
| ... | ... | @@ -16,6 +16,23 @@ def assert_result(new_content, old_content, expect): |
| 16 | 16 | )
|
| 17 | 17 | |
| 18 | 18 | |
| 19 | +def assert_alternative(content, alternative_content, alternative_ids, expect):
|
|
| 20 | + if content is not None:
|
|
| 21 | + content = textwrap.dedent(content)
|
|
| 22 | + if alternative_content is not None:
|
|
| 23 | + alternative_content = textwrap.dedent(alternative_content)
|
|
| 24 | + if expect is not None:
|
|
| 25 | + expect = textwrap.dedent(expect)
|
|
| 26 | + assert expect == combine_files(
|
|
| 27 | + "test.properties",
|
|
| 28 | + content,
|
|
| 29 | + alternative_content,
|
|
| 30 | + "ALTERNATIVE STRING",
|
|
| 31 | + alternative_ids,
|
|
| 32 | + ".alt",
|
|
| 33 | + )
|
|
| 34 | + |
|
| 35 | + |
|
| 19 | 36 | def test_combine_empty():
|
| 20 | 37 | assert_result(None, None, None)
|
| 21 | 38 | |
| ... | ... | @@ -320,3 +337,74 @@ def test_removed_string_with_comment(): |
| 320 | 337 | removed.4 = Fourth removed
|
| 321 | 338 | """,
|
| 322 | 339 | )
|
| 340 | + |
|
| 341 | + |
|
| 342 | +def test_alternatives():
|
|
| 343 | + assert_alternative(
|
|
| 344 | + """\
|
|
| 345 | + string.1 = First string
|
|
| 346 | + """,
|
|
| 347 | + """\
|
|
| 348 | + string.1 = Alternative string
|
|
| 349 | + """,
|
|
| 350 | + ["string.1"],
|
|
| 351 | + """\
|
|
| 352 | + string.1 = First string
|
|
| 353 | + |
|
| 354 | + # ALTERNATIVE STRING
|
|
| 355 | + string.1.alt = Alternative string
|
|
| 356 | + """,
|
|
| 357 | + )
|
|
| 358 | + assert_alternative(
|
|
| 359 | + """\
|
|
| 360 | + # Comment 1
|
|
| 361 | + string.1 = First string
|
|
| 362 | + # Comment 2
|
|
| 363 | + string.2 = Second string
|
|
| 364 | + string.3 = Third string
|
|
| 365 | + """,
|
|
| 366 | + """\
|
|
| 367 | + string.1 = First string
|
|
| 368 | + # Alt comment
|
|
| 369 | + string.2 = Alternative string
|
|
| 370 | + string.3 = Third string different
|
|
| 371 | + string.4 = Other string
|
|
| 372 | + """,
|
|
| 373 | + ["string.2"],
|
|
| 374 | + """\
|
|
| 375 | + # Comment 1
|
|
| 376 | + string.1 = First string
|
|
| 377 | + # Comment 2
|
|
| 378 | + string.2 = Second string
|
|
| 379 | + string.3 = Third string
|
|
| 380 | + |
|
| 381 | + # ALTERNATIVE STRING
|
|
| 382 | + # Alt comment
|
|
| 383 | + string.2.alt = Alternative string
|
|
| 384 | + """,
|
|
| 385 | + )
|
|
| 386 | + assert_alternative(
|
|
| 387 | + """\
|
|
| 388 | + string.1 = First string
|
|
| 389 | + string.2 = Second string
|
|
| 390 | + string.3 = Third string
|
|
| 391 | + """,
|
|
| 392 | + """\
|
|
| 393 | + string.1 = Alternative string
|
|
| 394 | + string.3 = Third string
|
|
| 395 | + # comment
|
|
| 396 | + string.4 = Other string
|
|
| 397 | + """,
|
|
| 398 | + ["string.1", "string.4"],
|
|
| 399 | + """\
|
|
| 400 | + string.1 = First string
|
|
| 401 | + string.2 = Second string
|
|
| 402 | + string.3 = Third string
|
|
| 403 | + |
|
| 404 | + # ALTERNATIVE STRING
|
|
| 405 | + string.1.alt = Alternative string
|
|
| 406 | + # ALTERNATIVE STRING
|
|
| 407 | + # comment
|
|
| 408 | + string.4.alt = Other string
|
|
| 409 | + """,
|
|
| 410 | + ) |