Bug 1699643 [wpt PR 28148] - screen-wake-lock: Adapt to https://github.com/w3c/screen...
[gecko.git] / intl / docs / dataintl.rst
bloba3c733b27ec7f76f991ffcb725b15adca17e07be
1 .. role:: js(code)
2    :language: javascript
4 =========================
5 UI Internationalization
6 =========================
8 There are many types of data that need to be formatted into a locale specific format,
9 or require locale specific API operations.
11 Gecko provides a rich set of locale aware APIs for operations such as:
13 * date and time formatting
14 * number formatting
15 * searching
16 * sorting
17 * plural rules
18 * calendar and locale information
20 .. note::
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.
27 JavaScript Internationalization API
28 ===================================
30 Data internationalization APIs are formalized in the JavaScript standard `ECMA 402`_.
31 These APIs are supported by all major JS environments.
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.
38 Services.intl
39 =============
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.
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.
48 For example, here's an example of a locale aware date formatting
49 using the regular :js:`Intl.DateTimeFormat`:
51 .. code-block:: javascript
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());
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.
64 But that ignores bits of information that could inform the formatting.
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.
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.
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:
77 .. code-block:: javascript
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());
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.
90 For those reasons, when dealing with Gecko application UI, it is always recommended
91 to use the :js:`Services.intl` wrapper.
93 Additional APIs
94 ================
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.
99 Many of those extensions are in the process of being standardized, but are
100 already available to Gecko developers for internal use.
102 Below is the list of current extensions:
104 mozIntl.DateTimeFormat
105 ----------------------
107 `DateTimeFormat` in `mozIntl` gets additional options that provide greater
108 simplicity and consistency to the API.
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.
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.
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.
123 Example use:
125 .. code-block:: javascript
127     let dtf = new Services.intl.DateTimeFormat(undefined, {
128       timeStyle: "short",
129       dateStyle: "short"
130     });
131     let value = dtf.format(new Date());
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.
138 mozIntl.getCalendarInfo(locale)
139 -------------------------------
141 The API will return the following calendar information for a given locale code:
143 * firstDayOfWeek
144     an integer in the range 1=Sunday to 7=Saturday indicating the day
145     considered the first day of the week in calendars, e.g. 1 for en-US,
146     2 for en-GB, 1 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 * weekendStart
151     an integer in the range 1=Sunday to 7=Saturday indicating the day
152     considered the beginning of a weekend, e.g. 7 for en-US, 7 for en-GB,
153     1 for bn-IN
154 * weekendEnd
155     an integer in the range 1=Sunday to 7=Saturday indicating the day
156     considered the end of a weekend, e.g. 1 for en-US, 1 for en-GB,
157     1 for bn-IN (note that "weekend" is *not* necessarily two days)
159 Those bits of information should be especially useful for any UI that works
160 with calendar data.
162 Example:
164 .. code-block:: javascript
166     // omitting the `locale` argument will make the API return data for the
167     // current Gecko application UI locale.
168     let {
169       firstDayOfWeek,  // 2
170       minDays,         // 4
171       weekendStart,    // 7
172       weekendEnd,      // 1
173       calendar,        // "gregory"
174       locale,          // "pl"
175     } = Services.intl.getCalendarInfo();
178 mozIntl.getDisplayNames(locales, options)
179 -----------------------------------------
181 :js:`getDisplayNames` API is useful to retrieve various terms available in the
182 internationalization API.
184 The API takes a locale fallback chain list, and an options object which can contain
185 two keys:
187 * :js:`style` which can takes values :js:`short`, :js:`medium`, :js:`long`
188 * :js:`keys` which is a list of keys in the following pattern:
190   * :js:`dates/fields/{year|month|week|day}`
191   * :js:`dates/gregorian/months/{january|...|december}`
192   * :js:`dates/gregorian/weekdays/{sunday|...|saturday}`
193   * :js:`dates/gregorian/dayperiods/{am|pm}`
195 The return object provides values for the requested keys for the given locale and
196 style.
198 Example:
200 .. code-block:: javascript
202     let {
203       locale,    // "pl"
204       style,     // "long"
205       values
206     } = Services.intl.getDisplayNames(undefined, {
207       style: "long",
208       keys: [
209         "dates/fields/year",
210         "dates/gregorian/months/january",
211         "dates/gregorian/weekdays/monday",
212         "dates/gregorian/dayperiods/am"
213       ]
214     });
216     values["dates/fields/year"] == "rok";
217     values["dates/gregorian/months/january"] = "styczeń";
218     values["dates/gregorian/weekdays/monday"] = "poniedziałek";
219     values["dates/gregorian/dayperiods/am"] = "AM";
222 mozIntl.getLocaleInfo(locales, options)
223 ---------------------------------------
225 The API returns a simple object with information about the requested locale.
227 At the moment the only bit handled by the API is directionality defined as `direction`
228 key on the returned object.
230 Example:
232 .. code-block:: javascript
234     let {
235       locale,    // "pl"
236       direction: // "ltr"
237     } = Services.intl.getLocaleInfo(undefined);
240 mozIntl.RelativeTimeFormat(locales, options)
241 --------------------------------------------
243 API which can be used to format an interval or a date into a textual
244 representation of a relative time, such as **5 minutes ago** or **in 2 days**.
246 This API is in the process of standardization and in its raw form will not handle
247 any calculations to select the best unit. It is intended to just offer a way
248 to format a value.
250 `mozIntl` wrapper extends the functionality providing the calculations and
251 allowing the user to get the current best textual representation of the delta.
253 Example:
255 .. code-block:: javascript
257     let rtf = new Services.intl.RelativeTimeFormat(undefined, {
258       style: "long", // "narrow" | "short" | "long" (default)
259       numeric: "auto", // "always" | "auto" (default)
260     });
262     let now = Date.now();
263     rtf.formatBestUnit(new Date(now - 3 * 1000 * 60)); // "3 minutes ago"
265 The option `numeric` has value set to `auto` by default, which means that when possible
266 the formatter will use special textual terms like *yesterday*, *last year*, and so on.
268 Those values require specific calculations that the raw `Intl.*` API cannot provide.
269 For example, *yesterday* requires the algorithm to know not only the time delta,
270 but also what time of the day `now` is. 15 hours ago may be *yesterday* if it
271 is 10am, but will still be *today* if it is 11pm.
273 For that reason the future `Intl.RelativeTimeFormat` will use *always* as default,
274 since terms such as *15 hours ago* are independent of the current time.
276 .. note::
278   In the current form, the API should be only used to format standalone values.
279   Without additional capitalization rules, it cannot be freely used in sentences.
281 mozIntl.getLanguageDisplayNames(locales, langCodes)
282 ---------------------------------------------------
284 API which returns a list of language names formatted for display.
286 Example:
288 .. code-block:: javascript
290   let langs = getLanguageDisplayNames(["pl"], ["fr", "de", "en"]);
291   langs === ["Francuski", "Niemiecki", "Angielski"];
294 mozIntl.getRegionDisplayNames(locales, regionCodes)
295 ---------------------------------------------------
297 API which returns a list of region names formatted for display.
299 Example:
301 .. code-block:: javascript
303   let regs = getLanguageDisplayNames(["pl"], ["US", "CA", "MX"]);
304   regs === ["Stany Zjednoczone", "Kanada", "Meksyk"];
306 mozIntl.getLocaleDisplayNames(locales, localeCodes)
307 ---------------------------------------------------
309 API which returns a list of region names formatted for display.
311 Example:
313 .. code-block:: javascript
315   let locs = getLanguageDisplayNames(["pl"], ["sr-RU", "es-MX", "fr-CA"]);
316   locs === ["Serbski (Rosja)", "Hiszpański (Meksyk)", "Francuski (Kanada)"];
318 mozIntl.getAvailableLocaleDisplayNames(type)
319 ---------------------------------------------------
321 API which returns a list of locale display name codes available for a
322 given type.
323 Available types are: "language", "region".
325 Example:
327 .. code-block:: javascript
329   let codes = getAvailableLocaleDisplayNames("region");
330   codes === ["au", "ae", "af", ...];
332 Best Practices
333 ==============
335 The most important best practice when dealing with data internationalization is to
336 perform it as close to the actual UI as possible; right before the UI is displayed.
338 The reason for this practice is that internationalized data is considered *"opaque"*,
339 which means that no code should ever attempt to operate on it. Late resolution also
340 increases the chance that the data will be formatted in the current locale
341 selection and not formatted and cached prematurely.
343 It's very important to not attempt to search, concatenate or in any other way
344 alter the output of the API. Once it gets formatted, the only thing to do with
345 the output should be to present it to the user.
347 Testing
348 -------
350 The above is also important in the context of testing. It is a common mistake to
351 attempt to write tests that verify the output of the UI with internationalized data.
353 The underlying data set used to create the formatted version of the data may and will
354 change over time, both due to dataset improvements and also changes to the language
355 and regional preferences over time.
356 That means that tests that attempt to verify the exact output will require
357 significantly higher level of maintenance and will remain brittle.
359 Most of the APIs provide special method, like :js:`resolvedOptions` which should be used
360 instead to verify that the output is matching the expectations.
362 Future extensions
363 =================
365 If you find yourself in the need of additional internationalization APIs not currently
366 supported, you can verify if the API proposal is already in the works here,
367 and file a bug in the component `Core::Internationalization`_ to request it.
369 .. _ECMA 402: https://tc39.github.io/ecma402/
370 .. _Intl API: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl
371 .. _CLDR: http://cldr.unicode.org/
372 .. _ICU: http://site.icu-project.org/
373 .. _Core::Internationalization: https://bugzilla.mozilla.org/enter_bug.cgi?product=Core&component=Internationalization