iLib
Our i18n
library relies on the wonderful iLib library. It provides many useful tools for things like string translation, date/time formatting, number parsing, etc.
You must install iLib
as a dependency in your application to use the i18n
module:
npm install ilib@^14.2.0
We will cover a good deal of how to use iLib
inside your Enact
app below, but we will not cover everything.
If you wish to learn more about ilib
checkout their github and docs.
Accessing iLib from Enact
You can use iLib like this:
import DateFmt from 'ilib/lib/DateFmt';
const formatter = new DateFmt({
date: 'dmwy',
length: 'full',
timezone: 'local',
useNative: false
});
Translating Strings
ResBundle
ilib/ResBundle
, the resource bundle class, represents a set of translated
strings. Each app has its own resource bundle. These bundles are loaded
dynamically, with each one having a name and locale.
The locale may be specified as an option in the constructor.
import ResBundle from 'ilib/lib/ResBundle';
...
const rb = new ResBundle({locale: "ko-KR"});
In practical terms, ResBundle
’s most important method is getString()
.
const str = toIString("My Label");
The actual data contained in the bundle is stored under the application’s
resources
directory. Within resources
is a hierarchy of subdirectories
named for locales. iLib
reads translated strings from strings.json
files
found in these directories.
In the layered structure of the locale directories, values from deeper levels override those from nearer the surface, as in the following example:
resources/
en/
strings.json - shared strings for all English
appinfo.json - application description
CA/
strings.json - only strings special to Canada
GB/
strings.json - only strings special to Great Britain
For the en-GB
locale, if a string value is defined in both
/resources/en/strings.json
and /resources/en/GB/strings.json
, the value from
the latter (more-specific) file will override the value from the former file.
It’s worth noting that, in addition to strings, other localized files (such as
appinfo.json
) may also be placed in these hierarchical directories, with their
data following the same rules of precedence. In the case of appinfo.json
, the
locale-specific files will typically include values for “title”, “keywords”, and
“description”. The other properties will keep the values inherited from the
app’s top-level appinfo.json
.
$L()
is a convience method for ResBundle
, it is described in the main section of the i18n
docs here
Using iLib for formatting
String Formatting
ilib/IString
is used to format strings. You will not generally need to require IString
directly to use it. Its format()
method allows for
interpolation of named parameters into the string. The following syntax is
recommended:
import {toIString} from 'i18n/$L'
const template = toIString("There are {n} objects.");
const str = template.format({n: 15});
str
now has the value "There are 15 objects."
Note that we are populating template
by calling toIString()
on the
localized resource bundle $L.rb
. This is because format()
accepts an
ilib/IString
object, but not an intrinsic JavaScript string. (A call to
toIString()
on a resource bundle returns an instance of ilib/IString
, while
a call to $L()
returns an intrinsic JavaScript string.)
ilib/IString
has the same methods as an intrinsic string, and in many cases may
be used as a substitute. For those places that require an intrinsic string, you
must call the toString()
method to convert the ilib/IString
to an intrinsic
string.
Handling Plurals
ilib/IString
uses the formatChoice()
method to handle plurals. This allows
translators to adjust strings to handle plurals properly for their respective
languages.
const number = 3;
const template = toIString( "0#There are no objects.|1#There is 1 object.|#There are {n} objects.");
const str = template.formatChoice(number, {n: number});
str
now has the value "There are 3 objects."
formatChoice()
also supports number classes (“zero”, “one”, “two”, “few” and
“many”) for languages with complex rules for pluralization, such as Russian or
Serbian.
const template = toIString( "0#There are no objects.|few#There are a few ({n}) objects.|#There are many objects. ({n})");
Dates and Times
The formatting of dates and times can differ widely from one locale to the next:
------------------------------------
Locale Format
--------- --------------------------
en-US Mo 11/12/2012 2:30pm
en-CA Mo 12/11/2012 2:30 PM
de-DE 14:30 Mo 12.11.2012
zh-CN 2012-11-12周一下午2:30
it-IT Lu 12/11/2012 14.30
------------------------------------
In iLib
, the ilib/DateFmt
class is used to format dates and times. The
constructor accepts various options, which control how the formatter behaves.
Once you create a DateFmt
instance, you may call its format()
method as many
times as you want to format dates according to the given set of options.
import DateFmt from 'ilib/lib/DateFmt';
...
const fmt = new DateFmt();
const d = fmt.format(date);
Among the options you may specify are the following:
- Which locale to use
- Whether to format the date only, time only, or both date and time together
- Which components of the date or time to format (e.g., only format the month and year components of the date)
- Whether to use a 12-hour clock, a 24-hour clock, or the default clock for the locale
- Whether to use short, medium, long, or full-length text for components that use words (e.g., Sunday may be expressed as “S”, “Su”, “Sun”, or “Sunday”)
- Which time zone to format for
const fmt = new DateFmt({ locale: "tr-TR",
type: "date", date: "dmy", timezone: "Europe/Istanbul"
});
Calendar Dates
iLib
also supports the formatting of dates in multiple calendaring systems,
with the default being the familiar Gregorian calendar.
To create a date, you may call the factory method or use the calendar date directly, e.g.:
import HebrewDate from 'ilib/lib/HebrewDate';
...
const now = new HebrewDate();
This is equivalent to the following factory method call:
import dateFactory from 'ilib/lib/DateFactory';
...
const now = dateFactory({type: "hebrew"});
Dates may be converted between calendars via a “Julian Day” number. A Julian Day is the number of whole days and fractions of a day since the beginning of the epoch on 24 November -4713 BCE (Gregorian):
const now = dateFactory();
// now.year is currently 2013
const jd = now.getJulianDay();
const hebrewDate = new HebrewDate({julianday: jd});
// hebrewDate.year is 5773
To format a date in a non-Gregorian Calendar, follow the pattern of creating a
DateFmt
object and calling format()
on it.
const fmt = new DateFmt({
length: "full",
locale: "en-US",
calendar: "hebrew"
});
const d = fmt.format(date);
The value of d
is "Adar 27, 5773 11:47PM PDT"
.
Use ilib/CalendarFactory
as a factory method to
create the other calendar types.
import calendarFactory from 'ilib/lib/CalendarFactory';
const cal = calendarFactory({
// looks up calendar for this locale
locale: "nl-NL"
});
const days = cal.getMonLength(2, year);
days
is 28
in regular years and 29
in leap years.
Ranges and Durations
ilib/DateRngFmt
may be used to format a date/time range—a period of time with
a specific start point and end point. As with the other formatter classes, the
final output (e.g., 'Mar 11-14, 2013'
) will depend on the options supplied to
the formatter.
Similarly, ilib/DurationFmt
lets you format durations—how long things take to
happen. Again, you may customize the output (e.g., '36 hours, 24 minutes, and
37 seconds'
) by setting the formatter’s options.
Time Zones
In many countries, the national government determines the time zone. In some
countries, including the United States, this may be overridden by smaller
jurisdictions such as states/provinces, counties, towns, etc. Time zones are
specified using the IANA convention of “continent/city” (e.g.,
'America/Los_Angeles'
or 'Asia/Seoul'
).
ilib/TimeZone
represents information about a particular time zone. Instances
may be passed to other classes such as ilib/DateFmt
, although the specifier
string itself is also accepted.
import TimeZone from 'ilib/lib/TimeZone';
...
const tz = new TimeZone({
id: "America/Los_Angeles"
});
const offset = tz.getOffset(dateFactory());
offset
is now {h: -8, m: 0}
.
Numeric Values
The formatting of numeric values—in numbers, currency, and percentages—is another locale-sensitive process.
--------------------------------------------------------
Locale Float Currency Percentage
--------- --------------- -------------- ---------------
en-US 1,234,567.89 $1,234.56 57.2%
de-DE 1.234.567,89 1.234,56 € 57,2 %
fr-FR 1 234 567,89 1 234,56 € 57,2%
tr-TR 1.234.567,89 1.234,56 TL % 57,2
--------------------------------------------------------
As shown in the following examples, iLib
handles each of these cases using
ilib/NumFmt
.
Numbers
import NumFmt from 'ilib/lib/NumFmt';
...
const fmt = new NumFmt({
locale: "de-DE"
});
const str = fmt.format(1234567.89);
str
is now '1.234.567,89'
.
Currency
const fmt = new NumFmt({
style: "currency",
currency: "EUR",
locale: "de-DE"
});
const amount = fmt.format(1234.56289);
amount
is now '1.234,56 €'
.
Percentages
const fmt = new NumFmt({
style: "percentage",
maxFractionDigits: 2,
locale: "tr-TR"
});
const percentString = fmt.format(0.893453);
percentString
is now '% 89,34'
.