Hello tools-l10n,
Below you'll find a summary of my thought process behind the next
revision of the L20n API. I split it into three emails for your
reading convenience:
Email #1: Design goals and constraints (this email)
Email #2: Language registration
Email #3: Specifying and modifying the supported languages
Email #4: Getting translations
* * *
L20n API versions 1.0 and 2.0 (see [1] for the story behind versions
and how they map to branches) were based on a concept of a single
localization context which held the state of language negotiation and
resource loading. These contexts also had a synchronous 'get' method
for retrieving translations which in case of errors internally made use
of sync XHR calls to fetch resources in fallback languages.
For the past year of working on Gaia's l10n.js (L20n API 2.0), we have
learned a lot about what a good localization framework should look
like. Here are some high-level design criteria for API 3.0:
Design goals:
G1. when translations fail to load or format the fallback should be
graceful,
G2. apps can be available in more langs than they're bundled with,
G3. it should be easy to create multiple contexts per document.
Design constraints:
C1. user language preferences are an array in which ordering matters,
C2. resources can only be fetch asynchronously
Note that G1 has already been achieved in API 2.0 to some extent.
However, to achieve other goals and keep within the constraints, we
need to make a number of breaking changes to the API. Following is the
run-down, based on the goals that we want to meet.
Consequences:
G1. There's a chain of languages negotiated between the user's
preferred languages (see C1) and the app's available ones; I'll
call them supported languages.
When a translation in one supported language errors out, the next
supported language is fetched to fall back on; this means that
the 'get' method needs to be asynchronous.
G2. An app can register a number of languages with the l10n framework
to say 'here are the languages I'm available in'; this list can
be extended by a language package service, so the registration
needs to be asynchronous.
G3. Multiple contexts per document can still share the state of
language negotiation. It should be possible then to abstract the
language negotiation into a higher-level object.
In the rest of this thread, this higher-level object will be
called a localization environment (env for short). Envs spawn
contexts.
We end up with two classes: environment and context, which interact
with each other and which primarily use async methods. This
interaction is interesting: since all contexts share a common parent
env, we can use the env as a cache to store localization resources.
This way, creating new contexts which want to re-use resources is fast
and easy. In fact, all the information a context should have about
itself is the order of resources in which to look translations up.
Take the following for instance:
var ctx1 = env.require(['res1.{locale}.l20n', 'res2.{locale}.l20n']);
var ctx2 = env.require(['res2.{locale}.l20n', 'res3.{locale}.l20n']);
Actual localization files are fetched and stored by the env. Res2 is
fetched only once. When ctx1.get(id) is called, we first look for the
identifier in res1 and then in res2. We don't even have to store flat
AST for each context: we can simply iterate over the cached resources
in the env.
The exact API of this will be the topic of my next email. To know
which files to fetch, the env has to already have negotiated its
supported languages. This is also where things become a bit tricky :)
Stay tuned,
-stas
[1] https://groups.google.com/forum/#!topic/mozilla.tools.l10n/MJ_sMgqOKT4
--
@stas
_______________________________________________
tools-l10n mailing list
[email protected]
https://lists.mozilla.org/listinfo/tools-l10n