tor-browser

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

dataintl.rst (12845B)


      1 .. role:: js(code)
      2   :language: javascript
      3 
      4 =========================
      5 UI Internationalization
      6 =========================
      7 
      8 There are many types of data that need to be formatted into a locale specific format,
      9 or require locale specific API operations.
     10 
     11 Gecko provides a rich set of locale aware APIs for operations such as:
     12 
     13 * date and time formatting
     14 * number formatting
     15 * searching
     16 * sorting
     17 * plural rules
     18 * calendar and locale information
     19 
     20 .. note::
     21 
     22  Most of the APIs are backed by the Unicode projects `CLDR`_ and `ICU`_ and are
     23  focused on enabling front-end code internationalization, which means the majority of
     24  the APIs are primarily available in JavaScript, with C++ and Rust having only a small
     25  subset of them exposed.
     26 
     27 JavaScript Internationalization API
     28 ===================================
     29 
     30 Data internationalization APIs are formalized in the JavaScript standard `ECMA 402`_.
     31 These APIs are supported by all major JS environments.
     32 
     33 It is best to consult the MDN article on the current state of the `Intl API`_.
     34 Mozilla has an excellent support of the API and relies on it for majority
     35 of its needs. Yet, when working on Firefox UI the :js:`Services.intl` wrapper
     36 should be used.
     37 
     38 Services.intl
     39 =============
     40 
     41 :js:`Services.intl` is an extension of the JS Intl API which should be used whenever
     42 working with Gecko app user interface with chrome privileges.
     43 
     44 The API provides the same objects and methods as :js:`Intl.*`, but fine tunes them
     45 to the Gecko app user preferences, including matching OS Preferences and
     46 other locale choices that web content exposed JS Intl API cannot.
     47 
     48 For example, here's an example of a locale aware date formatting
     49 using the regular :js:`Intl.DateTimeFormat`:
     50 
     51 .. code-block:: javascript
     52 
     53    let rtf = new Intl.DateTimeFormat(navigator.languages, {
     54      year: "numeric",
     55      month: "long",
     56      day: "numeric"
     57    });
     58    let value = rtf.format(new Date());
     59 
     60 It will do a good job at formatting the date to the user locale, but it will
     61 only be able to use the customization bits that are exposed to the Web, based on
     62 the locale the user broadcasts to the Web and any additional settings.
     63 
     64 But that ignores bits of information that could inform the formatting.
     65 
     66 Public API such as :js:`Intl.*` will not be able to look into the Operating System for
     67 regional preferences. It will also respect settings such as `Resist Fingerprinting`
     68 by masking its timezone and locale settings.
     69 
     70 This is a fair tradeoff when dealing with the Web Content, but in most cases, the
     71 privileged UI of the Gecko application should be able to access all of those
     72 additional bits and not be affected by the anti-fingerprinting masking.
     73 
     74 `mozIntl` is a simple wrapper which in its simplest form works exactly the same. It's
     75 exposed on :js:`Services.intl` object and can be used just like a regular `Intl` API:
     76 
     77 .. code-block:: javascript
     78 
     79    let rtf = new Services.intl.DateTimeFormat(undefined, {
     80      year: "numeric",
     81      month: "long",
     82      day: "numeric"
     83    });
     84    let value = rtf.format(new Date());
     85 
     86 The difference is that this API will now use the set of locales as defined for
     87 Gecko, and will also respect additional regional preferences that Gecko
     88 will fetch from the Operating System.
     89 
     90 For those reasons, when dealing with Gecko application UI, it is always recommended
     91 to use the :js:`Services.intl` wrapper.
     92 
     93 Additional APIs
     94 ================
     95 
     96 On top of wrapping up `Intl` API, `mozIntl` provides a number of features
     97 in form of additional options to existing APIs as well as completely new APIs.
     98 
     99 Many of those extensions are in the process of being standardized, but are
    100 already available to Gecko developers for internal use.
    101 
    102 Below is the list of current extensions:
    103 
    104 mozIntl.DateTimeFormat
    105 ----------------------
    106 
    107 `DateTimeFormat` in `mozIntl` gets additional options that provide greater
    108 simplicity and consistency to the API.
    109 
    110 * :js:`timeStyle` and :js:`dateStyle` can take values :js:`short`, :js:`medium`,
    111  :js:`long` and :js:`full`.
    112  These options can replace the manual listing of tokens like :js:`year`, :js:`day`, :js:`hour` etc.
    113  and will compose the most natural date or time format of a given style for the selected
    114  locale.
    115 
    116 Using :js:`timeStyle` and :js:`dateStyle` is highly recommended over listing the tokens,
    117 because different locales may use different default styles for displaying the same tokens.
    118 
    119 Additional value is that using those styles allows `mozIntl` to look into
    120 Operating System patterns, which gives users the ability to customize those
    121 patterns to their liking.
    122 
    123 Example use:
    124 
    125 .. code-block:: javascript
    126 
    127    let dtf = new Services.intl.DateTimeFormat(undefined, {
    128      timeStyle: "short",
    129      dateStyle: "short"
    130    });
    131    let value = dtf.format(new Date());
    132 
    133 This will select the best locale to match the current Gecko application locale,
    134 then potentially check for Operating System regional preferences customizations,
    135 produce the correct pattern for short date+time style and format the date into it.
    136 
    137 
    138 mozIntl.getCalendarInfo(locale)
    139 -------------------------------
    140 
    141 The API will return the following calendar information for a given locale code:
    142 
    143 * firstDayOfWeek
    144    an integer in the range 1=Monday to 7=Sunday indicating the day
    145    considered the first day of the week in calendars, e.g. 7 for en-US,
    146    1 for en-GB, 7 for bn-IN
    147 * minDays
    148    an integer in the range of 1 to 7 indicating the minimum number
    149    of days required in the first week of the year, e.g. 1 for en-US, 4 for de
    150 * weekend
    151    an array with values in the range 1=Monday to 7=Sunday indicating the days
    152    of the week considered as part of the weekend, e.g. [6, 7] for en-US and en-GB,
    153    [7] for bn-IN (note that "weekend" is *not* necessarily two days)
    154 
    155 Those bits of information should be especially useful for any UI that works
    156 with calendar data.
    157 
    158 Example:
    159 
    160 .. code-block:: javascript
    161 
    162    // omitting the `locale` argument will make the API return data for the
    163    // current Gecko application UI locale.
    164    let {
    165      firstDayOfWeek,  // 1
    166      minDays,         // 4
    167      weekend,         // [6, 7]
    168      calendar,        // "gregory"
    169      locale,          // "pl"
    170    } = Services.intl.getCalendarInfo();
    171 
    172 
    173 mozIntl.DisplayNames(locales, options)
    174 -----------------------------------------
    175 
    176 :js:`DisplayNames` API is useful to retrieve various terms available in the
    177 internationalization API. :js:`mozIntl.DisplayNames` extends the standard
    178 `Intl.DisplayNames`_ to additionally provide localization for date-time types.
    179 
    180 The API takes a locale fallback chain list, and an options object which can contain
    181 two keys:
    182 
    183 * :js:`style` which can take values :js:`narrow`, :js:`short`, :js:`abbreviated`, :js:`long`
    184 * :js:`type` which can take values :js:`language`, :js:`script`, :js:`region`,
    185  :js:`currency`, :js:`weekday`, :js:`month`, :js:`quarter`, :js:`dayPeriod`,
    186  :js:`dateTimeField`
    187 
    188 Example:
    189 
    190 .. code-block:: javascript
    191 
    192    let dateTimeFieldDisplayNames = new Services.intl.DisplayNames(undefined, {
    193      type: "dateTimeField",
    194    });
    195    dateTimeFieldDisplayNames.resolvedOptions().locale = "pl";
    196    dateTimeFieldDisplayNames.of("year") = "rok";
    197 
    198    let monthDisplayNames = new Services.intl.DisplayNames(undefined, {
    199      type: "month", style: "long",
    200    });
    201    monthDisplayNames.of(1) = "styczeń";
    202 
    203    let weekdaysDisplayNames = new Services.intl.DisplayNames(undefined, {
    204      type: "weekday", style: "short",
    205    });
    206    weekdaysDisplayNames.of(1) = "pon";
    207 
    208    let dayPeriodsDisplayNames = new Services.intl.DisplayNames(undefined, {
    209      type: "dayPeriod", style: "narrow",
    210    });
    211    dayPeriodsDisplayNames.of("am") = "AM";
    212 
    213 
    214 mozIntl.RelativeTimeFormat(locales, options)
    215 --------------------------------------------
    216 
    217 API which can be used to format an interval or a date into a textual
    218 representation of a relative time, such as **5 minutes ago** or **in 2 days**.
    219 
    220 This API is in the process of standardization and in its raw form will not handle
    221 any calculations to select the best unit. It is intended to just offer a way
    222 to format a value.
    223 
    224 `mozIntl` wrapper extends the functionality providing the calculations and
    225 allowing the user to get the current best textual representation of the delta.
    226 
    227 Example:
    228 
    229 .. code-block:: javascript
    230 
    231    let rtf = new Services.intl.RelativeTimeFormat(undefined, {
    232      style: "long", // "narrow" | "short" | "long" (default)
    233      numeric: "auto", // "always" | "auto" (default)
    234    });
    235 
    236    let now = Date.now();
    237    rtf.formatBestUnit(new Date(now - 3 * 1000 * 60)); // "3 minutes ago"
    238 
    239 The option `numeric` has value set to `auto` by default, which means that when possible
    240 the formatter will use special textual terms like *yesterday*, *last year*, and so on.
    241 
    242 Those values require specific calculations that the raw `Intl.*` API cannot provide.
    243 For example, *yesterday* requires the algorithm to know not only the time delta,
    244 but also what time of the day `now` is. 15 hours ago may be *yesterday* if it
    245 is 10am, but will still be *today* if it is 11pm.
    246 
    247 For that reason the future `Intl.RelativeTimeFormat` will use *always* as default,
    248 since terms such as *15 hours ago* are independent of the current time.
    249 
    250 .. note::
    251 
    252  In the current form, the API should be only used to format standalone values.
    253  Without additional capitalization rules, it cannot be freely used in sentences.
    254 
    255 mozIntl.getLanguageDisplayNames(locales, langCodes)
    256 ---------------------------------------------------
    257 
    258 API which returns a list of language names formatted for display.
    259 
    260 Example:
    261 
    262 .. code-block:: javascript
    263 
    264  let langs = getLanguageDisplayNames(["pl"], ["fr", "de", "en"]);
    265  langs === ["Francuski", "Niemiecki", "Angielski"];
    266 
    267 
    268 mozIntl.getRegionDisplayNames(locales, regionCodes)
    269 ---------------------------------------------------
    270 
    271 API which returns a list of region names formatted for display.
    272 
    273 Example:
    274 
    275 .. code-block:: javascript
    276 
    277  let regs = getRegionDisplayNames(["pl"], ["US", "CA", "MX"]);
    278  regs === ["Stany Zjednoczone", "Kanada", "Meksyk"];
    279 
    280 mozIntl.getLocaleDisplayNames(locales, localeCodes)
    281 ---------------------------------------------------
    282 
    283 API which returns a list of region names formatted for display.
    284 
    285 Example:
    286 
    287 .. code-block:: javascript
    288 
    289  let locs = getLocaleDisplayNames(["pl"], ["sr-RU", "es-MX", "fr-CA"]);
    290  locs === ["Serbski (Rosja)", "Hiszpański (Meksyk)", "Francuski (Kanada)"];
    291 
    292 mozIntl.getAvailableLocaleDisplayNames(type)
    293 ---------------------------------------------------
    294 
    295 API which returns a list of locale display name codes available for a
    296 given type.
    297 Available types are: "language", "region".
    298 
    299 Example:
    300 
    301 .. code-block:: javascript
    302 
    303  let codes = getAvailableLocaleDisplayNames("region");
    304  codes === ["au", "ae", "af", ...];
    305 
    306 Best Practices
    307 ==============
    308 
    309 The most important best practice when dealing with data internationalization is to
    310 perform it as close to the actual UI as possible; right before the UI is displayed.
    311 
    312 The reason for this practice is that internationalized data is considered *"opaque"*,
    313 which means that no code should ever attempt to operate on it. Late resolution also
    314 increases the chance that the data will be formatted in the current locale
    315 selection and not formatted and cached prematurely.
    316 
    317 It's very important to not attempt to search, concatenate or in any other way
    318 alter the output of the API. Once it gets formatted, the only thing to do with
    319 the output should be to present it to the user.
    320 
    321 Testing
    322 -------
    323 
    324 The above is also important in the context of testing. It is a common mistake to
    325 attempt to write tests that verify the output of the UI with internationalized data.
    326 
    327 The underlying data set used to create the formatted version of the data may and will
    328 change over time, both due to dataset improvements and also changes to the language
    329 and regional preferences over time.
    330 That means that tests that attempt to verify the exact output will require
    331 significantly higher level of maintenance and will remain brittle.
    332 
    333 Most of the APIs provide special method, like :js:`resolvedOptions` which should be used
    334 instead to verify that the output is matching the expectations.
    335 
    336 Future extensions
    337 =================
    338 
    339 If you find yourself in the need of additional internationalization APIs not currently
    340 supported, you can verify if the API proposal is already in the works here,
    341 and file a bug in the component `Core::Internationalization`_ to request it.
    342 
    343 .. _ECMA 402: https://tc39.github.io/ecma402/
    344 .. _Intl API: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl
    345 .. _CLDR: http://cldr.unicode.org/
    346 .. _ICU: http://site.icu-project.org/
    347 .. _Core::Internationalization: https://bugzilla.mozilla.org/enter_bug.cgi?product=Core&component=Internationalization
    348 .. _Intl.DisplayNames: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DisplayNames