tor-browser

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

tutorial.rst (29637B)


      1 .. role:: html(code)
      2   :language: html
      3 
      4 .. role:: js(code)
      5   :language: JavaScript
      6 
      7 =============================
      8 Fluent for Firefox Developers
      9 =============================
     10 
     11 
     12 This tutorial is intended for Firefox engineers already familiar with the previous
     13 localization systems offered by Gecko - DTD and StringBundle - and assumes
     14 prior experience with those systems.
     15 
     16 For a more hands-on tutorial of understanding Fluent from the ground up, try
     17 following the `Fluent DOMLocalization Tutorial`__, which provides some background on
     18 how Fluent works and walks you through creating a basic web project from scratch that
     19 uses Fluent for localization.
     20 
     21 __ https://projectfluent.org/dom-l10n-documentation/
     22 
     23 Using Fluent in Gecko
     24 =====================
     25 
     26 `Fluent`_ is a modern localization system introduced into
     27 the Gecko platform with a focus on quality, performance, maintenance and completeness.
     28 
     29 The legacy DTD system is deprecated, and Fluent should be used where possible.
     30 
     31 Getting a Review
     32 ----------------
     33 
     34 If you work on any patch that touches FTL files, you'll need to get a review
     35 from `fluent-reviewers`__. There's a Herald hook that automatically sets
     36 that group as a blocking reviewer.
     37 
     38 __ https://phabricator.services.mozilla.com/tag/fluent-reviewers/
     39 
     40 Guidelines for the review process are available `here`__.
     41 
     42 __ ./fluent_review.html
     43 
     44 To lighten the burden on reviewers, please take a moment to review some
     45 best practices before submitting your patch for review.
     46 
     47 -  `ProjectFluent Good Practices for Developers`_
     48 -  `Mozilla Localization Best Practices For Developers`_
     49 
     50 .. _ProjectFluent Good Practices for Developers: https://github.com/projectfluent/fluent/wiki/Good-Practices-for-Developers
     51 .. _Mozilla Localization Best Practices For Developers: https://mozilla-l10n.github.io/documentation/localization/dev_best_practices.html
     52 
     53 Major Benefits
     54 ==============
     55 
     56 Fluent `ties tightly`__ into the domain of internationalization
     57 through `Unicode`_, `CLDR`_ and `ICU`_.
     58 
     59 __ https://github.com/projectfluent/fluent/wiki/Fluent-and-Standards
     60 
     61 More specifically, the most observable benefits for each group of consumers are
     62 
     63 
     64 Developers
     65 ----------
     66 
     67 - Support for XUL, XHTML, HTML, Web Components, React, JS, Python and Rust
     68 - Strings are available in a single, unified localization context available for both DOM and runtime code
     69 - Full internationalization (i18n) support: date and time formatting, number formatting, plurals, genders etc.
     70 - Strong focus on `declarative API via DOM attributes`__
     71 - Extensible with custom formatters, Mozilla-specific APIs etc.
     72 - `Separation of concerns`__: localization details, and the added complexity of some languages, don't leak onto the source code and are no concern for developers
     73 - Compound messages link a single translation unit to a single UI element
     74 - `DOM Overlays`__ allow for localization of DOM fragments
     75 - Simplified build system model
     76 - No need for pre-processing instructions
     77 - Support for pseudolocalization
     78 
     79 __ https://github.com/projectfluent/fluent/wiki/Get-Started
     80 __ https://github.com/projectfluent/fluent/wiki/Design-Principles
     81 __ https://github.com/projectfluent/fluent.js/wiki/DOM-Overlays
     82 
     83 
     84 Product Quality
     85 ------------------
     86 
     87 - A robust, multilevel, `error fallback system`__ prevents XML errors and runtime errors
     88 - Simplified l10n API reduces the amount of l10n specific code and resulting bugs
     89 - Runtime localization allows for dynamic language changes and updates over-the-air
     90 - DOM Overlays increase localization security
     91 
     92 __ https://github.com/projectfluent/fluent/wiki/Error-Handling
     93 
     94 
     95 Fluent Translation List - FTL
     96 =============================
     97 
     98 Fluent introduces a file format designed specifically for easy readability
     99 and the localization features offered by the system.
    100 
    101 At first glance the format is a simple key-value store. It may look like this:
    102 
    103 .. code-block:: fluent
    104 
    105  home-page-header = Home Page
    106 
    107  # The label of a button opening a new tab
    108  new-tab-open = Open New Tab
    109 
    110 But the FTL file format is significantly more powerful and the additional features
    111 quickly add up. In order to familiarize yourself with the basic features,
    112 consider reading through the `Fluent Syntax Guide`_ to understand
    113 a more complex example like:
    114 
    115 .. code-block:: fluent
    116 
    117  ### These messages correspond to security and privacy user interface.
    118  ###
    119  ### Please choose simple and non-threatening language when localizing
    120  ### to help user feel in control when interacting with the UI.
    121 
    122  ## General Section
    123 
    124  -brand-short-name = Firefox
    125      .gender = masculine
    126 
    127  pref-pane =
    128      .title =
    129          { PLATFORM() ->
    130              [windows] Options
    131             *[other] Preferences
    132          }
    133      .accesskey = C
    134 
    135  # Variables:
    136  #   $tabCount (Number) - number of container tabs to be closed
    137  containers-disable-alert-ok-button =
    138      { $tabCount ->
    139          [one] Close { $tabCount } Container Tab
    140         *[other] Close { $tabCount } Container Tabs
    141      }
    142 
    143  update-application-info =
    144      You are using { -brand-short-name } Version: { $version }.
    145      Please read the <a>privacy policy</a>.
    146 
    147 The above, of course, is a particular selection of complex strings intended to exemplify
    148 the new features and concepts introduced by Fluent.
    149 
    150 .. important::
    151 
    152  While in Fluent it’s possible to use both lowercase and uppercase characters in message
    153  identifiers, the naming convention in Gecko is to use lowercase and hyphens, avoiding
    154  CamelCase and underscores. For example, `allow-button` should be preferred to
    155  `allow_button` or `allowButton`, unless there are technically constraints – like
    156  identifiers generated at run-time from external sources – that make this impractical.
    157 
    158 In order to ensure the quality of the output, a lot of checks and tooling
    159 is part of the build system.
    160 `Pontoon`_, the main localization tool used to translate Firefox, also supports
    161 Fluent and its features to help localizers in their work.
    162 
    163 
    164 .. _fluent-tutorial-social-contract:
    165 
    166 Social Contract
    167 ===============
    168 
    169 Fluent uses the concept of a `social contract` between developer and localizers.
    170 This contract is established by the selection of a unique identifier, called :js:`l10n-id`,
    171 which carries a promise of being used in a particular place to carry a particular meaning.
    172 
    173 The use of unique identifiers is shared with legacy localization systems in
    174 Firefox.
    175 
    176 .. important::
    177 
    178  An important part of the contract is that the developer commits to treat the
    179  localization output as `opaque`. That means that no concatenations, replacements
    180  or splitting should happen after the translation is completed to generate the
    181  desired output.
    182 
    183 In return, localizers enter the social contract by promising to provide an accurate
    184 and clean translation of the messages that match the request.
    185 
    186 In Fluent, the developer is not to be bothered with inner logic and complexity that the
    187 localization will use to construct the response. Whether `declensions`__ or other
    188 variant selection techniques are used is up to a localizer and their particular translation.
    189 From the developer perspective, Fluent returns a final string to be presented to
    190 the user, with no l10n logic required in the running code.
    191 
    192 __ https://en.wikipedia.org/wiki/Declension
    193 
    194 
    195 Markup Localization
    196 ===================
    197 
    198 To localize an element in Fluent, the developer adds a new message to
    199 an FTL file and then has to associate an :js:`l10n-id` with the element
    200 by defining a :js:`data-l10n-id` attribute:
    201 
    202 .. code-block:: html
    203 
    204  <h1 data-l10n-id="home-page-header" />
    205 
    206  <button data-l10n-id="pref-pane" />
    207 
    208 Fluent will take care of the rest, populating the element with the message value
    209 in its content and all localizable attributes if defined.
    210 
    211 The developer provides only a single message to localize the whole element,
    212 including the value and selected attributes.
    213 
    214 The value can be a whole fragment of DOM:
    215 
    216 .. code-block:: html
    217 
    218  <p data-l10n-id="update-application-info" data-l10n-args='{"version": "60.0"}'>
    219    <a data-l10n-name="privacy-url" href="http://www.mozilla.org/privacy" />
    220  </p>
    221 
    222 .. code-block:: fluent
    223 
    224  -brand-short-name = Firefox
    225  update-application-info =
    226      You are using { -brand-short-name } Version: { $version }.
    227      Please read the <a data-l10n-name="privacy-url">privacy policy</a>.
    228 
    229 
    230 Fluent will overlay the translation onto the source fragment preserving attributes like
    231 :code:`class` and :code:`href` from the source and adding translations for the elements
    232 inside. The resulting localized content will look like this:
    233 
    234 .. code-block:: html
    235 
    236  <p data-l10n-id="update-application-info" data-l10n-args='{"version": "60.0"}'">
    237    You are using Firefox Version: 60.0.
    238    Please read the <a href="http://www.mozilla.org/privacy">privacy policy</a>.
    239  </p>
    240 
    241 
    242 This operation is sanitized, and Fluent takes care of selecting which elements and
    243 attributes can be safely provided by the localization.
    244 The list of allowed elements and attributes is `maintained by the W3C`__, and if
    245 the developer needs to allow for localization of additional attributes, they can
    246 allow them using :code:`data-l10n-attrs` list:
    247 
    248 .. code-block:: html
    249 
    250  <label data-l10n-id="search-input" data-l10n-attrs="style" />
    251 
    252 The above example adds an attribute :code:`style` to be allowed on this
    253 particular :code:`label` element.
    254 
    255 
    256 External Arguments
    257 ------------------
    258 
    259 Notice in the previous example the attribute :code:`data-l10n-args`, which is
    260 a JSON object storing variables exposed by the developer to the localizer.
    261 
    262 This is the main channel for the developer to provide additional variables
    263 to be used in the localization.
    264 
    265 It's worth noting that, when the :code:`l10n-args` are set in
    266 the runtime code, they are in fact encoded as JSON and stored together with
    267 :code:`l10n-id` as an attribute of the element.
    268 
    269 __ https://www.w3.org/TR/2011/WD-html5-20110525/text-level-semantics.html
    270 
    271 
    272 Runtime Localization
    273 ====================
    274 
    275 In almost every case the JS runtime code will operate on a particular document, either
    276 XUL, XHTML or HTML.
    277 
    278 If the document has its markup already localized, then Fluent exposes a new
    279 attribute on the :js:`document` element - :js:`document.l10n`.
    280 
    281 This property is an object of type :js:`DOMLocalization` which maintains the main
    282 localization context for this document and exposes it to runtime code as well.
    283 
    284 With a focus on `declarative localization`__, the primary method of localization is
    285 to alter the localization attributes in the DOM. Fluent provides a method to facilitate this:
    286 
    287 .. code-block:: JavaScript
    288 
    289  document.l10n.setAttributes(element, "new-panel-header");
    290 
    291 This will set the :code:`data-l10n-id` on the element and translate it before the next
    292 animation frame.
    293 
    294 This API can be used to set both the ID and the arguments at the same time.
    295 
    296 .. code-block:: JavaScript
    297 
    298  document.l10n.setAttributes(element, "containers-disable-alert-ok-button", {
    299    tabCount: 5
    300  });
    301 
    302 If only the arguments need to be updated, then it's possible to use the :code:`setArgs`
    303 method.
    304 
    305 .. code-block:: JavaScript
    306 
    307  document.l10n.setArgs(element, {
    308    tabCount: 5
    309  });
    310 
    311 On debug builds if the Fluent arguments are not provided, then Firefox will crash. This
    312 is done so that these errors are caught in CI. On rare occasions it may be necessary
    313 to work around this crash by providing a blank string as an argument value.
    314 
    315 __ https://github.com/projectfluent/fluent/wiki/Good-Practices-for-Developers
    316 
    317 
    318 Non-Markup Localization
    319 -----------------------
    320 
    321 In rare cases, when the runtime code needs to retrieve the translation and not
    322 apply it onto the DOM, Fluent provides an API to retrieve it:
    323 
    324 .. code-block:: JavaScript
    325 
    326  let [ msg ] = await document.l10n.formatValues([
    327    {id: "remove-containers-description"}
    328  ]);
    329 
    330  alert(msg);
    331 
    332 This model is heavily discouraged and should be used only in cases where the
    333 DOM annotation is not possible.
    334 
    335 .. note::
    336 
    337  This API is available as asynchronous. In case of Firefox,
    338  the only non-DOM localizable calls are used where the output goes to
    339  a third-party like Bluetooth, Notifications etc.
    340  All those cases should already be asynchronous. If you can't avoid synchronous
    341  access, you can use ``mozILocalization.formatMessagesSync`` with synchronous IO.
    342 
    343 
    344 Internationalization
    345 ====================
    346 
    347 The majority of internationalization issues are implicitly handled by Fluent without
    348 any additional requirement. Full Unicode support, `bidirectionality`__, and
    349 correct number formatting work without any action required from either
    350 developer or localizer.
    351 
    352 __ https://github.com/projectfluent/fluent/wiki/BiDi-in-Fluent
    353 
    354 .. code-block:: JavaScript
    355 
    356  document.l10n.setAttributes(element, "welcome-message", {
    357    userName: "اليسع",
    358    count: 5
    359  });
    360 
    361 A message like this localized to American English will correctly wrap the user
    362 name in directionality marks, allowing the layout engine to determine how to
    363 display the bidirectional text.
    364 
    365 On the other hand, the same message localized to Arabic will use the Eastern Arabic
    366 numeral for number "5".
    367 
    368 
    369 Plural Rules
    370 ------------
    371 
    372 The most common localization feature is the ability to provide different variants
    373 of the same string depending on plural categories. Fluent ties into the Unicode CLDR
    374 standard called `Plural Rules <http://cldr.unicode.org/index/cldr-spec/plural-rules>`__.
    375 
    376 In order to allow localizers to use it, all the developer has to do is to pass
    377 an external argument number:
    378 
    379 .. code-block:: JavaScript
    380 
    381  document.l10n.setAttributes(element, "unread-warning", { unreadCount: 5 });
    382 
    383 Localizers can use the argument to build a multi variant message if their
    384 language requires that:
    385 
    386 .. code-block:: fluent
    387 
    388  unread-warning =
    389      { $unreadCount ->
    390          [one] You have { $unreadCount } unread message
    391         *[other] You have { $unreadCount } unread messages
    392      }
    393 
    394 If the variant selection is performed based on a number, Fluent matches that
    395 number against literal numbers as well as its `plural category`__.
    396 
    397 If the given translation doesn't need pluralization for the string (for example
    398 Japanese often will not), the localizer can replace it with:
    399 
    400 .. code-block:: fluent
    401 
    402  unread-warning = You have { $unreadCount } unread messages
    403 
    404 and the message will preserve the social contract.
    405 
    406 One additional feature is that the localizer can further improve the message by
    407 specifying variants for particular values:
    408 
    409 .. code-block:: fluent
    410 
    411  unread-warning =
    412      { $unreadCount ->
    413          [0] You have no unread messages
    414          [1] You have one unread message
    415         *[other] You have { $unreadCount } unread messages
    416      }
    417 
    418 The advantage here is that per-locale choices don't leak onto the source code
    419 and the developer is not affected.
    420 
    421 
    422 .. note::
    423 
    424  There is an important distinction between a variant keyed on plural category
    425  `one` and digit `1`. Although in English the two are synonymous, in other
    426  languages category `one` may be used for other numbers.
    427  For example in `Bosnian`__, category `one` is used for numbers like `1`, `21`, `31`
    428  and so on, and also for fractional numbers like `0.1`.
    429 
    430 __ https://unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html
    431 __ https://unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html#bs
    432 
    433 Partially-formatted variables
    434 -----------------------------
    435 
    436 When it comes to formatting data, Fluent allows the developer to provide
    437 a set of parameters for the formatter, and the localizer can fine tune some of them.
    438 This technique is called `partially-formatted variables`__.
    439 
    440 For example, when formatting a date, the developer can just pass a JS :js:`Date` object,
    441 but its default formatting will be pretty expressive. In most cases, the developer
    442 may want to use some of the :js:`Intl.DateTimeFormat` options to select the default
    443 representation of the date in string:
    444 
    445 .. code-block:: JavaScript
    446 
    447  document.l10n.formatValue("welcome-message", {
    448  startDate: FluentDateTime(new Date(), {
    449      year: "numeric",
    450      month: "long",
    451      day: "numeric"
    452    })
    453  });
    454 
    455 .. code-block:: fluent
    456 
    457  welcome-message = Your session will start date: { $startDate }
    458 
    459 In most cases, that will be enough and the date would get formatted in the current
    460 Firefox as `February 28, 2018`.
    461 
    462 But if in some other locale the string would get too long, the localizer can fine
    463 tune the options as well:
    464 
    465 .. code-block:: fluent
    466 
    467  welcome-message = Początek Twojej sesji: { DATETIME($startDate, month: "short") }
    468 
    469 This will adjust the length of the month token in the message to short and get formatted
    470 in Polish as `28 lut 2018`.
    471 
    472 At the moment Fluent supports two formatters that match JS Intl API counterparts:
    473 
    474 * **NUMBER**: `Intl.NumberFormat`__
    475 * **DATETIME**: `Intl.DateTimeFormat`__
    476 
    477 With time more formatters will be added. Also, this feature is not exposed
    478 to ``setAttributes`` at this point, as that serializes to JSON.
    479 
    480 __ https://projectfluent.org/fluent/guide/functions.html#partially-formatted-variables
    481 __ https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat
    482 __ https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat
    483 
    484 Registering New L10n Files
    485 ==========================
    486 
    487 Fluent uses a wildcard statement, packaging all localization resources into
    488 their component's `/localization/` directory.
    489 
    490 That means that, if a new file is added to a component of Firefox already
    491 covered by Fluent like `browser`, it's enough to add the new file to the
    492 repository in a path like `browser/locales/en-US/browser/component/file.ftl`, and
    493 the toolchain will package it into `browser/localization/browser/component/file.ftl`.
    494 
    495 At runtime Firefox uses a special registry for all localization data. It will
    496 register the browser's `/localization/` directory and make all files inside it
    497 available to be referenced.
    498 
    499 To make the document localized using Fluent, all the developer has to do is add
    500 localizable resources for Fluent API to use:
    501 
    502 .. code-block:: html
    503 
    504  <link rel="localization" href="branding/brand.ftl"/>
    505  <link rel="localization" href="browser/preferences/preferences.ftl"/>
    506 
    507 The URI provided to the :html:`<link/>` element are relative paths within the localization
    508 system.
    509 
    510 
    511 Custom Localizations
    512 ====================
    513 
    514 The above method creates a single localization context per document.
    515 In almost all scenarios that's sufficient.
    516 
    517 In rare edge cases where the developer needs to fetch additional resources, or
    518 the same resources in another language, it is possible to create additional
    519 Localization object manually using the `Localization` class:
    520 
    521 .. code-block:: JavaScript
    522 
    523  const myL10n = new Localization([
    524    "branding/brand.ftl",
    525    "browser/preferences/preferences.ftl"
    526  ]);
    527 
    528 
    529  let [isDefaultMsg, isNotDefaultMsg] =
    530    await myL10n.formatValues({id: "is-default"}, {id: "is-not-default"});
    531 
    532 
    533 .. admonition:: Example
    534 
    535  An example of a use case is the Preferences UI in Firefox, which uses the
    536  main context to localize the UI but also to build a search index.
    537 
    538  It is common to build such search index both in a current language and additionally
    539  in English, since a lot of documentation and online help exist only in English.
    540 
    541  A developer may create manually a new context with the same resources as the main one,
    542  but hardcode it to `en-US` and then build the search index using both contexts.
    543 
    544 
    545 By default, all `Localization` contexts are asynchronous. It is possible to create a synchronous
    546 one by passing an `sync = false` argument to the constructor, or calling the `SetIsSync(bool)` method
    547 on the class.
    548 
    549 
    550 .. code-block:: JavaScript
    551 
    552  const myL10n = new Localization([
    553    "branding/brand.ftl",
    554    "browser/preferences/preferences.ftl"
    555  ], false);
    556 
    557 
    558  let [isDefaultMsg, isNotDefaultMsg] =
    559    myL10n.formatValuesSync({id: "is-default"}, {id: "is-not-default"});
    560 
    561 
    562 Synchronous contexts should be always avoided as they require synchronous I/O. If you think your use case
    563 requires a synchronous localization context, please consult Gecko, Performance and Localization teams.
    564 
    565 
    566 Designing Localizable APIs
    567 ==========================
    568 
    569 When designing localizable APIs, the most important rule is to resolve localization as
    570 late as possible. That means that instead of resolving strings somewhere deep in the
    571 codebase and then passing them on, or even caching, it is highly recommended to pass
    572 around :code:`l10n-id` or :code:`[l10n-id, l10n-args]` pairs until the top-most code
    573 resolves them or applies them onto the DOM element.
    574 
    575 
    576 Testing
    577 =======
    578 
    579 When writing tests that involve both I18n and L10n, the general rule is that
    580 result strings are opaque. That means that the developer should not assume any particular
    581 value and should never test against it.
    582 
    583 In case of raw i18n the :js:`resolvedOptions` method on all :js:`Intl.*` formatters
    584 makes it relatively easy. In case of localization, the recommended way is to test that
    585 the code sets the right :code:`l10n-id`/:code:`l10n-args` attributes like this:
    586 
    587 .. code-block:: JavaScript
    588 
    589  testedFunction();
    590 
    591  const l10nAttrs = document.l10n.getAttributes(element);
    592 
    593  deepEquals(l10nAttrs, {
    594    id: "my-expected-id",
    595    args: {
    596      unreadCount: 5
    597    }
    598  });
    599 
    600 If the code really has to test for particular values in the localized UI, it is
    601 always better to scan for a variable:
    602 
    603 .. code-block:: JavaScript
    604 
    605  testedFunction();
    606 
    607  equals(element.textContent.contains("John"));
    608 
    609 .. important::
    610 
    611  Testing against whole values is brittle and will break when we insert Unicode
    612  bidirectionality marks into the result string or adapt the output in other ways.
    613 
    614 
    615 Manually Testing UI with Pseudolocalization
    616 ===========================================
    617 
    618 When working with a Fluent-backed UI, the developer gets a new tool to test their UI
    619 against several classes of problems.
    620 
    621 Pseudolocalization is a mechanism which transforms messages on the fly, using
    622 specific logic to help emulate how the UI will look once it gets localized.
    623 
    624 The three classes of potential problems that this can help with are:
    625 
    626 - Hardcoded strings.
    627 
    628   Turning on pseudolocalization should expose any strings that were left
    629   hardcoded in the source, since they won't get transformed.
    630 
    631 
    632 - UI space not adapting to longer text.
    633 
    634   Many languages use longer strings than English. For example, German strings
    635   may be 30% longer (or more). Turning on pseudolocalization is a quick way to
    636   test how the layout handles such locales. Strings that don't fit the space
    637   available are truncated and pseudolocalization can also help with detecting them.
    638 
    639 
    640 - Bidi adaptation.
    641 
    642   For many developers, testing the UI in right-to-left mode is hard.
    643   Pseudolocalization shows how a right-to-left locale will look like.
    644 
    645 To turn on pseudolocalization, open the :doc:`Browser Toolbox <../../devtools-user/browser_toolbox/index>`,
    646 click the three dot menu in the top right corner, and choose one of the following:
    647 
    648 - **Enable “accented” locale** - [Ȧȧƈƈḗḗƞŧḗḗḓ Ḗḗƞɠŀīīşħ]
    649 
    650   This strategy replaces all Latin characters with their accented equivalents,
    651   and duplicates some vowels to create roughly 30% longer strings. Strings are
    652   wrapped in markers (square brackets), which help with detecting truncation.
    653 
    654   This option sets the ``intl.l10n.pseudo`` pref to ``accented``.
    655 
    656 
    657 - **Enable bidi locale** - ɥsıʅƃuƎ ıpıԐ
    658 
    659   This strategy replaces all Latin characters with their 180 degree rotated versions
    660   and enforces right to left text flow using Unicode UAX#9 `Explicit Directional Embeddings`__.
    661   In this mode, the UI directionality will also be set to right-to-left.
    662 
    663   This option sets the ``intl.l10n.pseudo`` pref to ``bidi``.
    664 
    665 __ https://www.unicode.org/reports/tr9/#Explicit_Directional_Embeddings
    666 
    667 Testing other locales
    668 =====================
    669 
    670 .. important::
    671 
    672  For Firefox engineering work, you should prefer using pseudolocales.
    673  Especially on Nightly, localizations can be incomplete (as we add/remove
    674  localized content all the time) and cause confusing behaviour due to how
    675  fallback works.
    676 
    677 Installing Nightly in a different locale
    678 ----------------------------------------
    679 
    680 Localized Nightly builds are listed on `firefox.com`_.
    681 
    682 Installing language packs on local builds
    683 -----------------------------------------
    684 
    685 To fix bugs that only reproduce with a specific locale, you may need to run a
    686 development or nightly build with that locale. The UI language switcher in
    687 Settings is disabled by default on Nightly, because language packs can become
    688 incomplete and cause errors in the UI — there is no fallback to English for
    689 strings using legacy formats, like .properties.
    690 
    691 However, if you really need to use this, you can:
    692 
    693 1. Open ``about:config`` and flip the ``intl.multilingual.enabled`` and
    694   ``intl.multilingual.liveReload`` preferences to ``true``
    695 2. Open `the FTP listing for langpacks`_ and click the XPI file corresponding
    696   to your language and nightly version (note that, especially around merge days,
    697   multiple versions may be present).
    698 
    699   .. note::
    700      This is a Linux listing because that's the platform on which we run the
    701      l10n jobs, but the XPIs should work on macOS and Windows as well.
    702      The only exception is the "special" Japanese-for-mac locale,
    703      which is in the ``mac/xpi`` subdirectory under
    704      ``latest-mozilla-central-l10n`` instead. (``ja-JP-mac`` and ``ja`` will
    705      both "work" cross-platform, but use different terminology in some places.)
    706 
    707 3. Click through the prompts to install the language pack.
    708 4. Open the Firefox Settings UI.
    709 5. Switch to your chosen language.
    710 
    711 Building localized builds for single and multi-locale repacks
    712 -------------------------------------------------------------
    713 
    714 Instructions on how to build localization repacks are available on :doc:`/build/buildsystem/locales`.
    715 
    716 Including specific localization repacks to a Try Server push
    717 ------------------------------------------------------------
    718 
    719 Instructions are available in the `Firefox L10N FAQs`_.
    720 
    721 Finding a regression in a localized build
    722 -----------------------------------------
    723 
    724 You can run `mozregression`_ with localized builds!
    725 
    726 At the commandline, if you wanted to find a regression in a Dutch (``nl``)
    727 build, you could run something like:::
    728 
    729    mozregression --app firefox-l10n --lang nl --good 2024-01-01
    730 
    731 and that should run localized nightlies.
    732 
    733 .. _firefox.com: https://www.firefox.com/en-US/download/all/desktop-nightly/
    734 .. _the FTP listing for langpacks: https://ftp.mozilla.org/pub/firefox/nightly/latest-mozilla-central-l10n/linux-x86_64/xpi/
    735 .. _Firefox L10N FAQs: https://mozilla-l10n.github.io/documentation/products/firefox_desktop/firefox_l10n_faqs.html#how-can-i-test-a-different-locale
    736 .. _mozregression: https://mozilla.github.io/mozregression/
    737 
    738 Inner Structure of Fluent
    739 =========================
    740 
    741 The inner structure of Fluent in Gecko is out of scope of this tutorial, but
    742 since the class and file names may show up during debugging or profiling,
    743 below is a list of major components, each with a corresponding file in `/intl/l10n`
    744 modules in Gecko.
    745 
    746 For more hands-on experience with some of the concepts below, try
    747 following the `Fluent DOMLocalization Tutorial`__, which provides some
    748 background on how Fluent works and walks you through creating a basic
    749 web project from scratch that uses Fluent for localization.
    750 
    751 __ https://projectfluent.org/dom-l10n-documentation/overview.html
    752 
    753 FluentBundle
    754 --------------
    755 
    756 FluentBundle is the lowest level API. It's fully synchronous, contains a parser for the
    757 FTL file format and a resolver for the logic. It is not meant to be used by
    758 consumers directly.
    759 
    760 In the future we intend to offer this layer for standardization and it may become
    761 part of the :js:`mozIntl.*` or even :js:`Intl.*` API sets.
    762 
    763 That part of the codebase is also the first that we'll be looking to port to Rust.
    764 
    765 
    766 Localization
    767 ------------
    768 
    769 Localization is a higher level API which uses :js:`FluentBundle` internally but
    770 provides a full layer of compound message formatting and robust error fall-backing.
    771 
    772 It is intended for use in runtime code and contains all fundamental localization
    773 methods.
    774 
    775 
    776 DOMLocalization
    777 ---------------
    778 
    779 DOMLocalization extends :js:`Localization` with functionality to operate on HTML, XUL
    780 and the DOM directly including DOM Overlays and Mutation Observers.
    781 
    782 DocumentL10n
    783 ------------
    784 
    785 DocumentL10n implements the DocumentL10n WebIDL API and allows Document to
    786 communicate with DOMLocalization.
    787 
    788 Events
    789 ^^^^^^
    790 
    791 DOM translation is asynchronous (e.g., setting a `data-l10n-id` attribute won't
    792 immediately reflect the localized content in the DOM).
    793 
    794 We expose a :js:`Document.hasPendingL10nMutations` member that reflects whether
    795 there are any async operations pending. When they are finished, the
    796 `L10nMutationsFinished` event is fired on the document, so that chrome code can
    797 be certain all the async operations are done.
    798 
    799 L10nRegistry
    800 ------------
    801 
    802 L10nRegistry is our resource management service. It
    803 maintains the state of resources packaged into the build and language packs,
    804 providing an asynchronous iterator of :js:`FluentBundle` objects for a given locale set
    805 and resources that the :js:`Localization` class uses.
    806 
    807 
    808 .. _Fluent: https://projectfluent.org/
    809 .. _Firefox Preferences: https://bugzilla.mozilla.org/show_bug.cgi?id=1415730
    810 .. _Unprivileged Contexts: https://bugzilla.mozilla.org/show_bug.cgi?id=1407418
    811 .. _System Add-ons: https://bugzilla.mozilla.org/show_bug.cgi?id=1425104
    812 .. _CLDR: http://cldr.unicode.org/
    813 .. _ICU: http://site.icu-project.org/
    814 .. _Unicode: https://www.unicode.org/
    815 .. _Fluent Syntax Guide: https://projectfluent.org/fluent/guide/
    816 .. _Pontoon: https://pontoon.mozilla.org/