|
|
@ -19,14 +19,8 @@ |
|
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
|
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
|
|
|
* IN THE SOFTWARE. |
|
|
|
* IN THE SOFTWARE. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
/* |
|
|
|
|
|
|
|
Additional modifications for PDF.js project: |
|
|
|
|
|
|
|
- Disables language initialization on page loading; |
|
|
|
|
|
|
|
- Removes consoleWarn and consoleLog and use console.log/warn directly. |
|
|
|
|
|
|
|
- Removes window._ assignment. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*jshint browser: true, devel: true, globalstrict: true */ |
|
|
|
/*jshint browser: true, devel: true, es5: true, globalstrict: true */ |
|
|
|
'use strict'; |
|
|
|
'use strict'; |
|
|
|
|
|
|
|
|
|
|
|
document.webL10n = (function(window, document, undefined) { |
|
|
|
document.webL10n = (function(window, document, undefined) { |
|
|
@ -53,6 +47,29 @@ document.webL10n = (function(window, document, undefined) { |
|
|
|
var gAsyncResourceLoading = true; // read-only
|
|
|
|
var gAsyncResourceLoading = true; // read-only
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Debug helpers |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* gDEBUG == 0: don't display any console message |
|
|
|
|
|
|
|
* gDEBUG == 1: display only warnings, not logs |
|
|
|
|
|
|
|
* gDEBUG == 2: display all console messages |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var gDEBUG = 1; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function consoleLog(message) { |
|
|
|
|
|
|
|
if (gDEBUG >= 2) { |
|
|
|
|
|
|
|
console.log('[l10n] ' + message); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function consoleWarn(message) { |
|
|
|
|
|
|
|
if (gDEBUG) { |
|
|
|
|
|
|
|
console.warn('[l10n] ' + message); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* DOM helpers for the so-called "HTML API". |
|
|
|
* DOM helpers for the so-called "HTML API". |
|
|
|
* |
|
|
|
* |
|
|
@ -85,7 +102,7 @@ document.webL10n = (function(window, document, undefined) { |
|
|
|
try { |
|
|
|
try { |
|
|
|
args = JSON.parse(l10nArgs); |
|
|
|
args = JSON.parse(l10nArgs); |
|
|
|
} catch (e) { |
|
|
|
} catch (e) { |
|
|
|
console.warn('could not parse arguments for #' + l10nId); |
|
|
|
consoleWarn('could not parse arguments for #' + l10nId); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
return { id: l10nId, args: args }; |
|
|
|
return { id: l10nId, args: args }; |
|
|
@ -98,14 +115,14 @@ document.webL10n = (function(window, document, undefined) { |
|
|
|
document.dispatchEvent(evtObject); |
|
|
|
document.dispatchEvent(evtObject); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function xhrLoadText(url, onSuccess, onFailure, asynchronous) { |
|
|
|
function xhrLoadText(url, onSuccess, onFailure) { |
|
|
|
onSuccess = onSuccess || function _onSuccess(data) {}; |
|
|
|
onSuccess = onSuccess || function _onSuccess(data) {}; |
|
|
|
onFailure = onFailure || function _onFailure() { |
|
|
|
onFailure = onFailure || function _onFailure() { |
|
|
|
console.warn(url + ' not found.'); |
|
|
|
consoleWarn(url + ' not found.'); |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
var xhr = new XMLHttpRequest(); |
|
|
|
var xhr = new XMLHttpRequest(); |
|
|
|
xhr.open('GET', url, asynchronous); |
|
|
|
xhr.open('GET', url, gAsyncResourceLoading); |
|
|
|
if (xhr.overrideMimeType) { |
|
|
|
if (xhr.overrideMimeType) { |
|
|
|
xhr.overrideMimeType('text/plain; charset=utf-8'); |
|
|
|
xhr.overrideMimeType('text/plain; charset=utf-8'); |
|
|
|
} |
|
|
|
} |
|
|
@ -174,8 +191,10 @@ document.webL10n = (function(window, document, undefined) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// parse *.properties text data into an l10n dictionary
|
|
|
|
// parse *.properties text data into an l10n dictionary
|
|
|
|
function parseProperties(text) { |
|
|
|
// If gAsyncResourceLoading is false, then the callback will be called
|
|
|
|
var dictionary = []; |
|
|
|
// synchronously. Otherwise it is called asynchronously.
|
|
|
|
|
|
|
|
function parseProperties(text, parsedPropertiesCallback) { |
|
|
|
|
|
|
|
var dictionary = {}; |
|
|
|
|
|
|
|
|
|
|
|
// token expressions
|
|
|
|
// token expressions
|
|
|
|
var reBlank = /^\s*|\s*$/; |
|
|
|
var reBlank = /^\s*|\s*$/; |
|
|
@ -185,15 +204,22 @@ document.webL10n = (function(window, document, undefined) { |
|
|
|
var reSplit = /^([^=\s]*)\s*=\s*(.+)$/; // TODO: escape EOLs with '\'
|
|
|
|
var reSplit = /^([^=\s]*)\s*=\s*(.+)$/; // TODO: escape EOLs with '\'
|
|
|
|
|
|
|
|
|
|
|
|
// parse the *.properties file into an associative array
|
|
|
|
// parse the *.properties file into an associative array
|
|
|
|
function parseRawLines(rawText, extendedSyntax) { |
|
|
|
function parseRawLines(rawText, extendedSyntax, parsedRawLinesCallback) { |
|
|
|
var entries = rawText.replace(reBlank, '').split(/[\r\n]+/); |
|
|
|
var entries = rawText.replace(reBlank, '').split(/[\r\n]+/); |
|
|
|
var currentLang = '*'; |
|
|
|
var currentLang = '*'; |
|
|
|
var genericLang = lang.split('-', 1)[0]; |
|
|
|
var genericLang = lang.split('-', 1)[0]; |
|
|
|
var skipLang = false; |
|
|
|
var skipLang = false; |
|
|
|
var match = ''; |
|
|
|
var match = ''; |
|
|
|
|
|
|
|
|
|
|
|
for (var i = 0; i < entries.length; i++) { |
|
|
|
function nextEntry() { |
|
|
|
var line = entries[i]; |
|
|
|
// Use infinite loop instead of recursion to avoid reaching the
|
|
|
|
|
|
|
|
// maximum recursion limit for content with many lines.
|
|
|
|
|
|
|
|
while (true) { |
|
|
|
|
|
|
|
if (!entries.length) { |
|
|
|
|
|
|
|
parsedRawLinesCallback(); |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
var line = entries.shift(); |
|
|
|
|
|
|
|
|
|
|
|
// comment or blank line?
|
|
|
|
// comment or blank line?
|
|
|
|
if (reComment.test(line)) |
|
|
|
if (reComment.test(line)) |
|
|
@ -201,10 +227,11 @@ document.webL10n = (function(window, document, undefined) { |
|
|
|
|
|
|
|
|
|
|
|
// the extended syntax supports [lang] sections and @import rules
|
|
|
|
// the extended syntax supports [lang] sections and @import rules
|
|
|
|
if (extendedSyntax) { |
|
|
|
if (extendedSyntax) { |
|
|
|
if (reSection.test(line)) { // section start?
|
|
|
|
|
|
|
|
match = reSection.exec(line); |
|
|
|
match = reSection.exec(line); |
|
|
|
|
|
|
|
if (match) { // section start?
|
|
|
|
// RFC 4646, section 4.4, "All comparisons MUST be performed
|
|
|
|
// RFC 4646, section 4.4, "All comparisons MUST be performed
|
|
|
|
// in a case-insensitive manner."
|
|
|
|
// in a case-insensitive manner."
|
|
|
|
|
|
|
|
|
|
|
|
currentLang = match[1].toLowerCase(); |
|
|
|
currentLang = match[1].toLowerCase(); |
|
|
|
skipLang = (currentLang !== '*') && |
|
|
|
skipLang = (currentLang !== '*') && |
|
|
|
(currentLang !== lang) && (currentLang !== genericLang); |
|
|
|
(currentLang !== lang) && (currentLang !== genericLang); |
|
|
@ -212,9 +239,10 @@ document.webL10n = (function(window, document, undefined) { |
|
|
|
} else if (skipLang) { |
|
|
|
} else if (skipLang) { |
|
|
|
continue; |
|
|
|
continue; |
|
|
|
} |
|
|
|
} |
|
|
|
if (reImport.test(line)) { // @import rule?
|
|
|
|
|
|
|
|
match = reImport.exec(line); |
|
|
|
match = reImport.exec(line); |
|
|
|
loadImport(baseURL + match[1]); // load the resource synchronously
|
|
|
|
if (match) { // @import rule?
|
|
|
|
|
|
|
|
loadImport(baseURL + match[1], nextEntry); |
|
|
|
|
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -225,17 +253,20 @@ document.webL10n = (function(window, document, undefined) { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
nextEntry(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// import another *.properties file
|
|
|
|
// import another *.properties file
|
|
|
|
function loadImport(url) { |
|
|
|
function loadImport(url, callback) { |
|
|
|
xhrLoadText(url, function(content) { |
|
|
|
xhrLoadText(url, function(content) { |
|
|
|
parseRawLines(content, false); // don't allow recursive imports
|
|
|
|
parseRawLines(content, false, callback); // don't allow recursive imports
|
|
|
|
}, null, false); // load synchronously
|
|
|
|
}, null); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// fill the dictionary
|
|
|
|
// fill the dictionary
|
|
|
|
parseRawLines(text, true); |
|
|
|
parseRawLines(text, true, function() { |
|
|
|
return dictionary; |
|
|
|
parsedPropertiesCallback(dictionary); |
|
|
|
|
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// load and parse l10n data (warning: global variables are used here)
|
|
|
|
// load and parse l10n data (warning: global variables are used here)
|
|
|
@ -243,7 +274,7 @@ document.webL10n = (function(window, document, undefined) { |
|
|
|
gTextData += response; // mostly for debug
|
|
|
|
gTextData += response; // mostly for debug
|
|
|
|
|
|
|
|
|
|
|
|
// parse *.properties text data into an l10n dictionary
|
|
|
|
// parse *.properties text data into an l10n dictionary
|
|
|
|
var data = parseProperties(response); |
|
|
|
parseProperties(response, function(data) { |
|
|
|
|
|
|
|
|
|
|
|
// find attribute descriptions, if any
|
|
|
|
// find attribute descriptions, if any
|
|
|
|
for (var key in data) { |
|
|
|
for (var key in data) { |
|
|
@ -265,7 +296,8 @@ document.webL10n = (function(window, document, undefined) { |
|
|
|
if (successCallback) { |
|
|
|
if (successCallback) { |
|
|
|
successCallback(); |
|
|
|
successCallback(); |
|
|
|
} |
|
|
|
} |
|
|
|
}, failureCallback, gAsyncResourceLoading); |
|
|
|
}); |
|
|
|
|
|
|
|
}, failureCallback); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// load and parse all resources for the specified locale
|
|
|
|
// load and parse all resources for the specified locale
|
|
|
@ -289,7 +321,7 @@ document.webL10n = (function(window, document, undefined) { |
|
|
|
// we might have a pre-compiled dictionary instead
|
|
|
|
// we might have a pre-compiled dictionary instead
|
|
|
|
var dict = getL10nDictionary(); |
|
|
|
var dict = getL10nDictionary(); |
|
|
|
if (dict && dict.locales && dict.default_locale) { |
|
|
|
if (dict && dict.locales && dict.default_locale) { |
|
|
|
console.log('using the embedded JSON directory, early way out'); |
|
|
|
consoleLog('using the embedded JSON directory, early way out'); |
|
|
|
gL10nData = dict.locales[lang]; |
|
|
|
gL10nData = dict.locales[lang]; |
|
|
|
if (!gL10nData) { |
|
|
|
if (!gL10nData) { |
|
|
|
var defaultLocale = dict.default_locale.toLowerCase(); |
|
|
|
var defaultLocale = dict.default_locale.toLowerCase(); |
|
|
@ -305,7 +337,7 @@ document.webL10n = (function(window, document, undefined) { |
|
|
|
} |
|
|
|
} |
|
|
|
callback(); |
|
|
|
callback(); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
console.log('no resource to load, early way out'); |
|
|
|
consoleLog('no resource to load, early way out'); |
|
|
|
} |
|
|
|
} |
|
|
|
// early way out
|
|
|
|
// early way out
|
|
|
|
fireL10nReadyEvent(lang); |
|
|
|
fireL10nReadyEvent(lang); |
|
|
@ -328,24 +360,23 @@ document.webL10n = (function(window, document, undefined) { |
|
|
|
// load all resource files
|
|
|
|
// load all resource files
|
|
|
|
function L10nResourceLink(link) { |
|
|
|
function L10nResourceLink(link) { |
|
|
|
var href = link.href; |
|
|
|
var href = link.href; |
|
|
|
var type = link.type; |
|
|
|
// Note: If |gAsyncResourceLoading| is false, then the following callbacks
|
|
|
|
|
|
|
|
// are synchronously called.
|
|
|
|
this.load = function(lang, callback) { |
|
|
|
this.load = function(lang, callback) { |
|
|
|
var applied = lang; |
|
|
|
|
|
|
|
parseResource(href, lang, callback, function() { |
|
|
|
parseResource(href, lang, callback, function() { |
|
|
|
console.warn(href + ' not found.'); |
|
|
|
consoleWarn(href + ' not found.'); |
|
|
|
applied = ''; |
|
|
|
// lang not found, used default resource instead
|
|
|
|
|
|
|
|
consoleWarn('"' + lang + '" resource not found'); |
|
|
|
|
|
|
|
gLanguage = ''; |
|
|
|
|
|
|
|
// Resource not loaded, but we still need to call the callback.
|
|
|
|
|
|
|
|
callback(); |
|
|
|
}); |
|
|
|
}); |
|
|
|
return applied; // return lang if found, an empty string if not found
|
|
|
|
|
|
|
|
}; |
|
|
|
}; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
for (var i = 0; i < langCount; i++) { |
|
|
|
for (var i = 0; i < langCount; i++) { |
|
|
|
var resource = new L10nResourceLink(langLinks[i]); |
|
|
|
var resource = new L10nResourceLink(langLinks[i]); |
|
|
|
var rv = resource.load(lang, onResourceLoaded); |
|
|
|
resource.load(lang, onResourceLoaded); |
|
|
|
if (rv != lang) { // lang not found, used default resource instead
|
|
|
|
|
|
|
|
console.warn('"' + lang + '" resource not found'); |
|
|
|
|
|
|
|
gLanguage = ''; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -764,7 +795,7 @@ document.webL10n = (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]; |
|
|
@ -811,7 +842,7 @@ document.webL10n = (function(window, document, undefined) { |
|
|
|
function getL10nData(key, args, fallback) { |
|
|
|
function getL10nData(key, args, fallback) { |
|
|
|
var data = gL10nData[key]; |
|
|
|
var data = gL10nData[key]; |
|
|
|
if (!data) { |
|
|
|
if (!data) { |
|
|
|
console.warn('#' + key + ' is undefined.'); |
|
|
|
consoleWarn('#' + key + ' is undefined.'); |
|
|
|
if (!fallback) { |
|
|
|
if (!fallback) { |
|
|
|
return null; |
|
|
|
return null; |
|
|
|
} |
|
|
|
} |
|
|
@ -861,28 +892,17 @@ document.webL10n = (function(window, document, undefined) { |
|
|
|
|
|
|
|
|
|
|
|
// replace {{arguments}} with their values
|
|
|
|
// replace {{arguments}} with their values
|
|
|
|
function substArguments(str, args, key) { |
|
|
|
function substArguments(str, args, key) { |
|
|
|
var reArgs = /\{\{\s*(.+?)\s*\}\}/; |
|
|
|
var reArgs = /\{\{\s*(.+?)\s*\}\}/g; |
|
|
|
var match = reArgs.exec(str); |
|
|
|
return str.replace(reArgs, function(matched_text, arg) { |
|
|
|
while (match) { |
|
|
|
|
|
|
|
if (!match || match.length < 2) |
|
|
|
|
|
|
|
return str; // argument key not found
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var arg = match[1]; |
|
|
|
|
|
|
|
var sub = ''; |
|
|
|
|
|
|
|
if (args && arg in args) { |
|
|
|
if (args && arg in args) { |
|
|
|
sub = args[arg]; |
|
|
|
return args[arg]; |
|
|
|
} else if (arg in gL10nData) { |
|
|
|
|
|
|
|
sub = gL10nData[arg][gTextProp]; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
console.log('argument {{' + arg + '}} for #' + key + ' is undefined.'); |
|
|
|
|
|
|
|
return str; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (arg in gL10nData) { |
|
|
|
str = str.substring(0, match.index) + sub + |
|
|
|
return gL10nData[arg]; |
|
|
|
str.substr(match.index + match[0].length); |
|
|
|
|
|
|
|
match = reArgs.exec(str); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
return str; |
|
|
|
consoleLog('argument {{' + arg + '}} for #' + key + ' is undefined.'); |
|
|
|
|
|
|
|
return matched_text; |
|
|
|
|
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// translate an HTML element
|
|
|
|
// translate an HTML element
|
|
|
@ -894,7 +914,7 @@ document.webL10n = (function(window, document, undefined) { |
|
|
|
// get the related l10n object
|
|
|
|
// get the related l10n object
|
|
|
|
var data = getL10nData(l10n.id, l10n.args); |
|
|
|
var data = getL10nData(l10n.id, l10n.args); |
|
|
|
if (!data) { |
|
|
|
if (!data) { |
|
|
|
console.warn('#' + l10n.id + ' is undefined.'); |
|
|
|
consoleWarn('#' + l10n.id + ' is undefined.'); |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -962,6 +982,151 @@ document.webL10n = (function(window, document, undefined) { |
|
|
|
translateElement(element); |
|
|
|
translateElement(element); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Startup & Public API |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* Warning: this part of the code contains browser-specific chunks -- |
|
|
|
|
|
|
|
* that's where obsolete browsers, namely IE8 and earlier, are handled. |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* Unlike the rest of the lib, this section is not shared with FirefoxOS/Gaia. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// load the default locale on startup
|
|
|
|
|
|
|
|
function l10nStartup() { |
|
|
|
|
|
|
|
gReadyState = 'interactive'; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// most browsers expose the UI language as `navigator.language'
|
|
|
|
|
|
|
|
// but IE uses `navigator.userLanguage' instead
|
|
|
|
|
|
|
|
var userLocale = navigator.language || navigator.userLanguage; |
|
|
|
|
|
|
|
consoleLog('loading [' + userLocale + '] resources, ' + |
|
|
|
|
|
|
|
(gAsyncResourceLoading ? 'asynchronously.' : 'synchronously.')); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// load the default locale and translate the document if required
|
|
|
|
|
|
|
|
if (document.documentElement.lang === userLocale) { |
|
|
|
|
|
|
|
loadLocale(userLocale); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
loadLocale(userLocale, translateFragment); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// browser-specific startup
|
|
|
|
|
|
|
|
if (document.addEventListener) { // modern browsers and IE9+
|
|
|
|
|
|
|
|
if (document.readyState === 'loading') { |
|
|
|
|
|
|
|
// the document is not fully loaded yet: wait for DOMContentLoaded.
|
|
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', l10nStartup); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
// l10n.js is being loaded with <script defer> or <script async>,
|
|
|
|
|
|
|
|
// the DOM is ready for parsing.
|
|
|
|
|
|
|
|
window.setTimeout(l10nStartup); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else if (window.attachEvent) { // IE8 and before (= oldIE)
|
|
|
|
|
|
|
|
// TODO: check if jQuery is loaded (CSS selector + JSON + events)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// dummy `console.log' and `console.warn' functions
|
|
|
|
|
|
|
|
if (!window.console) { |
|
|
|
|
|
|
|
consoleLog = function(message) {}; // just ignore console.log calls
|
|
|
|
|
|
|
|
consoleWarn = function(message) { |
|
|
|
|
|
|
|
if (gDEBUG) { |
|
|
|
|
|
|
|
alert('[l10n] ' + message); // vintage debugging, baby!
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// XMLHttpRequest for IE6
|
|
|
|
|
|
|
|
if (!window.XMLHttpRequest) { |
|
|
|
|
|
|
|
xhrLoadText = function(url, onSuccess, onFailure) { |
|
|
|
|
|
|
|
onSuccess = onSuccess || function _onSuccess(data) {}; |
|
|
|
|
|
|
|
onFailure = onFailure || function _onFailure() { |
|
|
|
|
|
|
|
consoleWarn(url + ' not found.'); |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
var xhr = new ActiveXObject('Microsoft.XMLHTTP'); |
|
|
|
|
|
|
|
xhr.open('GET', url, gAsyncResourceLoading); |
|
|
|
|
|
|
|
xhr.onreadystatechange = function() { |
|
|
|
|
|
|
|
if (xhr.readyState == 4) { |
|
|
|
|
|
|
|
if (xhr.status == 200) { |
|
|
|
|
|
|
|
onSuccess(xhr.responseText); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
onFailure(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
xhr.send(null); |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// worst hack ever for IE6 and IE7
|
|
|
|
|
|
|
|
if (!window.JSON) { |
|
|
|
|
|
|
|
getL10nAttributes = function(element) { |
|
|
|
|
|
|
|
if (!element) |
|
|
|
|
|
|
|
return {}; |
|
|
|
|
|
|
|
var l10nId = element.getAttribute('data-l10n-id'), |
|
|
|
|
|
|
|
l10nArgs = element.getAttribute('data-l10n-args'), |
|
|
|
|
|
|
|
args = {}; |
|
|
|
|
|
|
|
if (l10nArgs) try { |
|
|
|
|
|
|
|
args = eval(l10nArgs); // XXX yeah, I know...
|
|
|
|
|
|
|
|
} catch (e) { |
|
|
|
|
|
|
|
consoleWarn('could not parse arguments for #' + l10nId); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return { id: l10nId, args: args }; |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// override `getTranslatableChildren' and `getL10nResourceLinks'
|
|
|
|
|
|
|
|
if (!document.querySelectorAll) { |
|
|
|
|
|
|
|
getTranslatableChildren = function(element) { |
|
|
|
|
|
|
|
if (!element) |
|
|
|
|
|
|
|
return []; |
|
|
|
|
|
|
|
var nodes = element.getElementsByTagName('*'), |
|
|
|
|
|
|
|
l10nElements = [], |
|
|
|
|
|
|
|
n = nodes.length; |
|
|
|
|
|
|
|
for (var i = 0; i < n; i++) { |
|
|
|
|
|
|
|
if (nodes[i].getAttribute('data-l10n-id')) |
|
|
|
|
|
|
|
l10nElements.push(nodes[i]); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return l10nElements; |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
getL10nResourceLinks = function() { |
|
|
|
|
|
|
|
var links = document.getElementsByTagName('link'), |
|
|
|
|
|
|
|
l10nLinks = [], |
|
|
|
|
|
|
|
n = links.length; |
|
|
|
|
|
|
|
for (var i = 0; i < n; i++) { |
|
|
|
|
|
|
|
if (links[i].type == 'application/l10n') |
|
|
|
|
|
|
|
l10nLinks.push(links[i]); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return l10nLinks; |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// override `getL10nDictionary'
|
|
|
|
|
|
|
|
if (!window.JSON || !document.querySelectorAll) { |
|
|
|
|
|
|
|
getL10nDictionary = function() { |
|
|
|
|
|
|
|
var scripts = document.getElementsByName('script'); |
|
|
|
|
|
|
|
for (var i = 0; i < scripts.length; i++) { |
|
|
|
|
|
|
|
if (scripts[i].type == 'application/l10n') { |
|
|
|
|
|
|
|
return eval(scripts[i].innerHTML); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return null; |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// fire non-standard `localized' DOM events
|
|
|
|
|
|
|
|
if (document.createEventObject && !document.createEvent) { |
|
|
|
|
|
|
|
fireL10nReadyEvent = function(lang) { |
|
|
|
|
|
|
|
// hack to simulate a custom event in IE:
|
|
|
|
|
|
|
|
// to catch this event, add an event handler to `onpropertychange'
|
|
|
|
|
|
|
|
document.documentElement.localized = 1; |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// startup for IE<9
|
|
|
|
|
|
|
|
window.attachEvent('onload', function() { |
|
|
|
|
|
|
|
gTextProp = document.textContent === null ? 'textContent' : 'innerText'; |
|
|
|
|
|
|
|
l10nStartup(); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// cross-browser API (sorry, oldIE doesn't support getters & setters)
|
|
|
|
// cross-browser API (sorry, oldIE doesn't support getters & setters)
|
|
|
|
return { |
|
|
|
return { |
|
|
|
// get a localized string
|
|
|
|
// get a localized string
|
|
|
@ -990,17 +1155,20 @@ document.webL10n = (function(window, document, undefined) { |
|
|
|
|
|
|
|
|
|
|
|
// get|set the document language
|
|
|
|
// get|set the document language
|
|
|
|
getLanguage: function() { return gLanguage; }, |
|
|
|
getLanguage: function() { return gLanguage; }, |
|
|
|
setLanguage: function(lang) { loadLocale(lang, translateFragment); }, |
|
|
|
setLanguage: function(lang, callback) { |
|
|
|
|
|
|
|
loadLocale(lang, function() { |
|
|
|
|
|
|
|
if (callback) |
|
|
|
|
|
|
|
callback(); |
|
|
|
|
|
|
|
translateFragment(); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
// get the direction (ltr|rtl) of the current language
|
|
|
|
// get the direction (ltr|rtl) of the current language
|
|
|
|
getDirection: function() { |
|
|
|
getDirection: function() { |
|
|
|
// http://www.w3.org/International/questions/qa-scripts
|
|
|
|
// http://www.w3.org/International/questions/qa-scripts
|
|
|
|
// Arabic, Hebrew, Farsi, Pashto, Urdu
|
|
|
|
// Arabic, Hebrew, Farsi, Pashto, Urdu
|
|
|
|
var rtlList = ['ar', 'he', 'fa', 'ps', 'ur']; |
|
|
|
var rtlList = ['ar', 'he', 'fa', 'ps', 'ur']; |
|
|
|
|
|
|
|
var shortCode = gLanguage.split('-', 1)[0]; |
|
|
|
// use the short language code for "full" codes like 'ar-sa' (issue 5440)
|
|
|
|
|
|
|
|
var shortCode = gLanguage.split('-')[0]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return (rtlList.indexOf(shortCode) >= 0) ? 'rtl' : 'ltr'; |
|
|
|
return (rtlList.indexOf(shortCode) >= 0) ? 'rtl' : 'ltr'; |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
@ -1013,12 +1181,18 @@ document.webL10n = (function(window, document, undefined) { |
|
|
|
if (!callback) { |
|
|
|
if (!callback) { |
|
|
|
return; |
|
|
|
return; |
|
|
|
} else if (gReadyState == 'complete' || gReadyState == 'interactive') { |
|
|
|
} else if (gReadyState == 'complete' || gReadyState == 'interactive') { |
|
|
|
window.setTimeout(callback); |
|
|
|
window.setTimeout(function() { |
|
|
|
|
|
|
|
callback(); |
|
|
|
|
|
|
|
}); |
|
|
|
} else if (document.addEventListener) { |
|
|
|
} else if (document.addEventListener) { |
|
|
|
document.addEventListener('localized', callback); |
|
|
|
document.addEventListener('localized', function once() { |
|
|
|
|
|
|
|
document.removeEventListener('localized', once); |
|
|
|
|
|
|
|
callback(); |
|
|
|
|
|
|
|
}); |
|
|
|
} else if (document.attachEvent) { |
|
|
|
} else if (document.attachEvent) { |
|
|
|
document.documentElement.attachEvent('onpropertychange', function(e) { |
|
|
|
document.documentElement.attachEvent('onpropertychange', function once(e) { |
|
|
|
if (e.propertyName === 'localized') { |
|
|
|
if (e.propertyName === 'localized') { |
|
|
|
|
|
|
|
document.documentElement.detachEvent('onpropertychange', once); |
|
|
|
callback(); |
|
|
|
callback(); |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
@ -1026,3 +1200,9 @@ document.webL10n = (function(window, document, undefined) { |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
}; |
|
|
|
}) (window, document); |
|
|
|
}) (window, document); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// gettext-like shortcut for document.webL10n.get
|
|
|
|
|
|
|
|
if (window._ === undefined) { |
|
|
|
|
|
|
|
var _ = document.webL10n.get; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|