intl-elements v0.0.1-alpha.19
intl-elements
Warning This project is under active development. Custom element APIs may change.
intl-elements is a set of custom elements that make it easy to use the
Intl APIs,
a set of built-in browser APIs, in a declarative way.
For example, instead of:
const dn = new Intl.DisplayNames('ja', {type: 'language'});
dn.of('en-US'); // => '英語 (米国)'
dn.of('zh'); // => '中国語'You can write:
<intl-displaynames locales="ja" option-type="language">
<p>
<intl-displaynames-of>
<data value="en-US">English (US)</data>
</intl-displaynames-of>
</p>
<p>
<intl-displaynames-of>
<data value="zh">Chinese</data>
</intl-displaynames-of>
</p>
</intl-displaynames>
<script type="module">
import {
defineIntlDisplayNamesElements,
} from 'https://esm.sh/intl-elements?bundle';
defineIntlDisplayNamesElements();
</script>Install
Install this package with NPM or Yarn:
npm install intl-elements
# yarn add intl-elementsOr in browsers:
<script type="module">
import {defineIntlElements} from 'https://esm.sh/intl-elements?bundle';
</script>Use
Define custom elements
The package doesn’t automatically define all the custom elements, but it provides multiple functions to do so.
You can define them all at once:
import {defineIntlElements} from 'intl-elements';
defineIntlElements();Or you can define them individually:
import {
defineIntlCollatorElements,
defineIntlDateTimeFormatElements,
defineIntlDisplayNamesElements,
defineIntlListFormatElements,
defineIntlLocaleElements,
defineIntlNumberFormatElements,
defineIntlPluralRulesElements,
defineIntlRelativeTimeFormatElements,
defineIntlSegmenterElements,
} from 'intl-elements';
defineIntlCollatorElements();
defineIntlDateTimeFormatElements();
defineIntlDisplayNamesElements();
defineIntlListFormatElements();
defineIntlLocaleElements();
defineIntlNumberFormatElements();
defineIntlPluralRulesElements();
defineIntlRelativeTimeFormatElements();
defineIntlSegmenterElements();Provider elements and consumer elements
When using the Intl APIs in JavaScript, you always need to create an instance
of one of the constructors with a list of locales and options, then call a
method on that instance to do what you need. For example:
// Creates an instance of `Intl.NumberFormat`…
const nf = new Intl.NumberFormat(
// … with a list of locales…
['en-JP', 'en'],
// … and options.
{style: 'currency', currency: 'JPY'}
);
// Calls a method on the instance to format the number 100000.
nf.format(100000);intl-elements translate these as such:
- constructor → provider element, e.g.
<intl-numberformat>- locale list →
localesattribute of provider elements (there are other ways to specify locales, which we’ll talk about later) - options →
option-*attributes of provider elements, e.g.option-style="currency"
- locale list →
- methods → consumer elements, e.g.
<intl-numberformat-format>- method arguments → it varies but in most cases,
<data>or<time>elements as children of consumer elements
- method arguments → it varies but in most cases,
Take the above JavaScript example, we can achieve this with intl-element:
<intl-numberformat locales="en-JP en" option-style="currency"
option-currency="jpy">
<intl-numbeformat-format>
<data value="100000">Ten thousand Japanese yen</data>
</intl-numberformat-format>
</intl-numberformat>The naming convention of provider elements is intl-{constructor}, and consumer
elements’ is intl-{constructor}-{method}.
Consumer elements don’t have to be descendants of their corresponding provider
elements, you can use the provider attribute to link them together.
<intl-relativetimeformat id="rtf" locales="th-u-nu-thai">
</intl-relativetimeformat>
<p>
{{message('Last seen:')}}
<intl-relativetimeformat-format provider="rtf">
<data slot="rtime" value="-10">10</data>
<data slot="unit" value="days">days</data>
ago
</intl-relativetimeformat-format>
</p>
<p>
{{message('Next available:')}}
<intl-relativetimeformat-format provider="rtf">
In
<data slot="rtime" value="5">5</data>
<data slot="unit" value="days">days</data>
</intl-relativetimeformat-format>
</p>Locale list
All provider elements need a list of locales to create their Intl instances.
It’s perfectly fine to only specify one locale, that’s likely the most common
case. If multiple locales are specified, they will be passed into the Intl
constructor for
locale identification and negotiation.
This could be useful if you support non-common locales and some of them may not
be supported by all browsers. So that the list provides a fallback mechanism.
There are 4 ways to specify the locale list, and they are prioritized in the following order:
localesattribute on the provider element.The
localesattribute is a space-separated list of BCP 47 locale identifiers.langattribute on the provider element.If the provider element doesn’t have
localesattribute, it will use thelangattribute, if exists, as the locale list. Although you can only specify one locale.The
<intl-locale>elements that are referenced by thelocales-fromattribute on the provider element.If the provider element doesn’t have
localesattribute orlangattribute, it will look for the<intl-locale>elements that are referenced by thelocales-fromattribute. Thelocales-fromattribute is a space-separated list of IDs of<intl-locale>elements. The locale list is the concatenation of thevalueproperties, which areIntl.Localeobjects, of the referenced<intl-locale>elements.<intl-locale id="locale1" tag="en"></intl-locale> <intl-locale id="locale2" tag="en" option-region="us"></intl-locale> <intl-displaynames locales-from="locale1 locale2"></intl-displaynames>The locale list of the
<intl-displaynames>element is['en', 'en-US'].An acenstor element of the provider element.
If the provider element doesn’t have
localesattribute,langattribute, orlocales-fromattribute, it will look for the closest<intl-locale>element or the closest element withlangattribute.<intl-locale tag="ja"> <intl-numberformat></intl-numberformat> </intl-locale>The locale list of the
<intl-numberformat>element is['ja'].<div lang="zh-Hant"> <div> <intl-numberformat></intl-numberformat> </div> </div>The locale list of the
<intl-numberformat>element is['zh-Hant'].This allows you not to explicitly specify any locales for all the intl elements if all of their locales are the same as the document’s language, i.e. the
langattribute of the<html>element.
The provider elements also observe the DOM changes to make sure its locale list
is always up to date, e.g. if you move a provider element that doesn’t have
locales, lang, or locales-from attributes from one <intl-locale> element
into another, its locale list will be updated accordingly.
localeList property
All provider elements have a localeList property, which is a DOMTokenList.
It is an exact reflection of value of the locales attribute. The localeList
is read only, and works the same way as classList, relList, etc. You can
refere to
DOMTokenList’s MDN documentation
for more details.
Other than the methods that are available on classList, e.g. add(),
remove(), toggle(), replace(), localeList also supports supports(),
which is part of the DOMTokenList interface (relList supports it).
supports() can be used to check if a locale is supported in the current
provider element. Note that different provider elements could support different
sets of locales, and the result of supports() depends on which provider
element you are getting the localeList property from. Internally, supports()
uses the supportedLocalesOf() method of the Intl constructor.
Note If the user agent doesn’t support
Intl.<Constructor>.supportedLocalesOf(),supports()always returnstrue.
Attributes
All attribute names are in lowercase. For provider elements, they useoption-*
attributes to specify options for their Intl instances. These attributes are
always in one word after “option-”, e.g. option-style,
option-numberingsystem.
All attribute values are case-insensitive. For example, the type option of
Intl.DisplayNames constructor has dateTimeField as one of its possible
values, but you can use datetimefield or DATETIMEFIELD as the value of
option-type attribute.
Some option attributes only accept a limited set of values. For example, the
option-calendar attribute only accepts buddhist, chinese, coptic, etc.
The provider elements use Intl.supportedValuesOf()
to verify if a value is valid before updating its consumer elements. If a value
is invalid, the consumer elements will not be updated/rendered.
Note If the user agent doesn’t support
Intl.supportedValuesOf(), the provider elements doesn’t verify if a value is valid.
Styling
Although the consumer elements have Shadow DOM, the content is usually plain
text, so it’ll inherit CSS styles like colors and fonts. However, if you do want
to style some of the internal components, you can use CSS Parts. All consumer
elements have a part named value as the container element, usually, it’s a
<span> element. Also when you use a “format to parts’ consumer element, e.g.
<intl-datetimeformat-formattoparts>, each formatted part is usually inside its
own container element and has its own CSS part, e.g year, month, day, etc.
The format**ToParts methods of Intl APIs return an array of objects, each
object represents a formatted part, and has a type property and optionally
a source property. The values of these 2 properties are used as the CSS part
names. If a value is in camel case, it’ll be converted to kebab case when used
as a CSS part name. For example, relatedYear → related-year. In the case of
<intl-segmenter-segment>, each part has the name segment, and if a segment
has isWordLike as true, its corresponding part element also has wordlike
as one of its part names.
intl-datetimeformat-formattoparts::part(related-year) {
font-weight: 700;
}
intl-segmenter-segment::part(segment) {
background-color: lemonchiffon;
padding-inline: .25rem;
}Refer to the API reference documents below for all parts.
Accessibility
The intl elements look a bit verbose in HTML code, but their existence is
invisible to the accessibility tree. Most elements have their content as
plain text, so screen readers can read through it with no problems. For all the
**toparts elements and intl-segmenter-segment element, due to their purpose,
they wrap content into many <span> elements in the Shadow DOM, this could make
screen readers read the content span by span. So these elements mark the
content container element with aria-hidden="true", and use another visually
invisible element to provide content for screen readers.
Additional features
Because the content locale of an intl element could be different from the
HTML document’s locale, the content container elements in all the consumer
elements’ Shadow DOMs have their own lang and dir attributes. Their values
are determined by the provider’s current active locale, this locale itself
is lang’s value, and it uses new Intl.Locale(locale).textInfo.direction
to check if this locale’s language is RTL, if so add dir="rtl" attribute,
otherwise, remove the dir attribute.
Polyfills
Not all browsers support all the Intl APIs, you can find the current support
from <caniuse.com/?search=intl>. You can use
@formatjs’s polyfills to provider better
cross-browser support.
import {defineIntlDisplayNamesElements} from 'intl-elements';
import '@formatjs/intl-displaynames/polyfill';
import '@formatjs/intl-displaynames/locale-data/en';
defineIntlDisplayNamesElements();API references
intl-localeintl-collatorintl-colaltor-compare
intl-datetimeformatintl-datetimeformat-formatintl-datetimeformat-formattopartsintl-datetimeformat-formatrangeintl-datetimeformat-formatrangetoparts
intl-displaynamesintl-displaynames-of
intl-listformatintl-listformat-formatintl-listformat-formattoparts
intl-numberformatintl-numberformat-formatintl-numberformat-formattoparts
intl-pluralrulesintl-pluralrules-select
intl-relativetimeformatintl-relativetimeformat-formatintl-relativetimeformat-formattoparts
intl-segmenterintl-segmenter-segment
Further readings
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago