Branding GRIT Resource Strings - RebelBrowser/rebel GitHub Wiki
GRIT (Google Resource and Internationalization Tool) is Chromium's solution to define resources (strings, icons, etc.) to be displayed to the user with localization.
Resource strings are stored in .grd(p) files throughout the codebase. Further, localizations of each resource string are stored in .xtb files (one file per locale per .grd file). These strings are the source of most locations that need to be branded when creating a fork of Chromium.
This document describes Rebel's automated process for branding resource strings and their localizations. The script which governs this process is located in //rebel/branding/create_branded_grd.py.
For quick reference, the format of a resource string in a .grd(p) file is:
<message name="IDS_PRODUCT_NAME" desc="The Chrome application name">
Chromium
</message>And the format of the localization of this string in an .xtb file is:
<translation id="7337881442233988129">Chromium</translation>The important piece that matches translated strings to the resource string is the translation ID (7337881442233988129 above). This is essentially a hash of the resource string and some of the attribute values in the <message> tag. This means that any change to the resource string in the .grd file invalidates the localization, because the hash of the resource string will of course change as well. Therefore, when branding a resource string, it is important to also generate new localizations and translation IDs that match the new hash.
The branding script performs the following operations on a provided upstream .grd file:
-
Parse the upstream Chromium
.grdfile. Iterate over every tag in the.grdfile and perform branding as follows:- If the tag is a
<part>tag (i.e. references a.grdpfile path), replace the path in the parsed.grdwith the path of the to-be-generated branded.grdpfile. - If the tag is a
<file>tag (i.e. references an.xtbfile path), replace the path in the parsed.grdwith the path of the to-be-generated branded.xtbfile. - If the tag is a
<message>tag, perform branding on the text of the resource string:- Compute and store the translation ID of the unmodified resource string.
- Replace any instances of "Chromium", "Chrome", etc. with the Rebel-branded browser name.
- Note: This selectively ignores instances like "Chrome Web Store", which should not be branded.
- Replace any instances of "chrome://" with the Rebel-branded URL schema.
- Replace a hard-coded list of instances of "Google" with the Rebel-branded company name.
- Note: We cannot wholesale replace "Google" because that would cover hundreds of instances like "Google Docs", which cannot be rebranded. The list of instances here that needs to be branded is rather small, so an allowlist is maintained in the script.
- Compute and store the translation ID of the newly modified resource string.
- If the tag is a
-
Inject new
<part>tags for any provided "extra".grdpfiles to be included.- This optionally allows creating new resource strings without modifying upstream
.grd(p)files. As an example, see //rebel/chrome/app/generated_resources.grdp, which is injected into //chrome/app/generated_resources.grd.
- This optionally allows creating new resource strings without modifying upstream
-
Inject new
<file>tags for any provided "extra".xtbfiles to be included.- Similarly, this allows creating localizations of new resource strings without modifying upstream files.
-
Write the contents of the branded
.grdfile to disk. It will be written to a path under e.g.//out/Default/gen/rebel. -
For every
<part>tag parsed above, perform the same parsing and branding steps for each<message>tag in the.grdpfile.- Append the computed pre- and post-branding translation IDs to the list created when parsing the
.grdfile. - Write the branded
.grdpto disk alongside the generated.grdfile.
- Append the computed pre- and post-branding translation IDs to the list created when parsing the
-
For every
<file>tag parsed above, perform the same parsing and branding steps for each<translation>tag in the.xtbfile.- The translation ID of the unbranded resource string is replaced with the translation ID of the branded resource string.
- Write the branded
.xtbto disk alongside the generated.grdfile.
The branding script is hooked into GN such that any time an upstream .grd or .grdp file is changed, the script is rerun to generate updated branded resource strings. The list of .grd(p) files that are branded is located in //rebel/branding/BUILD.gn. Each .grd is listed in a separate GN target, and is added to the deps of the corresponding upstream target that processes the .grd file.
Consider the following example.grd, example-es.xtb, and example-fr.xtb in Chromium's source tree:
Unbranded example.grd:
<?xml version="1.0" encoding="utf-8"?>
<grit>
<outputs>
<output filename="example-es.pak" type="data_package" lang="es" />
<output filename="example-fr.pak" type="data_package" lang="fr" />
</outputs>
<translations>
<file path="resources/example-es.xtb" lang="es" />
<file path="resources/example-fr.xtb" lang="fr" />
</translations>
<messages>
<message name="IDS_WELCOME_HEADER">
Welcome to Chromium
</message>
<message name="IDS_UNINSTALL_CHROME">
Uninstall Chromium
</message>
<message name="IDS_UNINSTALL_BUTTON_TEXT">
Uninstall
</message>
</messages>
</grit>Unbranded example-es.xtb:
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="es">
<translation id="5862307444128926510">Te damos la bienvenida a Chromium</translation>
<translation id="6510925080656968729">Desinstalar Chromium</translation>
<translation id="2485422356828889247">Desinstalar</translation>
</translationbundle>Unbranded example-fr.xtb:
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="fr">
<translation id="5862307444128926510">Bienvenue dans Chromium</translation>
<translation id="6510925080656968729">Désinstaller Chromium</translation>
<translation id="2485422356828889247">Désinstaller</translation>
</translationbundle>The script would then parse each of the resource strings and their localizations. The first two resource strings need branding, the third does not. The script would perform branding substitutions those two resource strings, and compute their new translation IDs. These values are:
| Original ID | Original String | New ID | New String |
|---|---|---|---|
| 5862307444128926510 | Welcome to Chromium | 2672336032113881312 | Welcome to Rebel |
| 6510925080656968729 | Uninstall Chromium | 1095419120871208990 | Uninstall Rebel |
It would then perform the same branding substitutions on the .xtb files.
With these values, it would create new .grd and .xtb files with the following contents:
Branded example.grd:
<?xml version="1.0" encoding="UTF-8"?>
<!-- This file was created by //rebel/branding/create_branded_grd.py. -->
<grit>
<outputs>
<output filename="example-es.pak" type="data_package" lang="es" />
<output filename="example-fr.pak" type="data_package" lang="fr" />
</outputs>
<translations>
<file path="resources/example-es.xtb" lang="es" />
<file path="resources/example-fr.xtb" lang="fr" />
</translations>
<messages>
<message name="IDS_WELCOME_HEADER">
Welcome to Rebel
</message>
<message name="IDS_UNINSTALL_CHROME">
Uninstall Rebel
</message>
<message name="IDS_UNINSTALL_BUTTON_TEXT">
Uninstall
</message>
</messages>
</grit>Branded example-es.xtb:
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="es">
<translation id="2672336032113881312">Te damos la bienvenida a Rebel</translation>
<translation id="1095419120871208990">Desinstalar Rebel</translation>
<translation id="2485422356828889247">Desinstalar</translation>
</translationbundle>Branded example-fr.xtb:
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="fr">
<translation id="2672336032113881312">Bienvenue dans Rebel</translation>
<translation id="1095419120871208990">Désinstaller Rebel</translation>
<translation id="2485422356828889247">Désinstaller</translation>
</translationbundle>