Pier Angelo Vendrame pushed to branch main at The Tor Project / Applications / tor-browser-build
Commits:
-
858ee2ee
by Henry Wilkes at 2025-02-18T11:35:44+00:00
3 changed files:
Changes:
| ... | ... | @@ -136,15 +136,50 @@ branding_dir=browser/branding/[% c("var/branding_directory_prefix") %]-[% c("var |
| 136 | 136 | |
| 137 | 137 | [% IF c("var/tor-browser") -%]
|
| 138 | 138 | tar -C "$rootdir" -xf "$rootdir/[% c('input_files_by_name/translation-tor-browser') %]"
|
| 139 | + |
|
| 140 | + # For the purpose of Weblate, all releases share a single brand.ftl and
|
|
| 141 | + # brand.properties file per locale in the translations repository.
|
|
| 142 | + # See tor-browser-build#41372.
|
|
| 143 | + # In brand.ftl, both `-brand-short-name` and `-brand-full-name` should
|
|
| 144 | + # differ between releases. As such, they have additional entries in the
|
|
| 145 | + # translations repository file (Weblate):
|
|
| 146 | + # -brand-short-name for the stable release.
|
|
| 147 | + # -brand-short-name_alpha for the alpha release.
|
|
| 148 | + # -brand-short-name_nightly for the nightly release.
|
|
| 149 | + # And similarly for -brand-full-name.
|
|
| 150 | + # For the final build, we only want to keep the string that matches the
|
|
| 151 | + # built release, and remove its suffix if it has one. So for the stable
|
|
| 152 | + # release we want to keep -brand-short-name. For the alpha release we want
|
|
| 153 | + # to keep -brand-short-name_alpha instead, and rename it to be
|
|
| 154 | + # -brand-short-name.
|
|
| 155 | + #
|
|
| 156 | + # As such, we parse the brand.ftl file to rename these strings to keep the
|
|
| 157 | + # version we want using rename-branding-strings.py.
|
|
| 158 | + #
|
|
| 159 | + # We do a similar thing with brandShortName and brandFullName in
|
|
| 160 | + # brand.properties.
|
|
| 161 | + |
|
| 162 | + # Instructions for the script to perform the renames.
|
|
| 163 | + brand_ftl_renames='{
|
|
| 164 | + "suffix": "[% c("var/branding_string_suffix") %]",
|
|
| 165 | + "ids": ["-brand-short-name", "-brand-full-name"]
|
|
| 166 | + }'
|
|
| 167 | + brand_properties_renames='{
|
|
| 168 | + "suffix": "[% c("var/branding_string_suffix") %]",
|
|
| 169 | + "ids": ["brandShortName", "brandFullName"]
|
|
| 170 | + }'
|
|
| 171 | + |
|
| 139 | 172 | pushd "$rootdir/translation-tor-browser"
|
| 140 | 173 | ln -s ja ja-JP-mac
|
| 141 | 174 | for lang in $supported_locales; do
|
| 142 | 175 | mv $lang/tor-browser.ftl "$l10ncentral/$lang/toolkit/toolkit/global/"
|
| 143 | - # Branding. Currently all releases use the same branding.
|
|
| 176 | + # Branding.
|
|
| 144 | 177 | l10n_branding_dir="$l10ncentral/$lang/$branding_dir/"
|
| 145 | 178 | mkdir -p "$l10n_branding_dir"
|
| 146 | - mv $lang/branding/brand.ftl "$l10n_branding_dir"
|
|
| 147 | - mv $lang/brand.properties "$l10n_branding_dir"
|
|
| 179 | + # Convert the translations repository branding files into files that work
|
|
| 180 | + # for this specific build.
|
|
| 181 | + python3 rename-branding-strings.py $lang/branding/brand.ftl "$brand_ftl_renames" > "$l10n_branding_dir/brand.ftl"
|
|
| 182 | + python3 rename-branding-strings.py $lang/brand.properties "$brand_properties_renames" > "$l10n_branding_dir/brand.properties"
|
|
| 148 | 183 | done
|
| 149 | 184 | popd
|
| 150 | 185 |
| ... | ... | @@ -54,6 +54,10 @@ var: |
| 54 | 54 | rm -Rf "$rezip_tmpdir"
|
| 55 | 55 | |
| 56 | 56 | l10n-changesets: '[% exec("git --no-pager show " _ c("git_hash") _ ":browser/locales/l10n-changesets.json", { exec_noco => 1 }) %]'
|
| 57 | + # The branding_string_suffix for the alpha and nightly should be
|
|
| 58 | + # '_alpha' and '_nightly', matching the "suffix" chosen in the
|
|
| 59 | + # tor-browser:update-translations.yml file.
|
|
| 60 | + branding_string_suffix: '_[% c("var/channel") %]'
|
|
| 57 | 61 | |
| 58 | 62 | steps:
|
| 59 | 63 | src-tarballs:
|
| ... | ... | @@ -94,6 +98,12 @@ targets: |
| 94 | 98 | var:
|
| 95 | 99 | nightly_updates_publish_dir_prefix: basebrowser-
|
| 96 | 100 | |
| 101 | + release:
|
|
| 102 | + var:
|
|
| 103 | + # For the stable release, the suffix is empty.
|
|
| 104 | + # I.e. we want to select `-brand-short-name` directly.
|
|
| 105 | + branding_string_suffix: ''
|
|
| 106 | + |
|
| 97 | 107 | nightly:
|
| 98 | 108 | git_hash: '[% c("var/project-name") %]-[% c("var/firefox_version") %]-[% c("var/browser_branch") %]'
|
| 99 | 109 | tag_gpg_id: 0
|
| ... | ... | @@ -183,6 +193,8 @@ input_files: |
| 183 | 193 | - project: binutils
|
| 184 | 194 | name: binutils
|
| 185 | 195 | enable: '[% c("var/linux") && ! c("var/linux-cross") %]'
|
| 196 | + - filename: rename-branding-strings.py
|
|
| 197 | + enable: '[% c("var/has_l10n") && c("var/tor-browser") %]'
|
|
| 186 | 198 | - filename: fix-info-plist.py
|
| 187 | 199 | enable: '[% c("var/macos") %]'
|
| 188 | 200 | - filename: nsis-uninstall.patch
|
| 1 | +import argparse
|
|
| 2 | +import json
|
|
| 3 | +import re
|
|
| 4 | + |
|
| 5 | +arg_parser = argparse.ArgumentParser(
|
|
| 6 | + description="Filter a branding file to only include the expected strings"
|
|
| 7 | +)
|
|
| 8 | +arg_parser.add_argument("file", metavar="<file>", help="branding file to process")
|
|
| 9 | +arg_parser.add_argument(
|
|
| 10 | + "details", metavar="<details>", help="JSON specification for renaming"
|
|
| 11 | +)
|
|
| 12 | + |
|
| 13 | +args = arg_parser.parse_args()
|
|
| 14 | +details_dict = json.loads(args.details)
|
|
| 15 | +# The suffix we want to search for or remove.
|
|
| 16 | +# Can be empty if we want to select the IDs that have no suffix.
|
|
| 17 | +suffix = details_dict["suffix"]
|
|
| 18 | +# The string IDs we want to rename.
|
|
| 19 | +rename_ids = details_dict["ids"]
|
|
| 20 | + |
|
| 21 | + |
|
| 22 | +def parse_ids(string, pattern):
|
|
| 23 | + """
|
|
| 24 | + Extract the IDs found in a string.
|
|
| 25 | + |
|
| 26 | + :param string: The string to parse.
|
|
| 27 | + :param pattern: The pattern to capture IDs.
|
|
| 28 | + |
|
| 29 | + :yields: A tuple containing a chunk of string and whether the chunk
|
|
| 30 | + is an ID.
|
|
| 31 | + """
|
|
| 32 | + regex = re.compile(pattern, flags=re.MULTILINE + re.ASCII)
|
|
| 33 | + while True:
|
|
| 34 | + match = regex.search(string)
|
|
| 35 | + if not match:
|
|
| 36 | + yield string, False
|
|
| 37 | + return
|
|
| 38 | + |
|
| 39 | + yield string[: match.start("id")], False
|
|
| 40 | + yield match.group("id"), True
|
|
| 41 | + string = string[match.end("id") :]
|
|
| 42 | + |
|
| 43 | + |
|
| 44 | +# We want to parse the file and rename the IDs we are interested in.
|
|
| 45 | +# If the ID starts with one of the `rename_ids` but the suffix does
|
|
| 46 | +# not match we append an "_UNUSED" suffix to the ID, to keep it in the output
|
|
| 47 | +# but functionally unused in the final build.
|
|
| 48 | +# Otherwise, if the ID starts with one of the `rename_ids` and the suffix
|
|
| 49 | +# matches we will remove the suffix from the ID, so that it is used in the
|
|
| 50 | +# final build.
|
|
| 51 | +# Everything else found in the file, like entry values, comments and blank
|
|
| 52 | +# lines, will be included in the output as it was.
|
|
| 53 | +#
|
|
| 54 | +# NOTE: This script is constructed to be *independent* of the order in which
|
|
| 55 | +# strings are present in the file. Weblate does not guarantee the order of
|
|
| 56 | +# translated files to use the same ordering as the original en-US file.
|
|
| 57 | +#
|
|
| 58 | +# NOTE: This script should work for all locales. In particular, for Fluent files
|
|
| 59 | +# it needs to be able to handle Fluent Terms that are multi-valued (conditional)
|
|
| 60 | +# and Terms with attributes. Therefore, whilst we could have written a script to
|
|
| 61 | +# *remove* unwanted strings, the parsing logic would have been far more complex
|
|
| 62 | +# to be able to handle all these cases. Hence why we only parse for the Fluent
|
|
| 63 | +# IDs and rename them, which is much simpler.
|
|
| 64 | +with open(args.file, "r") as file:
|
|
| 65 | + if file.name.endswith(".ftl"):
|
|
| 66 | + # A Fluent ID is the identifier for a Message or Term, which always
|
|
| 67 | + # starts on a newline, and will be followed by an "=" character.
|
|
| 68 | + id_pattern = r"^(?P<id>-?[a-zA-Z][a-zA-Z0-9_-]*) *="
|
|
| 69 | + elif file.name.endswith(".properties"):
|
|
| 70 | + # A properties ID can be preceded by whitespace, and can be any
|
|
| 71 | + # character other than whitespace, ":" or "=". The first character also
|
|
| 72 | + # cannot be "!" or "#" since this starts a comment. Technically the
|
|
| 73 | + # Java ".properties" spec allows a ID to include one of these characters
|
|
| 74 | + # if it is escaped by a "\", but we don't expect or care about such IDs.
|
|
| 75 | + # The Java spec also has a limited set of whitespace, which excludes
|
|
| 76 | + # "\v", but we do not expect Weblate or any other serialiser to
|
|
| 77 | + # insert whitespace beyond "\n", "\r", "\t" or " ".
|
|
| 78 | + id_pattern = r"^\s*(?P<id>[^!#:=\s][^:=\s]*)"
|
|
| 79 | + else:
|
|
| 80 | + raise ValueError(f"Unknown file type {file.name}")
|
|
| 81 | + |
|
| 82 | + for part, is_id in parse_ids(file.read(), id_pattern):
|
|
| 83 | + if is_id:
|
|
| 84 | + for string_id in rename_ids:
|
|
| 85 | + if part.startswith(string_id):
|
|
| 86 | + if part == string_id + suffix:
|
|
| 87 | + # This string matches the suffix, so we want to use its
|
|
| 88 | + # value. We adjust the ID to remove the suffix before
|
|
| 89 | + # printing.
|
|
| 90 | + part = string_id
|
|
| 91 | + else:
|
|
| 92 | + # Keep this entry in the output, but make it unused by
|
|
| 93 | + # appending to the ID.
|
|
| 94 | + part += "_UNUSED"
|
|
| 95 | + break
|
|
| 96 | + print(part, end="") |