

FormatJS – Internationalize your web apps on the client and server - juandopazo
http://formatjs.io/

======
cj
Shameless plug: I've been working on a similar project for about a year now
called Localize.js ([https://localizejs.com](https://localizejs.com)), with
the goal of automating the entire process of internationalization /
localization. It works by scanning the DOM for translatable content, and
injecting translations on-the-fly after the page loads (this happens so
quickly that the user never sees the text in the original language).

~~~
nawitus
>It works by scanning the DOM for translatable content, and injecting
translations on-the-fly after the page loads

I don't like this approach because it makes the framework/library harder to
integrate with other DOM-modifying frameworks like data binding frameworks
that are very popular these days. A more modular approach in my opinion would
be to simply provide a function/functions to do the localization conversions.
That can easily be integrated to any data binding framework.

>(this happens so quickly that the user never sees the text in the original
language)

And if you use it with something like AngularJS, the end result is visual
flicker after DOM changes..

~~~
cj
Localize.js is fully compatible with all DOM-modifying frameworks (Backbone,
Angular, etc). Localize.js doesn't actually replace existing DOM elements,
rather it simply changes the existing elements' contents as to not interfere
with bindings.

We've also spent _a ton_ of time making sure there's zero visual flicker as
the DOM changes take place. We have a bunch of companies using it to translate
Angular and Backbone apps (for example,
[http://venzee.com/](http://venzee.com/) and
[https://www.verbling.com](https://www.verbling.com)).

~~~
nawitus
But when the contents change, how does Localize.js know to translate the DOM
again? If you need to call something like 'Localize.translatePage', then you
need to track the changes yourself, which is not the correct way. I noticed
that one can bypass the DOM modifying by calling Localize.translate to
directly translate text, which is what I would do with Angular.JS. I'd just
write a simple directive which uses the Localize.js function to translate
text.

~~~
cj
We use MutationObserver ([https://developer.mozilla.org/en-
US/docs/Web/API/MutationObs...](https://developer.mozilla.org/en-
US/docs/Web/API/MutationObserver)) to detect when content on the page changes,
so when you add (or change) a <div> on your page, we're able to immediately
translate the new content, on-the-fly, as it's being inserted into the DOM.

Our goal with Localize.js is to make everything completely "plug and play",
and require as little extra development work as possible. We're hoping this
will make localization more accessible to startups / companies who don't have
weeks or months to spend manually internationalizing their application.

~~~
uptown
Could implementing things this way cause a problem if another script also
post-processed your pages? Could you wind up in a loop where each script kept
modifying the page in response to the changes made by the other script?

~~~
cj
Technically, it's possible. To get stuck in a loop you'd have to have another
script that uses MutationObserver to reverse the changes that Localize.js
makes to the DOM. We haven't run in to this yet, but I'll see if there's a way
to safeguard against this.

------
mikewhy
Are there any advantages over i18n-js[1]? Can't say I'm a huge fan of this
method of pluralization:

    
    
        Cart: {itemCount} {itemCount, plural,
            one {item}
            other {items}
        }
    

[1]: [https://github.com/fnando/i18n-js](https://github.com/fnando/i18n-js)

~~~
drewfish
Yeah, for a simple plural that can be a bit longer. In other languages,
though, the pluralization rules get rather complicated[1]. (For example,
Arabic has both complicated pluralization rules -and- a lot of people who
speak it.)

The strength of the ICU message format, in my mind, is that the messages can
be "nested" so that the translation can be customized for multiple concerns
(plural, gender, whatever).

Also, with the integrations (dust, handlebars, react) the details of
translation and display of data lives in the message format and/or template.
This is the "view layer", and means that your controller/code isn't littered
with a bunch of calls to a translation library.

[1] [http://unicode.org/repos/cldr-
tmp/trunk/diff/supplemental/la...](http://unicode.org/repos/cldr-
tmp/trunk/diff/supplemental/language_plural_rules.html)

~~~
mikewhy
Nice, I didn't know that about Arabic, and the many other languages.

Though i18n-js does let you write your own pluralizations rules (taken from
the readme), while supporting zero/one/many out of the box:

    
    
        I18n.pluralization["ru"] = function (count) {
          var key = count % 10 == 1 && count % 100 != 11 ? "one" : [2, 3, 4].indexOf(count % 10) >= 0 && [12, 13, 14].indexOf(count % 100) < 0 ? "few" : count % 10 == 0 || [5, 6, 7, 8, 9].indexOf(count % 10) >= 0 || [11, 12, 13, 14].indexOf(count % 100) >= 0 ? "many" : "other";
          return [key];
        };
    

I've posted an example below, but I don't consider `@div null,
@t('welcomeMessage', { username })` "littering" my code.

~~~
drewfish
There are a few NPM libraries (make-plural, cldr, probably others) which will
help you write those pluralization functions. The CLDR data does get updated
from time to time, so it's nice to rely on another package to trace those
changes.

------
MaBu
How does it compare to normal "Gettext workflow"?

Currently I use i18next with custom functions, where I just write strings like
_("car"), ngettext("window", "windows", number) in files. I Can use translator
comments, context and everything. (Like //TRANSLATORS: this is used in this
way etc.) [https://www.gnu.org/software/gettext/manual/html_node/PO-
Fil...](https://www.gnu.org/software/gettext/manual/html_node/PO-Files.html)
Babel extracts all translatable strings to POT file. I Translate POT file to
my wanted languages PO files with GUI tool of my choice. Then PO file is
converted to json and used in translations with i18next library. When New
translations are added I just rerun Babel new translations are added, are
retranslated if needed and converted to JSON. I looked into a lot of JS
libraries and extractors and these was the only one that supported Plurals,
context, translator comments, etc.

I looked into Mozilla's L20 which seems nice. But there is no GUI translation
editor. You have to find all translatable strings yourself etc. End it seems
it's the same here.

One better things is that with FormatJS I wouldn't need moment.js for date
localization.

~~~
caridy
The workflow is, for now, out of the scope of this project, we assume
developers will figure how to produce a javascript object that contains
key=value pairs, where each value is a message written in ICU message syntax,
and where values are feed into the template engine for helpers/methods to use
them.

Internally at Yahoo (just like facebook, and other big companies), we have an
infrastructure for translation that works based on a source file written in
english by developers, and the whole thing just work. But we have no plans to
open up any of that. We believe, such system will grow from the community once
people realize that ICU is good enough to internationalize their apps.

As for moment.js, you're right, if you will never need to parse a date, or
massage a date value, and the only thing you care about is to format a
timestamp that is coming from an API, then `formatRelative` helper should be
good enough.

------
gear54rus
A related thought: I wonder if we will some day live in a world where
translation is not required. Where everyone knows English and has no trouble
using English tools and consuming English content.

Same goes for measurement systems (metric), time, currency, and other formats.
I reckon it would simplify our lives greatly and spare us the trouble of
dealing with 1000s of encodings, multi-byte strings and different text
directions.

Technological landscape is the only place where such unity between nations is
possible, I tend to think that this is what should be pursued instead of
translate-everything-everywhere approach.

~~~
sambeau
What makes you think it won't be Chinese?

~~~
gear54rus
The fact that English is easier to learn :)

~~~
Ralfp
It's only "fact" because you are accustomed to latin alphabet and you are
native speaker of language that was either latin-influenced, shares root with
English, or you were taught it since you were child.

The last one actually is sometimes called "little chinese boy" in circles of
language tutors due to how young children can easily pick up languages
different from their native one due to lack of bias. My cousin thats 5 years
old now is already dual-languaged due to her parents exposing her to their
native languages at all times.

~~~
gear54rus
My original point also has a lot to do with single-byte encoding(s), as you
may have noticed. Will we really want to change ASCII to encode something
different than it does now? Will we throw away all our programming languages
as well? I don't think so.

------
caridy
The official announcement is now live:

[http://yahooeng.tumblr.com/post/100006468771/announcing-
form...](http://yahooeng.tumblr.com/post/100006468771/announcing-format-js-
internationalize-your-web-apps-on)

hopefully it will help to clarify few of the questions...

~~~
BruceM
Any thoughts on how hard this might be to integrate with Polymer (or web
components in general)?

------
alexchamberlain
> 10/14/2014 English (US) > 14/10/2014 French

Pedantic Englishman here: 14/10/2014 is used all over Europe, including
England. If only we could persuade the world to use an international format:
2014-10-14, for example.

~~~
pimlottc
I like using "14 Oct 2014" to avoid any ambiguity. 2014-10-14 is good too but
it comes off a bit technical, like dropping scientific notation in an everyday
message.

------
nawitus
I'm currently using Moment.js, i18next and Numeral.js with AngularJS. I wonder
how FormatJS compares with this. At least one benefit FormatJS could have is
having a unified collection of translation files.

~~~
caridy
the main benefit of formatjs is that it offers a declarative syntax at the
template level, which simplifies things drastically. we don't have an
integration for AngularJS or Ember just yet, but we are planning to do so very
soon.

------
macca321
I would be interested to know if it offers anything over Globalize

[https://github.com/jquery/globalize](https://github.com/jquery/globalize)

~~~
drewfish
Hmmm... after very quickly looking at Globalize, I'd say there are two things
about formatjs.io that I see as main differences:

* Integrations with Handlebars, Dust, and React hopefully make formatjs.io easy to use (since people are already using one of these).

* Focus on the ICU message format, which is fairly simple yet fairly expressive. (Professional translators should hopefully be familiar with this syntax, and it's actually fairly straightforward for us engineers to use.)

One thing that looks interesting (to me) about Globalize is the way the
latest/freshest CLDR data is loaded.

------
mrmch
Super shameless plug, but easy internationalization for email is something
we've added to Sendwithus. We're working with multiple partners on it (sample
at
[https://www.sendwithus.com/translations](https://www.sendwithus.com/translations)),
but are still looking for more beta users of the feature.

------
grakic
This may be offtopic, but is there an editor for translating ICU messageformat
strings?

There are many tools to extract gettext strings from source and editors to
translate PO catalogs, but I do not know of any that works with ICU
messageformat syntax.

------
joshdance
Looks great. One thing that wasn't clear, can you provide your own
translations or is it all machine translation?

~~~
juandopazo
Hi! There are helpers for dealing with dynamic content like numbers and dates,
but also there's message formatting for when you have your own translations
like "I have {numCats, number} cats.". Here's the explanation for the
Handlebars integration: [http://formatjs.io/guide/#messageformat-
syntax](http://formatjs.io/guide/#messageformat-syntax).

