tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

RemoteL10n.sys.mjs (5419B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 /**
      6 * The downloaded Fluent file is located in this sub-directory of the local
      7 * profile directory.
      8 */
      9 const USE_REMOTE_L10N_PREF =
     10  "browser.newtabpage.activity-stream.asrouter.useRemoteL10n";
     11 
     12 /**
     13 * All supported locales for remote l10n
     14 *
     15 * This is used by ASRouter.sys.mjs to check if the locale is supported before
     16 * issuing the request for remote fluent files to RemoteSettings.
     17 *
     18 * Note:
     19 *   * this is generated based on "browser/locales/all-locales" as l10n doesn't
     20 *     provide an API to fetch that list
     21 *
     22 *   * this list doesn't include "en-US", though "en-US" is well supported and
     23 *     `_RemoteL10n.isLocaleSupported()` will handle it properly
     24 */
     25 const ALL_LOCALES = new Set([
     26  "ach",
     27  "af",
     28  "an",
     29  "ar",
     30  "ast",
     31  "az",
     32  "be",
     33  "bg",
     34  "bn",
     35  "bo",
     36  "br",
     37  "brx",
     38  "bs",
     39  "ca",
     40  "ca-valencia",
     41  "cak",
     42  "ckb",
     43  "cs",
     44  "cy",
     45  "da",
     46  "de",
     47  "dsb",
     48  "el",
     49  "en-CA",
     50  "en-GB",
     51  "eo",
     52  "es-AR",
     53  "es-CL",
     54  "es-ES",
     55  "es-MX",
     56  "et",
     57  "eu",
     58  "fa",
     59  "ff",
     60  "fi",
     61  "fr",
     62  "fy-NL",
     63  "ga-IE",
     64  "gd",
     65  "gl",
     66  "gn",
     67  "gu-IN",
     68  "he",
     69  "hi-IN",
     70  "hr",
     71  "hsb",
     72  "hu",
     73  "hy-AM",
     74  "hye",
     75  "ia",
     76  "id",
     77  "is",
     78  "it",
     79  "ja",
     80  "ja-JP-mac",
     81  "ka",
     82  "kab",
     83  "kk",
     84  "km",
     85  "kn",
     86  "ko",
     87  "lij",
     88  "lo",
     89  "lt",
     90  "ltg",
     91  "lv",
     92  "meh",
     93  "mk",
     94  "mr",
     95  "ms",
     96  "my",
     97  "nb-NO",
     98  "ne-NP",
     99  "nl",
    100  "nn-NO",
    101  "oc",
    102  "pa-IN",
    103  "pl",
    104  "pt-BR",
    105  "pt-PT",
    106  "rm",
    107  "ro",
    108  "ru",
    109  "scn",
    110  "si",
    111  "sk",
    112  "sl",
    113  "son",
    114  "sq",
    115  "sr",
    116  "sv-SE",
    117  "szl",
    118  "ta",
    119  "te",
    120  "th",
    121  "tl",
    122  "tr",
    123  "trs",
    124  "uk",
    125  "ur",
    126  "uz",
    127  "vi",
    128  "wo",
    129  "xh",
    130  "zh-CN",
    131  "zh-TW",
    132 ]);
    133 
    134 export class _RemoteL10n {
    135  constructor() {
    136    this._l10n = null;
    137  }
    138 
    139  createElement(doc, elem, options = {}) {
    140    let node;
    141    if (options.content && options.content.string_id) {
    142      node = doc.createElement("remote-text");
    143    } else {
    144      node = doc.createElementNS("http://www.w3.org/1999/xhtml", elem);
    145    }
    146    if (options.classList) {
    147      node.classList.add(options.classList);
    148    }
    149    this.setString(node, options);
    150 
    151    return node;
    152  }
    153 
    154  // If `string_id` is present it means we are relying on fluent for translations.
    155  // Otherwise, we have a vanilla string.
    156  setString(el, { content, attributes = {} }) {
    157    if (content && content.string_id) {
    158      for (let [fluentId, value] of Object.entries(attributes)) {
    159        el.setAttribute(`fluent-variable-${fluentId}`, value);
    160      }
    161      el.setAttribute("fluent-remote-id", content.string_id);
    162    } else {
    163      el.textContent = content;
    164    }
    165  }
    166 
    167  get cfrFluentFileDir() {
    168    return PathUtils.join(
    169      Services.dirsvc.get("ProfLD", Ci.nsIFile).path,
    170      "settings",
    171      "main",
    172      "ms-language-packs"
    173    );
    174  }
    175 
    176  get cfrFluentFilePath() {
    177    return PathUtils.join(
    178      this.cfrFluentFileDir,
    179      "browser",
    180      "newtab",
    181      "asrouter.ftl"
    182    );
    183  }
    184 
    185  /**
    186   * Creates a new DOMLocalization instance with the Fluent file from Remote Settings.
    187   *
    188   * Note: it will use the local Fluent file in any of following cases:
    189   *   * the remote Fluent file is not available
    190   *   * it was told to use the local Fluent file
    191   */
    192  _createDOML10n() {
    193    /* istanbul ignore next */
    194    let useRemoteL10n = Services.prefs.getBoolPref(USE_REMOTE_L10N_PREF, true);
    195    if (useRemoteL10n && !L10nRegistry.getInstance().hasSource("cfr")) {
    196      const appLocale = Services.locale.appLocaleAsBCP47;
    197      let cfrIndexedFileSource = new L10nFileSource(
    198        "cfr",
    199        "app",
    200        [appLocale],
    201        `${PathUtils.toFileURI(this.cfrFluentFileDir)}/`,
    202        {
    203          addResourceOptions: {
    204            allowOverrides: true,
    205          },
    206        },
    207        [PathUtils.toFileURI(this.cfrFluentFilePath)]
    208      );
    209      L10nRegistry.getInstance().registerSources([cfrIndexedFileSource]);
    210    } else if (!useRemoteL10n && L10nRegistry.getInstance().hasSource("cfr")) {
    211      L10nRegistry.getInstance().removeSources(["cfr"]);
    212    }
    213 
    214    return new DOMLocalization(
    215      [
    216        "branding/brand.ftl",
    217        "browser/defaultBrowserNotification.ftl",
    218        "browser/newtab/asrouter.ftl",
    219        "browser/profiles.ftl",
    220        "browser/termsofuse.ftl",
    221        "toolkit/branding/brandings.ftl",
    222      ],
    223      false
    224    );
    225  }
    226 
    227  get l10n() {
    228    if (!this._l10n) {
    229      this._l10n = this._createDOML10n();
    230    }
    231    return this._l10n;
    232  }
    233 
    234  reloadL10n() {
    235    this._l10n = null;
    236  }
    237 
    238  isLocaleSupported(locale) {
    239    return locale === "en-US" || ALL_LOCALES.has(locale);
    240  }
    241 
    242  /**
    243   * Format given `localizableText`.
    244   *
    245   * Format `localizableText` if it is an object using any `string_id` field,
    246   * otherwise return `localizableText` unmodified.
    247   *
    248   * @param {object|string} `localizableText` to format.
    249   * @return {string} formatted text.
    250   */
    251  async formatLocalizableText(localizableText) {
    252    if (typeof localizableText !== "string") {
    253      // It's more useful to get an error than passing through an object without
    254      // a `string_id` field.
    255      let value = await this.l10n.formatValue(localizableText.string_id);
    256      return value;
    257    }
    258    return localizableText;
    259  }
    260 }
    261 
    262 export const RemoteL10n = new _RemoteL10n();