blob: 8da1fdf8e17f0fd282c36d262942525f38a02a13 [file] [log] [blame] [view]
# Internationalization preferences
Fuchsia has some conventions for how to communicate i18n preferences, whether
from an end user to components, or among components.
This guide covers the following:
- Use Unicode BCP-47 Locale Identifiers to encode [locale IDs](#locale-ids).
- Use `fuchsia.intl.PropertyProvider` in components to
[read internationalization preferences](#access). Make sure that the product
configuration includes [`intl_services`](#intl-services) or another
implementation of this protocol.
- If implementing a settings UI, use `fuchsia.settings.Intl` to
[write internationalization settings](#store).
## Locale identifiers {#locale-ids}
The keystone of i18n preferences is the _locale identifier_, which is a string
that concisely conveys information such as:
- Language (e.g. English, French, Arabic)
- Country or region (e.g. United Kingdom, Morocco, South Korea)
- Script (e.g. Latin, Cyrillic, Traditional Chinese, Simplified Chinese)
Locale identifiers can also convey more granular information, such as:
- Calendar (e.g. Gregorian, Japanese, Hebrew)
- First day of week
- Collation (sort order, grouping strings for search)
- Digit style (e.g. "Arabic" 012345, "Eastern Arabic" ٠١٢٣٤٥)
- Number format (decimal separator, digit grouping)
- Currency format
- Time and date formats
- Etc.
Specifying these details is particularly useful when overriding the default
values for a given language and region (see next section).
### Unicode BCP-47 Locale Identifiers {#unicode-bcp-47}
Fuchsia uses the
[Unicode BCP-47 Locale Identifier](http://www.unicode.org/reports/tr35/#BCP_47_Conformance)
standard for locale IDs.
For example, the following locale ID specifies the Serbian language (`sr`) as
spoken in Serbia (`RS`), written in a Cyrillic script (`Cyrl`):
```none {:.devsite-disable-click-to-copy}
"sr-Cyrl-RS"
```
You can use
[Unicode extension subtags](http://unicode.org/reports/tr35/#u_Extension) in the
locale ID to add overrides. Consider the following example:
```none {:.devsite-disable-click-to-copy}
"sr-Cyrl-RS-u-ca-hebrew-fw-monday-ms-ussystem-nu-deva-tz-usnyc"
```
This example specifies the following:
| Subtag(s) | Meaning |
| ------------- | ------------------------------------------------------------ |
| `sr` | Specifies the Serbian language. |
| `Cyrl` | Specifies the Cyrillic script. |
| `RS` | Specifies Serbia as the country/region. |
| `u` | Marks the start of the Unicode extension data. |
| `ca-hebrew` | Specifies the Hebrew calendar. |
| `fw-monday` | Specifies Monday as the first day of the week. |
| `ms-ussystem` | Specifies the measurement system as "US", e.g. feet, ounces, |
: : etc. :
| `nu-deva` | Specifies Devanagari numerals. |
| `tz-usnyc` | Specifies the time zone as `America/New_York`. |
Not all internationalization properties that one might want to express have a
corresponding Unicode extension. For example, there is currently no extension
for temperature units, so there is no way to express "use metric units, but use
Fahrenheit for temperature" in a locale ID.
## Accessing i18n preferences {#access}
To send i18n preferences between Fuchsia
[components](/docs/glossary/README.md#component), use the
[`fuchsia.intl.Profile`](https://fuchsia.dev/reference/fidl/fuchsia.intl#Profile)
FIDL table:
```fidl {:.devsite-disable-click-to-copy}
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="sdk/fidl/fuchsia.intl/intl.fidl" indented_block="type Profile" exclude_regexp="(//.*)|(^$)" %}
```
The locale ID is only a building block in the `Profile`. A profile contains a
ranked list of locale IDs (to express relative preference, priority, or degree
of support; see [Locale fallback](./localization/locale_fallback.md) for a
usage example), as well as other properties that cannot be fully expressed in a
single locale ID. When there is a conflict, explicit settings in the `Profile`
override the values in the locale ID (e.g. specifying US measurement units in
the locale ID but `CELSIUS` in the `temperature_unit` field).
When a component needs to provide i18n preferences to other components, it
should implement the
[`fuchsia.intl.PropertyProvider`](https://fuchsia.dev/reference/fidl/fuchsia.intl#PropertyProvider)
protocol, which serves the `Profile` and notifies of changes:
```fidl {:.devsite-disable-click-to-copy}
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="sdk/fidl/fuchsia.intl/property_provider.fidl" indented_block="protocol PropertyProvider" exclude_regexp="(//.*)|(^$)" %}
```
This protocol offers a _read-only_ view of an internationalization profile.
Depending on the implementation of the service and the
[realm](/docs/concepts/components/v2/realms.md) in which it is intended to
serve, the contents of the internationalization profile may be derived from
[user settings](#store), a product's factory settings, a specific component's
requirements, or some combination of the above.
### Do not assume an ambient locale {#no-ambient-locale}
Fuchsia has no ambient or system locale. Locale and other i18n preferences
depend on the context in which a component is running. This is in contrast to
other operating systems, which may have APIs to obtain global or default locale
settings, following Fuchsia's design principle of
[no ambient authority](/docs/concepts/principles/secure.md)
In runtimes where the standard library offers access to some default locale (for
example, `Platform.localeName` in Dart and Flutter), it is the responsibility of
the [runner](/docs/concepts/components/v2/capabilities/runners.md) to retrieve
the needed values from the realm of the component being run. In most cases, the
runner should call `fuchsia.intl.PropertyProvider.GetProfile`. See
[Runner integrations](#runner-integrations) below.
#### Multiple i18n `Profile`s {#multiple-i18n-profiles}
Depending on a product's design, it is possible that two component instances
running concurrently on the same machine in different realms are connected to
different `PropertyProvider` instances and receive different `Profile`s.
For example, an encyclopedia component showing a Spanish-language (`es-ES`)
article about Mallorca may choose to launch a map component with an `es-ES` UI,
while at the same time an English-language (`en-US`) article launches the same
map component, but tells it to display an `en-US` UI. This can be accomplished
with two different
[sub-realms](/docs/concepts/components/v2/realms.md#definitions) that each
receives a different `PropertyProvider` instance.
### `intl_services` library {#intl-services}
A basic C++ library implementing `fuchsia.intl.PropertyProvider` is found at
[`//src/lib/intl/intl_property_provider_impl`](/src/lib/intl/intl_property_provider_impl).
The
[`core`](/docs/development/build/build_system/boards_and_products.md#key_product_configurations)
product configuration includes [`intl_services`](/src/intl/intl_services), a
component that wraps this implementation.
### Runner integrations {#runner-integrations}
#### dart_runner
In the future, accessing the field `Platform.localeName` in Dart will return the
_first_ `LocaleId` from the vector `fuchsia.intl.Profile.locales`. (This is
currently blocked by a
[limitation](https://github.com/dart-lang/sdk/issues/37586) in the Dart SDK.)
#### flutter_runner
The Flutter runner on Fuchsia is [wired up][flutter-source] to
`fuchsia.intl.PropertyProvider`, so using the standard
[Flutter localization API][flutter-l10n] should allow a Flutter application to
access the current context's locale preferences and detect changes. For details
and examples, see [Localizing mods](localizing_mods.md).
Note: Both Dart and Flutter components that are built _only for Fuchsia_ have
the option of directly accessing the `fuchsia.intl.PropertyProvider` FIDL
service — in addition to using the OS-agnostic APIs. Cross-platform apps should
use the properties provided by their runtimes.
#### web_runner
The list of preferred locales from `Profile` is sent to web serves in the HTTP
request header [`Accept-Language`][accept-language]. In the future, they may
also be made available in JavaScript in
[`navigator.languages` and `navigator.language`][navigator-languages].
## Storing i18n user settings {#store}
As with other user settings on Fuchsia, internationalization settings are
modified through the
[`fuchsia.settings`](https://fuchsia.dev/reference/fidl/fuchsia.intl/index) FIDL
protocols.
Specifically, the protocol
[`fuchsia.settings.Intl`](https://fuchsia.dev/reference/fidl/fuchsia.intl/index#Intl)
is used to write and monitor internationalization-related settings.
```fidl {:.devsite-disable-click-to-copy}
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="sdk/fidl/fuchsia.settings/intl.fidl" indented_block="protocol Intl" exclude_regexp="(//.*)|(^$)" %}
```
```fidl {:.devsite-disable-click-to-copy}
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="sdk/fidl/fuchsia.settings/intl.fidl" indented_block="type IntlSettings" exclude_regexp="(//.*)|(^$)" %}
```
This protocol is intended specifically for components that require direct access
to user settings, such as a system control panel, a taskbar locale selector, or
an _implementor_ of `fuchsia.intl.PropertyProvider`. In typical Fuchsia product
configurations, this access should be restricted to a narrow allowlist.
Most client components will instead use the [read-only view](#access) offered
through `fuchsia.intl.PropertyProvider`.
### Implementation: `setui_service` {#setui-service}
The protocol `fuchsia.settings.Intl` is implemented by the
[`setui_service`](/src/settings/service) (along with the other protocols under
`fuchsia.settings`). This service serves as the backend for settings UIs in
Fuchsia products.
<!--xrefs-->
[flutter-source]: https://cs.opensource.google/flutter/engine/+/master:shell/platform/fuchsia/flutter/engine.cc;?q=%5Cbintl_property_provider_%5Cb
[flutter-l10n]: https://flutter.dev/docs/development/accessibility-and-localization/internationalization
[accept-language]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language
[navigator-languages]: https://developer.mozilla.org/en-US/docs/Web/API/NavigatorLanguage/languages