A given value like T.fld.name
will have a javascript value that can be converted to a string and be called.
The function call will return a pure string and can take arguments.
The interpolation is done in I18nClient::interpolate
and can of course be overridden.
The interpolation engine is linear. Reg-exp like if you wish, meaning it is not a programming language but is meant to be as flexible as possible in its limitations
Everything in between {...}
is interpolated. In order to have a curly-brace in the text, those have to be doubled. Look at my left brace -> {{
.
In the braces, every character is escaped with the \
character, even the \
character
Several values can be given for defaulting:
"This is a {$1}"
will have to be called with an argument, "This is a {$1|distraction}"
may be called with an argument.
To play with interpolation giving direct values for test purpose, one can use:
client.interpolate('test', 'qwe {$1} asd {$2} zxc', 'abc', 'xyz')
ℹ️ While interpolating, the argument nr 0 is the key, the first argument is the argument nr 1. This is meant to be used by translators - literary peeps - so of course the first argument has the number "1".
The parameters (given in the code) can be accessed as such: The last parameter is the one used for naming. If a named parameter is accessed, the last (or only) parameter should be an object with named properties
$0
is the key,$1
the first argument,$2
...$arg
access the argument namedarg
$
access the last argument (the names object)
To add a default, $arg[default value]
can be used, as well as $[name: John]
Also, not an argument but a sub-translation can be made with $.text.key
Processing occurs with the ::
operator, as {process :: arg1 | arg2 | ...}
where the arguments can be:
- A string
- An flat named list in the shape
key1: value1, key2: value2
where only,
and:
are used for the syntax.The
:
character triggers the list parsing.
If the first element is a named list, the second one will be the case to take from the list.
example: {question: ?, exclamation: !, default: ... | $1}
ℹ️ The case
default
get the remaining cases and, if not specified, an error is raised if an inexistent case is given
To use another translation can be useful, when for example one translation is a number format centralization common to all languages, or when a centralized (all-language) format string needs to use conjunctions or words that are language-specific.
The syntax {other.text.key :: arg1 | arg2}
can be used to do such.
ℹ️ The presence of a
.
will tell the interpolator it's a text key and not a processor
The syntax also allow some processing specification, when a processor name (with no .
in it) is used instead of a first element. The available processors can be extended :
import { processors, type TContext } from 'omni18n';
Object.assign(processors, {
myProc(this: TContext, arg1: any, ...args: any[]) {
...
}
});
⚠️ For obvious security reasons, nevereval
a string argument
Where TContext
contains mostly the client
(the object containing all the language specification)
The arguments will mainly be strings or object when flat named lists are specified
The syntax to use them is {processor:: arg1 | arg2}
.
example: {upper :: $1}
will render the first argument in upper-case
ℹ️
{$2[upper] :: $1}
is also possible, in which case the second argument can both specify a text key, a processor or be defaulted to theupper
processor.
upper(s)
lower(s)
title(s)
: uppercase-first
number(n, opt?)
: equivalent toIntl.NumberFormat()
who receive the listopt
as optionsdate(n, opt?)
: equivalent toIntl.DateTimeFormat()
who receive the listopt
as options
A list of predefined options can be set in exported variables
import { formats } from 'omni18n'
formats.date.year = { year: 'numeric' }
formats.number.arabic = { numberingSystem: 'arab' }
const client: I18nClient = ...;
client.interpolate({key: '*', zones: [], client}, '{date::$0|year}', new Date('2021-11-01T12:34:56.789Z')); // 2021
client.interpolate({key: '*', zones: [], client}, '{date::$0|month: numeric}', new Date('2021-11-01T12:34:56.789Z')); // 11
Also, each client has a property timeZone
. If set, it will be the default timeZone
used in the options.
Its format is the one taken by Date.toLocaleString()
We of course speak about the ones hard-coded in the Intl javascript core of Node and the browsers.
relative(n, opt?)
wheren
is number+unit (ex.1month
or-2 seconds
) - just forwards toIntl.RelativeTimeFormat
. Note that there is aformats.relative
like for dates or number- relative to
Intl.DisplayNames
:region(c)
ex: 'HU' -> "Hungary"language(c)
ex: 'en-UK' -> "British English"script(c)
ex: 'Kana' -> "Katakana"currency(c)
ex: 'USD' -> "Us dollars"
list(x, y, z, ..., opt?)
where arguments can be array (this will be flattened) allows the "..., ... and ..." auto-formating - cfr.Intl.ListFormat
As Intl.DurationFormat
is not widely implemented, that the polyfill is 100k while the functionality is widely used, the library provide its own processor duration
- widely based on Intl.NumberFormat
and Intl.ListFormat
The argument takes an object describing the duration by units, eg. {years: 1, days: 4}
(with or without ending 's')
The units are: year
, month
, week
, day
, hour
, minute
, second
, millisecond
, microsecond
, nanosecond
The behavior is standard if the provided numbers are integers, in-bounds (eg. no more than 59 seconds) or if the option calculate
is specified as false
Ex: Giving the argument { hours: 1200.5678 }
to the processor can produce 7 weeks, 1 day, 34 minutes, and 4 seconds
showZeros
- The most significant (year, ...) and least significant (picoseconds, ...) zeroes won't be shown, but the ones in the middle will be if this option is set to true:"2 years, 0 months, and 1 day"
minUnit
specifies toe minimum unit until where to calculate the precision ("day", "second", ... - singular)style
:long
|short
|narrow
useWeeks
: Wether to include weeks in calculations, and display it if zero while specifyingshowZeros
calculate
:true
by default,false
will force ignoring values and just display the given arguments
These two processors use a specific key, respectively internals.plurals
and internals.ordinals
.
These key contain js-like object who, for english would be:
ordinals: {one: '$st', two: '$nd', few: '$rd', other: '$th'}
numerals: {one: '$', other: '$s'}
It can also be done by specifying internals
as a js-like object or specifying internals.plurals.one
as a string
The keywords (one
, other
, ...) come from Intl.PluralRules
.
ordinal(n)
To display "1st", "2nd", ...plural(n, spec)
:- If
spec
is a word, theinternals.plurals
rule is used ({plural::1|cat}
-> "cat",{plural::2|cat}
-> "cats"). - The specification can use the
Intl.PluralRules
(ex:{plural::$1|one:ox,other:oxen}
) - A specific case is made for languages who use
one/other
(like english) :{plural::$1|ox|oxen}
- If
Something like {upper::relative::$1|short}
means "the relative time given as parameter, presented in a short format and upper case". The composition is applied right to left.
To translate from the interpolation format to a structured language, we could say that:
a1 | a2 :: b1 | b2 :: c1 | c2
is equivalent to
a1(b1(c1, c2), b2) || a2
Where every part can contain $...
replacements