in Polish istnieją 0 produkty w koszyku. istnieje 1 produkt w koszyku. istnieją 2 produkty w koszyku. istnieją 3 produkty w koszyku. istnieją 4 produkty w koszyku. istnieje 5 produktów w koszyku
Adding a new language means editing the entire codebase. Translations take time. This means several edits. Merge conflicts with every piece of the codebase that has user- visible text.
MessageFormat (or gettext, or ...) Push the list of cases out into each translation. Polish specifics go in the Polish language files. Programmers see only one string in the source code.
Polish (as line-wrapped JSON) { "cart": { "items": "{items, number, one {znajduje się # produkt w koszyku.} few {istnieją # produkty w koszyku.} many {istnieje # produktów w koszyku}}" } }
Decide on a definitive source translation. Update that, then retranslate the changed pieces in each language. Remember that you have to maintain any specialization.
app.use(function selectLanguageForRequest(req, res, next) { var lang = req.query.lang || 'en'; // Or use req.headers['Accept-Language'] // Or use the user's account settings. // Or use multiple strategies. req.messages = require(path.resolve(__dirname, 'locales', lang + '.json')) next(); });
// A trivial ‘render’ function for my component^Wapplication module.exports = function render(formatter) { document.querySelector('p').innerText = formatter.format("bag", { items: 2}); }
// Polyfills are scratchy require('intl'); require('intl/locale-data/jsonp/en.js'); require('intl/locale-data/jsonp/es.js'); var fetch = require('isomorphic-fetch'); var Promise = require('bluebird'); var Formatter = require('./formatter'); var render = require('./render');
User Interface Concerns • Finding word boundaries isn’t always easy • Japanese sentences involving imported words can be very long • German words get very long and finding good wrapping gets tricky • Arabic and 8 other currently used scripts start on the right and go left.
Culture Matters • Names don’t work everywhere the same way they do in your country. • Names don’t even work the way you think they do. • Not everyone writes numbers the same way.
Warnings Language != locale English is spoken in the US. English is spoken in the UK. But we spell colour differently and we write our dates inside out in the US. Same language, different specifics. You can call the language with the local details a "locale".
Tips for language tags If you’re parsing a language expectation from an external source, you may have more or less to the language tag than you expect. Use the bcp47 module to parse them. Use bcp47-serialize to get them as a string again. Canonicalize into a locale you support early on.
Tips for language tags Pass locale tags as opaque strings whenever possible -- it’s far easier to get right. "en-US" // Better {lang: 'en', region: 'US'} // You will make mistakes Especially once you add i-navajo and zh-CN-hanz.
Command-line apps Look at the LANG environment variable, or the pieces and parts, LC_*. How you sort, display numbers, each specifiable separately. Consider gettext format messages to be similar to apps in C.