require('@jess/l10n'): { bundle, bundles, locales, setLocales, addDirectory, addProvider, providers, reload, generation, formatPattern, new, directory, files, checkPattern }Text for people comes from here, not from string literals. A script asks for a bundle once and
calls it for each message with the values the pattern names; locale fallback, plural rules, and
number formatting are the module's. It's Luau in the built-in package, over
@dream/intl's CLDR
plural rules and decimal formatting. The model, the file layout, and the grammar are in
Localization.
--!strict
local l10n = require('@jess/l10n')
l10n.addDirectory('my-package/l10n')
l10n.reload()
l10n.setLocales { 'pl-PL', 'en' }
local common = l10n.bundle 'common'
print(common('hello', { name = 'Ada' })) -- Cześć, Ada!
print(common('file_count', { count = 5 })) -- 5 plików
l10n.bundle§
l10n.bundle(name: string, fallback: string?) -> l10n.BundleThe bundle named name, declared now if no provider offered it. fallback (default en) is the
locale tried after every preferred one and its parents; passing it again changes it, and raises
the generation.
- Parameters:
name: segments of letters, digits,_, and-, joined by.(common,
jess.editor). fallback: a BCP 47 tag.
- Errors: `'bad name' isn't a bundle name: use letters, digits, '_', and '-', in segments
separated by '.'; 'english' isn't a BCP 47 locale: the given language subtag is invalid`.
l10n.bundles§
l10n.bundles() -> { string }Every bundle: the ones providers offered at the last reload and the ones scripts asked for.
l10n.locales§
l10n.locales() -> { string }The preferred locales, canonically spelled, as they were set. Empty until setLocales runs:
then only each bundle's fallback locale is used.
l10n.setLocales§
l10n.setLocales(locales: string | { string }) -> numberSets the preferred locales, most wanted first, and returns the new generation. A message is looked
up in each locale and then its parents (pt-BR, then pt) before the next locale, then in the
bundle's fallback. Every bundle resolves again; nothing recompiles. Tags are canonicalized
(pt_br is pt-BR); extensions and private-use subtags are refused.
- Errors:
'english' isn't a BCP 47 locale: ...; `locales must be a list of strings, not
number`.
--!strict
local l10n = require('@jess/l10n')
local common = l10n.bundle 'common'
l10n.setLocales 'de-AT'
print(common('hello', { name = 'Ada' })) -- Hallo, Ada! (from de.toml)
l10n.addDirectory§
l10n.addDirectory(path: string, name: string?) -> ()Adds l10n.directory(path, name) as a provider: a folder of <bundle>/<locale>.toml files,
named name (default: the path) in diagnostics and provenance. Nothing is read until the next
reload.
l10n.addProvider§
l10n.addProvider(provider: l10n.Provider) -> ()Adds a provider. Providers added later take precedence: where two define the same message of the
same bundle and locale, the later one's text wins, message by message. Nothing is read until the
next reload.
l10n.providers§
l10n.providers() -> { string }The providers' names, lowest precedence first.
l10n.reload§
l10n.reload() -> ({ l10n.Diagnostic }, l10n.Report)Reads every provider again, compiles every pattern of every locale, and swaps in each bundle that loaded without an error. A bundle with an error keeps its last good generation, whole, so a broken edit never blanks the application; a bundle with none yet takes what did compile. A provider that can't list its files stops the reload: nothing changes. Returns what was wrong (an empty list is a clean reload) and a report.
--!strict
local l10n = require('@jess/l10n')
for _, problem in l10n.reload() do
print(tostring(problem))
end
l10n.Diagnostic§
type l10n.Diagnostic = { severity: 'info' | 'warning' | 'error', kind: string, detail: string, generation: number, provider: string?, path: string?, line: number?, column: number?, bundle: string?, locale: string?, message: string? }One problem. kind is a code: pattern-parse-failure, bundle-parse-failure,
duplicate-message, provider-collision, invalid-locale, invalid-bundle-name,
provider-failure, empty-bundle (a warning: a bundle nobody has files for), and, from
tryFormat, missing-message, missing-argument, and unsupported-argument-type. line and
column point at the message id, or, for a pattern that doesn't compile, at the place in the
pattern. tostring gives one line a log can print:
error[pattern-parse-failure] common/en.toml:1:40 (provider inline): common/en 'broken': at offset 29: 'othr' isn't a plural case; use =n, zero, one, two, few, many, or other
l10n.Report§
type l10n.Report = { generation: number, updated: { string }, kept: { string } }The generation after the reload, the bundles swapped in, and the bundles that kept their last good generation.
l10n.generation§
l10n.generation() -> numberThe generation: it rises whenever a reload swaps a bundle in, the preferred locales change, or a bundle's fallback does, which is whenever text a script cached may be stale.
l10n.formatPattern§
l10n.formatPattern(locale: string, pattern: string, values: l10n.Values?) -> stringCompiles and formats a pattern that isn't in any bundle, with locale's rules: for tools and for
text that arrives at runtime. It compiles on every call.
- Errors:
the pattern doesn't compile: at offset <n>: <why>, and the argument errors of
calling a bundle.
l10n.new§
l10n.new() -> l10n.RegistryA registry of its own, apart from the runtime's: bundle, bundles, locales, setLocales,
addDirectory, addProvider, providers, reload, generation, and formatPattern, as methods.
--!strict
local l10n = require('@jess/l10n')
local polish = l10n.new()
local pattern = '{count, plural, one {# plik} few {# pliki} many {# plików} other {# pliku}}'
print(polish:formatPattern('pl', pattern, { count = 22 })) -- 22 pliki
l10n.directory§
l10n.directory(root: string, name: string?) -> l10n.ProviderA provider over a folder on the host, <root>/<bundle>/<locale>.toml, read through @dream/fs.
It reads only under root.
l10n.files§
l10n.files(name: string, contents: { [string]: string }, prefix: string?) -> l10n.ProviderA provider over a table of /-separated paths to text, under prefix (l10n, say; omit it for
the top): the shape of a virtual filesystem, or of bundles a package carries as strings. The
table is read at every reload, so changing it and reloading takes the change.
l10n.Provider§
type l10n.Provider = { read name: string, read resources: () -> ({ l10n.Resource }?, string?, string?), read read: (resource: l10n.Resource) -> (string?, string?) }Anything that lists and reads bundle files. resources answers the files it offers, or nil, why,
and the path that failed; read answers one file's text, or nil and why. A l10n.Resource is
{ bundle, locale, path }, the locale as the file spells it.
l10n.checkPattern§
l10n.checkPattern(pattern: string) -> ({ { name: string, kind: 'any' | 'number' | 'select' } }?, string?)The arguments a pattern names and how it uses each, or nil and why it doesn't compile: for a tool that checks translations before anything loads them.
l10n.Bundle§
A bundle handle. Call it, or call format on it; tostring(bundle) is L10nBundle(common).
bundle(id, values)§
bundle(id: string, values: l10n.Values?) -> stringThe message id in the first locale of the chain that defines it, with values filled in. The
plural and number rules are those of the locale the text came from.
- Parameters:
values: a table of the names the pattern uses to strings, numbers, or
booleans ({ count = 3, name = 'Ada' }). A plural or number argument needs a number; a
select needs a string or boolean; a plain {name} takes any of the three.
- Errors:
bundle 'common' has no message 'missing' in de-AT, de, en; `bundle 'common',
message 'hello' (de): argument 'name' is missing; ... argument 'count' must be a number, not a
string; variable 'name' of message 'hello' in bundle 'common' must be a string, number, or
boolean, not table`. A missing message is never an empty string and never the id passed off as
text.
--!strict
local l10n = require('@jess/l10n')
local common = l10n.bundle 'common'
print(common('file_count', { count = 0 })) -- No files
print(common('file_count', { count = 1 })) -- 1 file
print(common('file_count', { count = 1234 })) -- 1,234 files
print(common('mode_label', { mode = 'edit' })) -- Editing
bundle:format§
bundle:format(id: string, values: l10n.Values?) -> stringThe same as calling the bundle, for code that prefers a method.
bundle:tryFormat§
bundle:tryFormat(id: string, values: l10n.Values?) -> (string?, l10n.Diagnostic?)As format, answering nil and the diagnostic instead of raising.
bundle:has§
bundle:has(id: string) -> booleanWhether some locale in the chain defines id. Formatting a message that isn't there is an error;
this is the question to ask first when a message is optional.
bundle:literal§
bundle:literal(id: string) -> string?The text of id when the chain resolves it to a pattern with no arguments, which needs no
formatting; nil otherwise.
bundle:placeholder§
bundle:placeholder(id: string) -> string[[common:missing]]: what to show in place of a message that can't be formatted, loud on purpose.
The error is still the caller's to report.
bundle:lookup§
bundle:lookup(id: string, locale: string?) -> l10n.MessageInfo?Where id's text comes from: through the chain, or, with locale, in exactly that locale with no
fallback. Nil when nothing defines it there. An l10n.MessageInfo is { locale, fallback,
provider, path, line, column, arguments }; fallback says a locale other than the first
preferred one supplied the text, and arguments are what checkPattern gives.
bundle:locales, bundle:messages, bundle:chain, bundle:fallback, bundle:generation§
The locales the bundle has files for; every message name it knows (defined or asked for), sorted; the locales a message is looked up in, in order; its fallback locale; and the generation it was last swapped in at.
bundle.name§
bundle.name: stringThe bundle's name.
l10n.Values§
type l10n.Values = { [string]: string | number | boolean }The values a message takes, by the names its pattern uses. Tables, functions, and userdata are errors naming the variable, the message, and the bundle: localization formats semantic values, so a script reduces an object to one first.