Pier Angelo Vendrame pushed to branch tor-browser-128.7.0esr-14.5-1 at The Tor Project / Applications / Tor Browser

Commits:

13 changed files:

Changes:

  • .gitlab/ci/jobs/update-translations.yml
    ... ... @@ -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" },
    

  • browser/branding/tb-alpha/locales/en-US/brand.ftl
    ... ... @@ -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.
    

  • browser/branding/tb-alpha/locales/en-US/brand.properties
    ... ... @@ -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

  • browser/branding/tb-nightly/locales/en-US/brand.ftl
    ... ... @@ -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.
    

  • browser/branding/tb-nightly/locales/en-US/brand.properties
    ... ... @@ -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

  • browser/branding/tb-release/locales/en-US/brand.ftl
    ... ... @@ -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.
    

  • browser/branding/tb-release/locales/en-US/brand.properties
    ... ... @@ -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

  • tools/base-browser/l10n/combine-translation-versions.py
    ... ... @@ -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
         )
    

  • tools/base-browser/l10n/combine/combine.py
    ... ... @@ -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
     
    

  • tools/base-browser/l10n/combine/tests/test_android.py
    ... ... @@ -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
    +    )

  • tools/base-browser/l10n/combine/tests/test_dtd.py
    ... ... @@ -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
    +    )

  • tools/base-browser/l10n/combine/tests/test_fluent.py
    ... ... @@ -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
    +    )

  • tools/base-browser/l10n/combine/tests/test_properties.py
    ... ... @@ -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
    +    )