Bug 1438687 - Add Developer documentation for Intl and mozIntl. draft
authorZibi Braniecki <zbraniecki@mozilla.com>
Sun, 25 Mar 2018 15:02:14 +0200
changeset 772247 0cfa620e34f2c29391ca1e5b06ae4c41c9530ab8
parent 772246 a97eed4b7a966ffe65f9076200ab7bcc72df1195
child 772248 7e797036d432bcfc9fe4e0f09b12e4f645c8f433
push id103879
push userbmo:gandalf@aviary.pl
push dateSun, 25 Mar 2018 13:04:10 +0000
bugs1438687
milestone61.0a1
Bug 1438687 - Add Developer documentation for Intl and mozIntl. MozReview-Commit-ID: JBfR1B6FwmJ
intl/docs/dataintl.rst
new file mode 100644
--- /dev/null
+++ b/intl/docs/dataintl.rst
@@ -0,0 +1,303 @@
+=========================
+UI Internationalization
+=========================
+
+There are many types of data that need to be formatted into a locale specific format,
+or requires locale specific API operations.
+
+Gecko provides a rich set of APIs for operations such as date and time formatting,
+number formatting, searching, sorting, plural rules, calendar and locale information
+and so on.
+
+Most of the APIs are backed by the CLDR and ICU and the focus is on enabling
+front-end code internationalization, which means the majority of the APIs are
+primarely available in JavaScript, with C++ and Rust having only a small subset of
+them enabled.
+
+Best Practices
+==============
+
+The most important best practice when dealing with data internationalization is to
+perform it on as high level as possible; right before the UI is displayed.
+
+That means that the API design should be focused on handling raw data such as dates,
+numbers and other information and only formatted to the user preferred locale
+as late as possible.
+
+The reason for this practice is that internationalized data is considered *"opaque"*,
+which means that no code should ever attempt to operate on it. Late resolution also
+increases the chance that the data will be formatted in the current locale
+selection and not cached or handled formatted prematurely.
+
+It's very important to not attempt to search, concatenate or in any other way
+alter the output of the API. Once it gets formatted, the only thing to do with
+the output should be to present it to the user.
+
+Testing
+-------
+
+The above is also important in the context of testing. It is a common mistake to
+attempt to write tests that verify the output of the UI with internationalized data.
+
+The underlying data set used to create the formatted version of the data may and will
+change over time both, due to dataset improvements and also changes to the language
+and regional preferences over time.
+That means that tests that attempt to verify the exact output will require
+significantly higher level of maintenance and will remain more brittle.
+
+Most of the APIs provide special method, like `resolvedOptions` which should be used
+in stead to verify the output matching the expectations.
+
+JavaScript Internationalization API
+===================================
+
+JavaScript standard comes with a set of APIs in form of a standard called ECMA402.
+ECMA402 contains a set of APIs designed to enable data internationalization and is
+supported by all major JS environments.
+
+It is best to consult the MDN article on the current state of the Intl API.
+Mozilla has an excellent support of the API and relies on it for majority
+of its needs, through when possible when working on Firefox UI the `mozIntl` wrapper
+should be used.
+
+MozIntl
+=======
+
+`MozIntl` is an extension of the JS Intl API which should be used whenever
+working with Gecko app user interface with chrome privileges.
+
+The API provides the same objects and methods as `Intl.*`, but fine tunes them
+to the Gecko app user preferences, including matching OS Preferences and
+other locale choices that web content exposed JS Intl API cannot.
+
+For example, while the regular `Intl.DateTimeFormat` will provide a good
+formatting:
+
+.. code-block:: javascript
+
+    let rtf = new Intl.DateTimeFormat(undefined, {
+      year: "numeric",
+      month: "long",
+      day: "numeric"
+    });
+    let value = rtf.format(new Date());
+
+It will do a good job at formatting the date to the user needs, but it will
+only be able to use the customization bits that are exposed to the Web, including
+the locale the user broadcasts to the Web and any additional settings.
+
+But that leaves a lot of bits of information that could inform the formatting out.
+
+Public API such as `Intl.*` will not be able to look into the Operating System for
+regional preferences. It will also respect settings such as `resist Fingerprinting`
+by masking its timezone and locale settings.
+
+This is a fair tradeoff when dealing with Web Content, but in most cases, the
+privileged UI of the Gecko application should be able to access all of those
+additional bits and not be affected by the anti-fingerpting masking.
+
+`mozIntl` is a simple wrapper which in its simplest form works exactly the same. It's
+exposed on `Services.intl` object and can be used just like a regular `Intl` API:
+
+.. code-block:: javascript
+
+    let rtf = new Services.intl.DateTimeFormat(undefined, {
+      year: "numeric",
+      month: "long",
+      day: "numeric"
+    });
+    let value = rtf.format(new Date());
+
+The difference is that this API will not use the set of locales as defined for
+Gecko, and will also respect additional regional preferences that Gecko
+will fetch from the Operating System.
+
+For those reasons, when dealing with Gecko application UI, it is always recommended
+to use the `MozIntl` wrapper.
+
+Additional APIs
+================
+
+On top of wrapping `Intl` API, `mozIntl` provides a number of additional features
+in form of additional options to existing APIs as well as completely new APIs.
+
+Many of those extensions are in the process of being standardized, but are
+already available to Gecko developers for the internal use.
+
+Below is the list of current extensions:
+
+mozIntl.DateTimeFormat
+----------------------
+
+`DateTimeFormat` in `mozIntl` gets additional options that provide greater
+simplicy and consistency to the API.
+
+* `timeStyle` and `dateStyle` can take values `short`, `medium`, `long` and `full`
+  Those options can replace the manual listing of tokens like `year`, `day`, `hour` etc.
+  and will compose the most natural date or time format of a given style for the selected
+  locale.
+
+Using `timeStyle` and `dateStyle` is highly recommended over listing the tokens,
+because different locales may use different default styles for displaying the same tokens.
+
+Additional value is that using those styles allows `mozIntl` to look into Operating
+System patterns which allow the user to customize those patterns to their liking.
+
+Example use:
+
+.. code-block:: javascript
+
+    let dtf = new Services.intl.DateTimeFormat(undefined, {
+      timeStyle: "short",
+      dateStyle: "short"
+    });
+    let value = dtf.format(new Date());
+
+This will select the best locale for to match the current Gecko application locale,
+then potentially check for Operating System regional preferneces customizations,
+and produce the correct pattern for short date+time style and format a date into it.
+
+
+mozIntl.getCalendarInfo(locale)
+-------------------------------
+
+The API will return the following calendar information for a given locale code:
+
+* firstDayOfWeek
+    an integer in the range 1=Sunday to 7=Saturday indicating the day
+    considered the first day of the week in calendars, e.g. 1 for en-US,
+    2 for en-GB, 1 for bn-IN
+* minDays
+    an integer in the range of 1 to 7 indicating the minimum number
+    of days required in the first week of the year, e.g. 1 for en-US, 4 for de
+* weekendStart
+    an integer in the range 1=Sunday to 7=Saturday indicating the day
+    considered the beginning of a weekend, e.g. 7 for en-US, 7 for en-GB,
+    1 for bn-IN
+* weekendEnd
+    an integer in the range 1=Sunday to 7=Saturday indicating the day
+    considered the end of a weekend, e.g. 1 for en-US, 1 for en-GB,
+    1 for bn-IN (note that "weekend" is *not* necessarily two days)
+
+Those bits of information should be especially useful for any UI that works
+with calendar data.
+
+Example:
+
+.. code-block:: javascript
+
+    // missing `locale` argument will make the API return data for the
+    // current Gecko application UI locale.
+    let {
+      firstDayOfWeek,  // 2
+      minDays,         // 4
+      weekendStart,    // 7
+      weekendEnd,      // 1
+      calendar,        // "gregory"
+      locale,          // "pl"
+    } = Services.intl.getCalendarInfo();
+
+
+mozIntl.getDisplayNames(locales, options)
+-----------------------------------------
+
+`getDisplayNames` API is useful to retrieve various terms available in the
+internationalization API. Currently the focus is on terms used in date and time
+formatting, but the keys can be extended.
+
+The API takes a locale fallback chain list, and an options object which can contain
+two keys:
+* `style` which can takes values `short`, `medium`, `long`
+* `keys` which is a list of keys in the following pattern:
+** 'dates/fields/{year|month|week|day}'
+** 'dates/gregorian/months/{january|...|december}'
+** 'dates/gregorian/weekdays/{sunday|...|saturday}'
+** 'dates/gregorian/dayperiods/{am|pm}'
+
+The return object provides values for the requested keys matching the locale and
+style.
+
+Example:
+
+.. code-block:: javascript
+
+    let {
+      locale,    // "pl"
+      style,     // "long"
+      values
+    } = Services.intl.getDisplayNames(undefined, {
+      style: "long",
+      keys: [
+        "dates/fields/year",
+        "dates/gregorian/months/january",
+        "dates/gregorian/weekdays/monday",
+        "dates/gregorian/dayperiods/am"
+      ]
+    });
+
+    values["dates/fields/year"];                // "rok"
+    values["dates/gregorian/months/january"];   // "styczeń"
+    values["dates/gregorian/weekdays/monday"];  // "poniedziałek"
+    values["dates/gregorian/dayperiods/am"];    // "AM"
+
+
+mozIntl.getLocaleInfo(locales, options)
+---------------------------------------
+
+The API returns a simple object with information about the requested locale.
+
+At the moment the only bit handled by the API is directionality defined as `direction`
+key on the returned object.
+
+Example:
+
+.. code-block:: javascript
+
+    let {
+      locale,    // "pl"
+      direction: // "ltr"
+    } = Services.intl.getLocaleInfo(undefined);
+
+
+mozIntl.RelativeTimeFormat(locales, options)
+--------------------------------------------
+
+API which can be used to format an interval or a date into a textual
+representation of a relative time such as *5 minutes ago* or *in 2 days*.
+
+This API is in the process of standardization and in its raw form will not handle
+any calculations to select the best unit. It is inteded to just offer a way
+to format a value.
+
+`mozIntl` wrapper extends the functionality providing the calculations and
+allowing the user to get the current best textual representation of the delta.
+
+Example:
+
+.. code-block:: javascript
+
+    let rtf = new Services.intl.RelativeTimeFormat(undefined, {
+      style: "long", // "narrow" | "short" | "long" (default)
+      numeric: "auto", // "always" | "auto" (defualt)
+    });
+
+    let now = Date.now();
+    rtf.formatBestUnit(new Date(now - 3 * 1000 * 60)); // "3 minutes ago"
+
+The option `numeric` has value set to `auto` by default, which means that when possible
+the formatter will use special textual terms like *yesterday*, *last year*, and so on.
+
+Those values require specific calculations that the raw `Intl.*` API cannot provide.
+For example, *yesterday* requires the algorithm to know not only the time delta,
+but also what time of the day `now` is. 15 hours ago may be *yesterday* if it
+is 10am, but will still be *today* if it is 11pm.
+
+For that reason the future `Intl.RelativeTimeFormat` will use *always* as default,
+since terms such as *15 hours ago* are independent of the current time.
+
+Additional APIs
+===============
+
+If you find yourself in the need of additional internationalization APIs not currently
+supported, you can verify if the API proposal is already in the works here,
+and file a bug in the component X to indicate the request for it.