gridtracker/package.nw/lib/jquery.i18n.js

334 wiersze
8.8 KiB
JavaScript

/* eslint-disable no-tabs */
/*!
* jQuery Internationalization library
*
* Copyright (C) 2012 Santhosh Thottingal
*
* jquery.i18n is dual licensed GPLv2 or later and MIT. You don't have to do
* anything special to choose one license or the other and you don't have to
* notify anyone which license you are using. You are free to use
* UniversalLanguageSelector in commercial projects as long as the copyright
* header is left intact. See files GPL-LICENSE and MIT-LICENSE for details.
*
* @licence GNU General Public Licence 2.0 or later
* @licence MIT License
*/
(function ($)
{
"use strict";
var I18N,
slice = Array.prototype.slice;
/**
* @constructor
* @param {Object} options
*/
I18N = function (options)
{
// Load defaults
this.options = $.extend({}, I18N.defaults, options);
this.parser = this.options.parser;
this.locale = this.options.locale;
this.messageStore = this.options.messageStore;
this.languages = {};
};
I18N.prototype = {
/**
* Localize a given messageKey to a locale.
* @param {string} messageKey
* @return {string} Localized message
*/
localize: function (messageKey)
{
var localeParts, localePartIndex, locale, fallbackIndex,
tryingLocale, message;
locale = this.locale;
fallbackIndex = 0;
while (locale)
{
// Iterate through locales starting at most-specific until
// localization is found. As in fi-Latn-FI, fi-Latn and fi.
localeParts = locale.split("-");
localePartIndex = localeParts.length;
do
{
tryingLocale = localeParts.slice(0, localePartIndex).join("-");
message = this.messageStore.get(tryingLocale, messageKey);
if (message)
{
return message;
}
localePartIndex--;
} while (localePartIndex);
if (locale === this.options.fallbackLocale)
{
break;
}
locale = ($.i18n.fallbacks[this.locale] &&
$.i18n.fallbacks[this.locale][fallbackIndex]) ||
this.options.fallbackLocale;
$.i18n.log("Trying fallback locale for " + this.locale + ": " + locale + " (" + messageKey + ")");
fallbackIndex++;
}
// key not found
return "";
},
/*
* Destroy the i18n instance.
*/
destroy: function ()
{
$.removeData(document, "i18n");
},
/**
* General message loading API This can take a URL string for
* the json formatted messages. Example:
* <code>load('path/to/all_localizations.json');</code>
*
* To load a localization file for a locale:
* <code>
* load('path/to/de-messages.json', 'de' );
* </code>
*
* To load a localization file from a directory:
* <code>
* load('path/to/i18n/directory', 'de' );
* </code>
* The above method has the advantage of fallback resolution.
* ie, it will automatically load the fallback locales for de.
* For most usecases, this is the recommended method.
* It is optional to have trailing slash at end.
*
* A data object containing message key- message translation mappings
* can also be passed. Example:
* <code>
* load( { 'hello' : 'Hello' }, optionalLocale );
* </code>
*
* A source map containing key-value pair of languagename and locations
* can also be passed. Example:
* <code>
* load( {
* bn: 'i18n/bn.json',
* he: 'i18n/he.json',
* en: 'i18n/en.json'
* } )
* </code>
*
* If the data argument is null/undefined/false,
* all cached messages for the i18n instance will get reset.
*
* @param {string|Object} source
* @param {string} locale Language tag
* @return {jQuery.Promise}
*/
load: function (source, locale)
{
var fallbackLocales, locIndex, fallbackLocale, sourceMap = {};
if (!source && !locale)
{
source = "i18n/" + $.i18n().locale + ".json";
locale = $.i18n().locale;
}
if (typeof source === "string" &&
// source extension should be json, but can have query params after that.
source.split("?")[0].split(".").pop() !== "json"
)
{
// Load specified locale then check for fallbacks when directory is
// specified in load()
sourceMap[locale] = source + "/" + locale + ".json";
fallbackLocales = ($.i18n.fallbacks[locale] || [])
.concat(this.options.fallbackLocale);
for (locIndex = 0; locIndex < fallbackLocales.length; locIndex++)
{
fallbackLocale = fallbackLocales[locIndex];
sourceMap[fallbackLocale] = source + "/" + fallbackLocale + ".json";
}
return this.load(sourceMap);
}
else
{
return this.messageStore.load(source, locale);
}
},
/**
* Does parameter and magic word substitution.
*
* @param {string} key Message key
* @param {Array} parameters Message parameters
* @return {string}
*/
parse: function (key, parameters)
{
var message = this.localize(key);
// FIXME: This changes the state of the I18N object,
// should probably not change the 'this.parser' but just
// pass it to the parser.
this.parser.language = $.i18n.languages[$.i18n().locale] || $.i18n.languages.default;
if (message === "")
{
message = key;
}
return this.parser.parse(message, parameters);
}
};
/**
* Process a message from the $.I18N instance
* for the current document, stored in jQuery.data(document).
*
* @param {string} key Key of the message.
* @param {string} param1 [param...] Variadic list of parameters for {key}.
* @return {string|$.I18N} Parsed message, or if no key was given
* the instance of $.I18N is returned.
*/
$.i18n = function (key, param1)
{
var parameters,
i18n = $.data(document, "i18n"),
options = typeof key === "object" && key;
// If the locale option for this call is different then the setup so far,
// update it automatically. This doesn't just change the context for this
// call but for all future call as well.
// If there is no i18n setup yet, don't do this. It will be taken care of
// by the `new I18N` construction below.
// NOTE: It should only change language for this one call.
// Then cache instances of I18N somewhere.
if (options && options.locale && i18n && i18n.locale !== options.locale)
{
i18n.locale = options.locale;
}
if (!i18n)
{
i18n = new I18N(options);
$.data(document, "i18n", i18n);
}
if (typeof key === "string")
{
if (param1 !== undefined)
{
parameters = slice.call(arguments, 1);
}
else
{
parameters = [];
}
return i18n.parse(key, parameters);
}
else
{
// FIXME: remove this feature/bug.
return i18n;
}
};
$.fn.i18n = function ()
{
var i18n = $.data(document, "i18n");
if (!i18n)
{
i18n = new I18N();
$.data(document, "i18n", i18n);
}
return this.each(function ()
{
var $this = $(this),
messageKey = $this.data("i18n"),
lBracket, rBracket, type, key;
if (messageKey)
{
lBracket = messageKey.indexOf("[");
rBracket = messageKey.indexOf("]");
if (lBracket !== -1 && rBracket !== -1 && lBracket < rBracket)
{
type = messageKey.slice(lBracket + 1, rBracket);
key = messageKey.slice(rBracket + 1);
if (type === "html")
{
$this.html(i18n.parse(key));
}
else
{
$this.attr(type, i18n.parse(key));
}
}
else
{
$this.text(i18n.parse(messageKey));
}
}
else
{
$this.find("[data-i18n]").i18n();
}
});
};
function getDefaultLocale()
{
var locale = $("html").attr("lang");
if (!locale)
{
locale = navigator.language || navigator.userLanguage || "";
}
return locale;
}
$.i18n.languages = {};
$.i18n.messageStore = $.i18n.messageStore || {};
$.i18n.parser = {
// The default parser only handles variable substitution
parse: function (message, parameters)
{
return message.replace(/\$(\d+)/g, function (str, match)
{
var index = parseInt(match, 10) - 1;
return parameters[index] !== undefined ? parameters[index] : "$" + match;
});
},
emitter: {}
};
$.i18n.fallbacks = {};
$.i18n.debug = false;
$.i18n.log = function (/* arguments */)
{
if (window.console && $.i18n.debug)
{
window.console.log.apply(window.console, arguments);
}
};
/* Static members */
I18N.defaults = {
locale: getDefaultLocale(),
fallbackLocale: "en",
parser: $.i18n.parser,
messageStore: $.i18n.messageStore
};
// Expose constructor
$.i18n.constructor = I18N;
}(jQuery));