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.

Localization overview

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.

Branding process

The branding script performs the following operations on a provided upstream .grd file:

  1. Parse the upstream Chromium .grd file. Iterate over every tag in the .grd file and perform branding as follows:

    1. If the tag is a <part> tag (i.e. references a .grdp file path), replace the path in the parsed .grd with the path of the to-be-generated branded .grdp file.
    2. If the tag is a <file> tag (i.e. references an .xtb file path), replace the path in the parsed .grd with the path of the to-be-generated branded .xtb file.
    3. If the tag is a <message> tag, perform branding on the text of the resource string:
      1. Compute and store the translation ID of the unmodified resource string.
      2. Replace any instances of "Chromium", "Chrome", etc. with the Rebel-branded browser name.
        1. Note: This selectively ignores instances like "Chrome Web Store", which should not be branded.
      3. Replace any instances of "chrome://" with the Rebel-branded URL schema.
      4. Replace a hard-coded list of instances of "Google" with the Rebel-branded company name.
        1. 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.
      5. Compute and store the translation ID of the newly modified resource string.
  2. Inject new <part> tags for any provided "extra" .grdp files to be included.

    1. 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.
  3. Inject new <file> tags for any provided "extra" .xtb files to be included.

    1. Similarly, this allows creating localizations of new resource strings without modifying upstream files.
  4. Write the contents of the branded .grd file to disk. It will be written to a path under e.g. //out/Default/gen/rebel.

  5. For every <part> tag parsed above, perform the same parsing and branding steps for each <message> tag in the .grdp file.

    1. Append the computed pre- and post-branding translation IDs to the list created when parsing the .grd file.
    2. Write the branded .grdp to disk alongside the generated .grd file.
  6. For every <file> tag parsed above, perform the same parsing and branding steps for each <translation> tag in the .xtb file.

    1. The translation ID of the unbranded resource string is replaced with the translation ID of the branded resource string.
    2. Write the branded .xtb to disk alongside the generated .grd file.

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.

Example

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>
⚠️ **GitHub.com Fallback** ⚠️