@qooxdoo/framework
Version:
The JS Framework for Coders
476 lines (404 loc) • 14.8 kB
JavaScript
/* ************************************************************************
qooxdoo - the new era of web development
http://qooxdoo.org
Copyright:
2004-2008 1&1 Internet AG, Germany, http://www.1und1.de
License:
MIT: https://opensource.org/licenses/MIT
See the LICENSE file in the project's top-level directory for details.
Authors:
* Sebastian Werner (wpbasti)
* Andreas Ecker (ecker)
* Fabian Jakobs (fjakobs)
************************************************************************ */
/**
* The qx.locale.Manager provides static translation methods (like tr()) and
* general locale information.
*
* @require(qx.event.dispatch.Direct)
* @require(qx.locale.LocalizedString)
*
* @cldr()
*/
qx.Class.define("qx.locale.Manager",
{
type : "singleton",
extend : qx.core.Object,
/*
*****************************************************************************
CONSTRUCTOR
*****************************************************************************
*/
construct : function()
{
this.base(arguments);
this.__translations = qx.$$translations || {};
this.__locales = qx.$$locales || {};
this.initLocale();
this.__clientLocale = this.getLocale();
},
/*
*****************************************************************************
STATICS
*****************************************************************************
*/
statics :
{
/**
* Translate a message
*
* @param messageId {String} message id (may contain format strings)
* @param varargs {Object} variable number of arguments applied to the format string
* @return {String | LocalizedString} The translated message or localized string
* @see qx.lang.String.format
*/
tr : function(messageId, varargs)
{
var args = qx.lang.Array.fromArguments(arguments);
args.splice(0, 1);
return qx.locale.Manager.getInstance().translate(messageId, args);
},
/**
* Translate a plural message
*
* Depending on the third argument the plural or the singular form is chosen.
*
* @param singularMessageId {String} message id of the singular form (may contain format strings)
* @param pluralMessageId {String} message id of the plural form (may contain format strings)
* @param count {Integer} singular form if equals 1, otherwise plural
* @param varargs {Object} variable number of arguments applied to the format string
* @return {String | LocalizedString} The translated message or localized string
* @see qx.lang.String.format
*/
trn : function(singularMessageId, pluralMessageId, count, varargs)
{
var args = qx.lang.Array.fromArguments(arguments);
args.splice(0, 3);
// assumes "Two forms, singular used for one only" (seems to be the most common form)
// (http://www.gnu.org/software/gettext/manual/html_node/gettext_150.html#Plural-forms)
// closely related with bug #745
if (count != 1) {
return qx.locale.Manager.getInstance().translate(pluralMessageId, args);
} else {
return qx.locale.Manager.getInstance().translate(singularMessageId, args);
}
},
/**
* Translate a message with translation hint (from developer addressed to translator).
*
* @param hint {String} hint for the translator of the message. Will be included in the .po file.
* @param messageId {String} message id (may contain format strings)
* @param varargs {Object} variable number of arguments applied to the format string
* @return {String | LocalizedString} The translated message or localized string
* @see qx.lang.String.format
*/
trc : function(hint, messageId, varargs)
{
var args = qx.lang.Array.fromArguments(arguments);
args.splice(0, 2);
return qx.locale.Manager.getInstance().translate(messageId, args);
},
/**
* Translate a plural message with translation hint (from developer addressed to translator).
*
* Depending on the third argument the plural or the singular form is chosen.
*
* @param hint {String} hint for the translator of the message. Will be included in the .po file.
* @param singularMessageId {String} message id of the singular form (may contain format strings)
* @param pluralMessageId {String} message id of the plural form (may contain format strings)
* @param count {Integer} singular form if equals 1, otherwise plural
* @param varargs {Object} variable number of arguments applied to the format string
* @return {String | LocalizedString} The translated message or localized string
* @see qx.lang.String.format
*/
trnc : function(hint, singularMessageId, pluralMessageId, count, varargs)
{
var args = qx.lang.Array.fromArguments(arguments);
args.splice(0, 4);
// see trn()
if (count != 1) {
return qx.locale.Manager.getInstance().translate(pluralMessageId, args);
} else {
return qx.locale.Manager.getInstance().translate(singularMessageId, args);
}
},
/**
* Mark the message for translation but return the original message.
*
* @param messageId {String} the message ID
* @return {String} messageId
*/
marktr : function(messageId) {
return messageId;
}
},
/*
*****************************************************************************
PROPERTIES
*****************************************************************************
*/
properties :
{
/** current locale. locale is an language code like de, de_AT, en, en_GB, fr, ... */
locale :
{
check : "String",
apply : "_applyLocale",
event : "changeLocale",
init : (function() {
var locale = qx.core.Environment.get("locale");
if(!locale || locale === "") {
return qx.core.Environment.get("locale.default");
}
var variant = qx.core.Environment.get("locale.variant");
if (variant !== "") {
locale += "_" + variant;
}
return locale; })()
}
},
/*
*****************************************************************************
MEMBERS
*****************************************************************************
*/
members :
{
__defaultLocale : qx.core.Environment.get("locale.default"),
__locale : null,
__language : null,
__translations : null,
__locales : null,
__clientLocale : null,
/**
* Get the language code of the current locale
*
* This is the first part of a locale definition. The language for "de_DE" would be "de"
*
* @return {String} language code
*/
getLanguage : function() {
return this.__language;
},
/**
* Get the territory code of the current locale
*
* This is the second part of a locale definition. The territory for "de_DE" would be "DE"
*
* @return {String} territory code
*/
getTerritory : function() {
return this.getLocale().split("_")[1] || "";
},
/**
* Return the available application locales
*
* This corresponds to the LOCALES setting in config.json. Without argument,
* it only returns the currently loaded locales, with an argument of true
* all locales that went into the build. This is particularly interesting if
* locales were generated as dedicated I18N parts, and have to be loaded
* explicitly before being available.
*
* @param includeNonloaded {Boolean?null} include locales not yet loaded
* @return {String[]} array of available locales
*/
getAvailableLocales : function(includeNonloaded)
{
var locales = [];
for (var locale in this.__locales)
{
if (locale != this.__defaultLocale) {
if (this.__locales[locale] === null && !includeNonloaded) {
continue; // skip not yet loaded locales
}
locales.push(locale);
}
}
return locales;
},
/**
* Extract the language part from a locale.
*
* @param locale {String} locale to be used
* @return {String} language
*/
__extractLanguage : function(locale)
{
var language;
if (locale == null) {
return null;
}
var pos = locale.indexOf("_");
if (pos == -1) {
language = locale;
} else {
language = locale.substring(0, pos);
}
return language;
},
// property apply
_applyLocale : function(value, old)
{
if (qx.core.Environment.get("qx.debug")) {
if (!(value in this.__locales || value == this.__clientLocale)) {
qx.log.Logger.warn("Locale: " + value+" not available.");
}
}
this.__locale = value;
this.__language = this.__extractLanguage(value);
},
/**
* Add a translation to the translation manager.
*
* If <code>languageCode</code> already exists, its map will be updated with
* <code>translationMap</code> (new keys will be added, existing keys will be
* overwritten).
*
* @param languageCode {String} language code of the translation like <i>de, de_AT, en, en_GB, fr, ...</i>
* @param translationMap {Map} mapping of message identifiers to message strings in the target
* language, e.g. <i>{"greeting_short" : "Hello"}</i>. Plural forms
* are separate keys.
*/
addTranslation : function(languageCode, translationMap)
{
var catalog = this.__translations;
if (catalog[languageCode])
{
for (var key in translationMap) {
catalog[languageCode][key] = translationMap[key];
}
}
else
{
catalog[languageCode] = translationMap;
}
},
/**
* Add a localization to the localization manager.
*
* If <code>localeCode</code> already exists, its map will be updated with
* <code>localeMap</code> (new keys will be added, existing keys will be overwritten).
*
* @param localeCode {String} locale code of the translation like <i>de, de_AT, en, en_GB, fr, ...</i>
* @param localeMap {Map} mapping of locale keys to the target locale values, e.g.
* <i>{"cldr_date_format_short" : "M/d/yy"}</i>.
*/
addLocale : function(localeCode, localeMap)
{
var catalog = this.__locales;
if (catalog[localeCode])
{
for (var key in localeMap) {
catalog[localeCode][key] = localeMap[key];
}
}
else
{
catalog[localeCode] = localeMap;
}
},
/**
* Translate a message using the current locale and apply format string to the arguments.
*
* Implements the lookup chain locale (e.g. en_US) -> language (e.g. en) ->
* default locale (e.g. C). Localizes the arguments if possible and splices
* them into the message. If qx.dynlocale is on, returns a {@link
* LocalizedString}.
*
* @param messageId {String} message id (may contain format strings)
* @param args {Object[]} array of objects, which are inserted into the format string
* @param locale {String ? #locale} locale to be used; if not given, defaults to the value of {@link #locale}
* @return {String | LocalizedString} translated message or localized string
*/
translate : function(messageId, args, locale)
{
var catalog = this.__translations;
return this.__lookupAndExpand(catalog, messageId, args, locale);
},
/**
* Provide localization (CLDR) data.
*
* Implements the lookup chain locale (e.g. en_US) -> language (e.g. en) ->
* default locale (e.g. C). Localizes the arguments if possible and splices
* them into the message. If qx.dynlocale is on, returns a {@link
* LocalizedString}.
*
* @param messageId {String} message id (may contain format strings)
* @param args {Object[]} array of objects, which are inserted into the format string
* @param locale {String ? #locale} locale to be used; if not given, defaults to the value of {@link #locale}
* @return {String | LocalizedString} translated message or localized string
*/
localize : function(messageId, args, locale)
{
var catalog = this.__locales;
return this.__lookupAndExpand(catalog, messageId, args, locale);
},
/**
* Look up an I18N key in a catalog and expand format strings.
*
* Implements the lookup chain locale (e.g. en_US) -> language (e.g. en) ->
* default locale (e.g. C). Localizes the arguments if possible and splices
* them into the message. If qx.dynlocale is on, returns a {@link
* LocalizedString}.
*
* @param catalog {Map} map of I18N keys and their values
* @param messageId {String} message id (may contain format strings)
* @param args {Object[]} array of objects, which are inserted into the format string
* @param locale {String ? #locale} locale to be used; if not given, defaults to the value of {@link #locale}
* @return {String | LocalizedString} translated message or localized string
*/
__lookupAndExpand : function(catalog, messageId, args, locale)
{
if (qx.core.Environment.get("qx.debug")) {
this.assertObject(catalog);
this.assertString(messageId);
this.assertArray(args);
}
var txt;
if (!catalog) {
return messageId;
}
if (locale) {
var language = this.__extractLanguage(locale);
}
else
{
locale = this.__locale;
language = this.__language;
}
// e.g. DE_at
if (!txt && catalog[locale]) {
txt = catalog[locale][messageId];
}
// e.g. DE
if (!txt && catalog[language]) {
txt = catalog[language][messageId];
}
// C
if (!txt && catalog[this.__defaultLocale]) {
txt = catalog[this.__defaultLocale][messageId];
}
if (!txt) {
txt = messageId;
}
if (args.length > 0)
{
var translatedArgs = [];
for ( var i = 0; i < args.length; i++)
{
var arg = args[i];
if (arg && arg.translate) {
translatedArgs[i] = arg.translate();
} else {
translatedArgs[i] = arg;
}
}
txt = qx.lang.String.format(txt, translatedArgs);
}
if (qx.core.Environment.get("qx.dynlocale")) {
txt = new qx.locale.LocalizedString(txt, messageId, args, catalog === this.__locales);
}
return txt;
}
}
});