[feat] Update l10n lib (#3248)

Last update was from 2014.

Fix #3244.
pull/3249/head
Luiza Pagliari 2017-08-15 10:22:59 -03:00 committed by GitHub
parent 9b9d604c09
commit 6bcaa00a4b
1 changed files with 133 additions and 86 deletions

View File

@ -23,16 +23,28 @@
window.html10n = (function(window, document, undefined) { window.html10n = (function(window, document, undefined) {
// fix console // fix console
(function() { var console = window.console
var noop = function() {}; function interceptConsole(method){
var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"]; if (!console) return function() {}
var console = (window.console = window.console || {});
for (var i = 0; i < names.length; ++i) { var original = console[method]
if (!console[names[i]]) {
console[names[i]] = noop; // do sneaky stuff
if (original.bind){
// Do this for normal browsers
return original.bind(console)
}else{
return function() {
// Do this for IE
var message = Array.prototype.slice.apply(arguments).join(' ')
original(message)
} }
} }
}()); }
var consoleLog = interceptConsole('log')
, consoleWarn = interceptConsole('warn')
, consoleError = interceptConsole('warn')
// fix Array#forEach in IE // fix Array#forEach in IE
// taken from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach // taken from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
@ -136,7 +148,7 @@ window.html10n = (function(window, document, undefined) {
for (var i=0, n=this.resources.length; i < n; i++) { for (var i=0, n=this.resources.length; i < n; i++) {
this.fetch(this.resources[i], lang, function(e) { this.fetch(this.resources[i], lang, function(e) {
reqs++; reqs++;
if(e) console.warn(e) if(e) consoleWarn(e)
if (reqs < n) return;// Call back once all reqs are completed if (reqs < n) return;// Call back once all reqs are completed
cb && cb() cb && cb()
@ -180,7 +192,7 @@ window.html10n = (function(window, document, undefined) {
} }
// dat alng ain't here, man! // dat alng ain't here, man!
if (!data[lang]) { if (!data) {
var msg = 'Couldn\'t find translations for '+lang var msg = 'Couldn\'t find translations for '+lang
, l , l
if(~lang.indexOf('-')) lang = lang.split('-')[0] // then let's try related langs if(~lang.indexOf('-')) lang = lang.split('-')[0] // then let's try related langs
@ -208,12 +220,12 @@ window.html10n = (function(window, document, undefined) {
return return
} }
if ('object' != typeof data[lang]) { if ('object' != typeof data) {
cb(new Error('Translations should be specified as JSON objects!')) cb(new Error('Translations should be specified as JSON objects!'))
return return
} }
this.langs[lang] = data[lang] this.langs[lang] = data
// TODO: Also store accompanying langs // TODO: Also store accompanying langs
cb() cb()
} }
@ -231,6 +243,17 @@ window.html10n = (function(window, document, undefined) {
html10n.rtl = ["ar","dv","fa","ha","he","ks","ku","ps","ur","yi"] html10n.rtl = ["ar","dv","fa","ha","he","ks","ku","ps","ur","yi"]
/**
* Language-Script fallbacks for Language-Region language tags, for languages that
* varies heavily on writing form and two-part locale expansion is not feasible.
* See also: https://tools.ietf.org/html/rfc4646 (RFC 4646)
*/
html10n.scripts = {
'zh-tw': 'zh-hant',
'zh-hk': 'zh-hant',
'zh-cn': 'zh-hans'
}
/** /**
* Get rules for plural forms (shared with JetPack), see: * Get rules for plural forms (shared with JetPack), see:
* http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html * http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html
@ -635,7 +658,7 @@ window.html10n = (function(window, document, undefined) {
// return a function that gives the plural form name for a given integer // return a function that gives the plural form name for a given integer
var index = locales2rules[lang.replace(/-.*$/, '')]; var index = locales2rules[lang.replace(/-.*$/, '')];
if (!(index in pluralRules)) { if (!(index in pluralRules)) {
console.warn('plural form unknown for [' + lang + ']'); consoleWarn('plural form unknown for [' + lang + ']');
return function() { return 'other'; }; return function() { return 'other'; };
} }
return pluralRules[index]; return pluralRules[index];
@ -669,24 +692,43 @@ window.html10n = (function(window, document, undefined) {
return str; return str;
}; };
/** /** Prepare localization context:
* Localize a document *
* @param langs An array of lang codes defining fallbacks * - Populate translations with strings for the indicated languages
* - adding in non-qualified versions of language codes immediately
* after any qualified (e.g., "en" for "en-GB")
* - Trigger "localized" event.
*
* @param {array} langs: diminishing-precedence lang codes, or one string.
*/ */
html10n.localize = function(langs) { html10n.localize = function(langs) {
var that = this var that = this,
// if only one string => create an array candidates = [];
if ('string' == typeof langs) langs = [langs] // if a single string, bundle it as an array:
if ('string' == typeof langs) {
langs = [langs];
}
// Expand two-part locale specs // Determine candidates from langs:
var i=0 // - Omitting empty strings
// - Adding in non-qualified versions of country-qualified codes.
langs.forEach(function(lang) { langs.forEach(function(lang) {
if(!lang) return; var splat;
langs[i++] = lang; if(!lang) { return; }
if(~lang.indexOf('-')) langs[i++] = lang.substr(0, lang.indexOf('-')); (candidates.indexOf(lang) == -1) && candidates.push(lang);
}) splat = lang.split('-');
if (splat[1]) {
(candidates.indexOf(splat[0]) == -1) && candidates.push(splat[0]);
}
});
this.build(langs, function(er, translations) { // Append script fallbacks for region-specific locales if applicable
for (var lang in html10n.scripts) {
i = candidates.indexOf(lang);
if (~i) candidates.splice(i, 0, html10n.scripts[lang])
}
this.build(candidates, function(er, translations) {
html10n.translations = translations html10n.translations = translations
html10n.translateElement(translations) html10n.translateElement(translations)
that.trigger('localized') that.trigger('localized')
@ -715,7 +757,7 @@ window.html10n = (function(window, document, undefined) {
var i = 0 var i = 0
, n = list.length , n = list.length
iterator(list[i], i, function each(err) { iterator(list[i], i, function each(err) {
if(err) console.error(err) if(err) consoleLog(err)
i++ i++
if (i < n) return iterator(list[i],i, each); if (i < n) return iterator(list[i],i, each);
cb() cb()
@ -738,8 +780,13 @@ window.html10n = (function(window, document, undefined) {
html10n.get = function(id, args) { html10n.get = function(id, args) {
var translations = html10n.translations var translations = html10n.translations
if(!translations) return console.warn('No translations available (yet)') if(!translations) {
if(!translations[id]) return console.warn('Could not find string '+id) if (! html10n.quiet) {
consoleWarn('No translations available (yet)');
}
return;
}
if(!translations[id]) return consoleWarn('Could not find string '+id)
// apply macros // apply macros
var str = translations[id] var str = translations[id]
@ -755,8 +802,9 @@ window.html10n = (function(window, document, undefined) {
// replace {{arguments}} with their values or the // replace {{arguments}} with their values or the
// associated translation string (based on its key) // associated translation string (based on its key)
function substArguments(str, args) { function substArguments(str, args) {
var reArgs = /\{\{\s*([a-zA-Z\.]+)\s*\}\}/ var reArgs = /\{\{\s*([a-zA-Z_\-\.]+)\s*\}\}/,
, match translations = html10n.translations,
match;
while (match = reArgs.exec(str)) { while (match = reArgs.exec(str)) {
if (!match || match.length < 2) if (!match || match.length < 2)
@ -764,12 +812,13 @@ window.html10n = (function(window, document, undefined) {
var arg = match[1] var arg = match[1]
, sub = '' , sub = ''
if (arg in args) { if (args && (arg in args)) {
sub = args[arg] sub = args[arg]
} else if (arg in translations) { } else if (arg in translations) {
sub = translations[arg] sub = translations[arg]
} else { } else {
console.warn('Could not find argument {{' + arg + '}}') consoleWarn('Could not satisfy argument {{' + arg + '}}' +
' for string "' + str + '"');
return str return str
} }
@ -781,7 +830,7 @@ window.html10n = (function(window, document, undefined) {
// replace {[macros]} with their values // replace {[macros]} with their values
function substMacros(key, str, args) { function substMacros(key, str, args) {
var regex = /\{\[\s*([a-zA-Z]+)\(([a-zA-Z]+)\)((\s*([a-zA-Z]+)\: ?([ a-zA-Z{}]+),?)+)*\s*\]\}/ //.exec('{[ plural(n) other: are {{n}}, one: is ]}') var regex = /\{\[\s*([a-zA-Z]+)\(([a-zA-Z]+)\)((\s*([a-zA-Z]+)\: ?([^,]+?),?)+)*\s*\]\}/ //.exec('{{n}} {[plural(n) one: Bomba, other: Bombe]} ]}')
, match , match
while(match = regex.exec(str)) { while(match = regex.exec(str)) {
@ -795,7 +844,7 @@ window.html10n = (function(window, document, undefined) {
if (!(macroName in html10n.macros)) continue if (!(macroName in html10n.macros)) continue
if(optv) { if(optv) {
optv.match(/(?=\s*)([a-zA-Z]+)\: ?([ a-zA-Z{}]+)(?=,?)/g).forEach(function(arg) { optv.match(/(?=\s*)([a-zA-Z]+)\: ?([^,\]]+)(?=,?)/g).forEach(function(arg) {
var parts = arg.split(':') var parts = arg.split(':')
, name = parts[0] , name = parts[0]
, value = parts[1].trim() , value = parts[1].trim()
@ -828,16 +877,16 @@ window.html10n = (function(window, document, undefined) {
str.id = node.getAttribute('data-l10n-id') str.id = node.getAttribute('data-l10n-id')
if (!str.id) return if (!str.id) return
if(!translations[str.id]) return console.warn('Couldn\'t find translation key '+str.id) if(!translations[str.id]) return consoleWarn('Couldn\'t find translation key '+str.id)
// get args // get args
if(window.JSON) { if(window.JSON) {
str.args = JSON.parse(node.getAttribute('data-l10n-args')) str.args = node.getAttribute('data-l10n-args') ? JSON.parse(node.getAttribute('data-l10n-args')) : [];
}else{ }else{
try{ try{
str.args = eval(node.getAttribute('data-l10n-args')) str.args = eval(node.getAttribute('data-l10n-args'))
}catch(e) { }catch(e) {
console.warn('Couldn\'t parse args for '+str.id) consoleWarn('Couldn\'t parse args for '+str.id)
} }
} }
@ -851,6 +900,7 @@ window.html10n = (function(window, document, undefined) {
, "innerHTML": 1 , "innerHTML": 1
, "alt": 1 , "alt": 1
, "textContent": 1 , "textContent": 1
, "placeholder": 1
, "value": 1 , "value": 1
} }
if (index > 0 && str.id.substr(index + 1) in attrList) { // an attribute has been specified if (index > 0 && str.id.substr(index + 1) in attrList) { // an attribute has been specified
@ -862,9 +912,6 @@ window.html10n = (function(window, document, undefined) {
// Apply translation // Apply translation
if (node.children.length === 0 || prop != 'textContent') { if (node.children.length === 0 || prop != 'textContent') {
node[prop] = str.str node[prop] = str.str
node.setAttribute("aria-label", str.str); // Sets the aria-label
// The idea of the above is that we always have an aria value
// This might be a bit of an abrupt solution but let's see how it goes
} else { } else {
var children = node.childNodes, var children = node.childNodes,
found = false found = false
@ -879,7 +926,7 @@ window.html10n = (function(window, document, undefined) {
} }
} }
if (!found) { if (!found) {
console.warn('Unexpected error: could not translate element content for key '+str.id, node) consoleWarn('Unexpected error: could not translate element content for key '+str.id, node)
} }
} }
} }