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="") |