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/