Move translation machinery into a separate module

This commit is contained in:
JC Brand 2017-10-24 12:56:35 +02:00
parent dffc6fbb50
commit f73075d20a
6 changed files with 165 additions and 133 deletions

View File

@ -10,6 +10,7 @@
define("moment_with_locales", [ define("moment_with_locales", [
'moment', // Everything below can be removed except for moment itself. 'moment', // Everything below can be removed except for moment itself.
'moment/locale/af', 'moment/locale/af',
'moment/locale/ca',
'moment/locale/de', 'moment/locale/de',
'moment/locale/es', 'moment/locale/es',
'moment/locale/fr', 'moment/locale/fr',

View File

@ -25,6 +25,7 @@
smaller filesize but means that the translations you want to provide need to smaller filesize but means that the translations you want to provide need to
be available. See the [locales_url](https://conversejs.org/docs/html/configurations.html#locales-url) be available. See the [locales_url](https://conversejs.org/docs/html/configurations.html#locales-url)
configuration setting for more info. configuration setting for more info.
- The translation machinery has now been moved to a separate module in `src/i18n.js`.
## 3.2.1 (2017-08-29) ## 3.2.1 (2017-08-29)

View File

@ -26,6 +26,7 @@ require.config({
"es6-promise": "node_modules/es6-promise/dist/es6-promise.auto", "es6-promise": "node_modules/es6-promise/dist/es6-promise.auto",
"eventemitter": "node_modules/otr/build/dep/eventemitter", "eventemitter": "node_modules/otr/build/dep/eventemitter",
"form-utils": "src/form-utils", "form-utils": "src/form-utils",
"i18n": "src/i18n",
"jed": "node_modules/jed/jed", "jed": "node_modules/jed/jed",
"jquery": "node_modules/jquery/dist/jquery", "jquery": "node_modules/jquery/dist/jquery",
"jquery.browser": "node_modules/jquery.browser/dist/jquery.browser", "jquery.browser": "node_modules/jquery.browser/dist/jquery.browser",

View File

@ -10,7 +10,7 @@
"es6-promise", "es6-promise",
"lodash.noconflict", "lodash.noconflict",
"polyfill", "polyfill",
"jed", "i18n",
"utils", "utils",
"moment_with_locales", "moment_with_locales",
"strophe", "strophe",
@ -19,7 +19,7 @@
"backbone.browserStorage", "backbone.browserStorage",
"backbone.overview", "backbone.overview",
], factory); ], factory);
}(this, function (sizzle, Promise, _, polyfill, Jed, utils, moment, Strophe, pluggable, Backbone) { }(this, function (sizzle, Promise, _, polyfill, i18n, utils, moment, Strophe, pluggable, Backbone) {
/* Cannot use this due to Safari bug. /* Cannot use this due to Safari bug.
* See https://github.com/jcbrand/converse.js/issues/196 * See https://github.com/jcbrand/converse.js/issues/196
@ -137,11 +137,11 @@
* (String) message - The message to be logged. * (String) message - The message to be logged.
* (Integer) level - The loglevel which allows for filtering of log * (Integer) level - The loglevel which allows for filtering of log
* messages. * messages.
* *
* Available loglevels are 0 for 'debug', 1 for 'info', 2 for 'warn', * Available loglevels are 0 for 'debug', 1 for 'info', 2 for 'warn',
* 3 for 'error' and 4 for 'fatal'. * 3 for 'error' and 4 for 'fatal'.
* *
* When using the 'error' or 'warn' loglevels, a full stacktrace will be * When using the 'error' or 'warn' loglevels, a full stacktrace will be
* logged as well. * logged as well.
*/ */
if (message instanceof Error) { if (message instanceof Error) {
@ -176,133 +176,19 @@
} }
}; };
// ---------------------
// Translation machinery
// ---------------------
_converse.__ = function (str) { _converse.__ = function (str) {
/* Translate the given string based on the current locale. /* Translate the given string based on the current locale.
* *
* Parameters: * Parameters:
* (String) str - The string to translate. * (String) str - The string to translate.
*/ */
if (_.isUndefined(Jed)) { if (_.isUndefined(i18n)) {
return str; return str;
} }
if (_.isUndefined(_converse.jed)) { return i18n.translate.apply(i18n, arguments);
return Jed.sprintf.apply(Jed, arguments);
}
var t = _converse.jed.translate(str);
if (arguments.length>1) {
return t.fetch.apply(t, [].slice.call(arguments, 1));
} else {
return t.fetch();
}
} }
function detectLocale (library_check) { const __ = _converse.__;
/* Determine which locale is supported by the user's system as well
* as by the relevant library (e.g. converse.js or moment.js).
*
* Parameters:
* (Function) library_check - Returns a boolean indicating whether
* the locale is supported.
*/
var locale, i;
if (window.navigator.userLanguage) {
locale = isLocaleAvailable(window.navigator.userLanguage, library_check);
}
if (window.navigator.languages && !locale) {
for (i=0; i<window.navigator.languages.length && !locale; i++) {
locale = isLocaleAvailable(window.navigator.languages[i], library_check);
}
}
if (window.navigator.browserLanguage && !locale) {
locale = isLocaleAvailable(window.navigator.browserLanguage, library_check);
}
if (window.navigator.language && !locale) {
locale = isLocaleAvailable(window.navigator.language, library_check);
}
if (window.navigator.systemLanguage && !locale) {
locale = isLocaleAvailable(window.navigator.systemLanguage, library_check);
}
return locale || 'en';
}
function isMomentLocale (locale) {
if (!_.isString(locale)) { return false; }
return moment.locale() !== moment.locale(locale);
}
function getLocale (preferred_locale, isSupportedByLibrary) {
if (_.isString(preferred_locale)) {
if (preferred_locale === 'en' || isSupportedByLibrary(preferred_locale)) {
return preferred_locale;
}
}
return detectLocale(isSupportedByLibrary) || 'en';
}
function isLocaleAvailable (locale, available) {
/* Check whether the locale or sub locale (e.g. en-US, en) is supported.
*
* Parameters:
* (String) locale - The locale to check for
* (Function) available - returns a boolean indicating whether the locale is supported
*/
if (available(locale)) {
return locale;
} else {
var sublocale = locale.split("-")[0];
if (sublocale !== locale && available(sublocale)) {
return sublocale;
}
}
}
function isLocaleSupported (locale) {
/* Check whether the passed in locale is supported by Converse
*
* Parameters:
* (String) locale: The given i18n locale
*/
if (!_.isString(locale)) { return false; }
return _.includes(_converse.locales, locale);
}
function fetchTranslations (locale, locale_url) {
/* Fetch the translations for the given local at the given URL.
*
* Parameters:
* (String) locale: The given i18n locale
* (String) locale_url: The URL from which the translations should be fetched
*/
return new Promise((resolve, reject) => {
if (!isLocaleSupported(locale) || locale === 'en') {
return resolve();
}
const xhr = new XMLHttpRequest();
xhr.open('GET', locale_url, true);
xhr.setRequestHeader(
'Accept',
"application/json, text/javascript"
);
xhr.onload = function () {
if (xhr.status >= 200 && xhr.status < 400) {
resolve(new Jed(window.JSON.parse(xhr.responseText)));
} else {
xhr.onerror();
}
};
xhr.onerror = function () {
reject(xhr.statusText);
};
xhr.send();
});
}
// --------------------------
// END: Translation machinery
// --------------------------
const PROMISES = [ const PROMISES = [
'initialized', 'initialized',
@ -445,10 +331,12 @@
} }
} }
/* Internationalization */ /* Localisation */
moment.locale(getLocale(settings.i18n, isMomentLocale)); if (!_.isUndefined(i18n)) {
_converse.locale = getLocale(settings.i18n, isLocaleSupported); i18n.setLocales(settings.i18n, _converse);
const __ = _converse.__; } else {
_converse.locale = 'en';
}
// Module-level variables // Module-level variables
// ---------------------- // ----------------------
@ -2026,23 +1914,24 @@
} }
if (!_.isUndefined(_converse.connection) && if (!_.isUndefined(_converse.connection) &&
_converse.connection.service === 'jasmine tests') { _converse.connection.service === 'jasmine tests') {
finishInitialization(); finishInitialization();
return _converse; return _converse;
} else if (_.isUndefined(i18n)) {
finishInitialization();
} else { } else {
fetchTranslations( i18n.fetchTranslations(
_converse.locale, _converse.locale,
_converse.locales,
_.template(_converse.locales_url)({'locale': _converse.locale}) _.template(_converse.locales_url)({'locale': _converse.locale})
).then((jed) => { ).then(() => {
_converse.jed = jed;
finishInitialization(); finishInitialization();
}).catch((reason) => { }).catch((reason) => {
finishInitialization(); finishInitialization();
_converse.log(reason, Strophe.LogLevel.ERROR); _converse.log(reason, Strophe.LogLevel.ERROR);
}); });
return init_promise.promise;
} }
return init_promise.promise;
}; };
// API methods only available to plugins // API methods only available to plugins

142
src/i18n.js Normal file
View File

@ -0,0 +1,142 @@
// Converse.js (A browser based XMPP chat client)
// http://conversejs.org
//
// This is the internationalization module.
//
// Copyright (c) 2012-2017, Jan-Carel Brand <jc@opkode.com>
// Licensed under the Mozilla Public License (MPLv2)
//
/*global define */
(function (root, factory) {
define([
"es6-promise",
"jed",
"lodash.noconflict",
"moment_with_locales"
], factory);
}(this, function (Promise, Jed, _, moment) {
'use strict';
function detectLocale (library_check) {
/* Determine which locale is supported by the user's system as well
* as by the relevant library (e.g. converse.js or moment.js).
*
* Parameters:
* (Function) library_check - Returns a boolean indicating whether
* the locale is supported.
*/
var locale, i;
if (window.navigator.userLanguage) {
locale = isLocaleAvailable(window.navigator.userLanguage, library_check);
}
if (window.navigator.languages && !locale) {
for (i=0; i<window.navigator.languages.length && !locale; i++) {
locale = isLocaleAvailable(window.navigator.languages[i], library_check);
}
}
if (window.navigator.browserLanguage && !locale) {
locale = isLocaleAvailable(window.navigator.browserLanguage, library_check);
}
if (window.navigator.language && !locale) {
locale = isLocaleAvailable(window.navigator.language, library_check);
}
if (window.navigator.systemLanguage && !locale) {
locale = isLocaleAvailable(window.navigator.systemLanguage, library_check);
}
return locale || 'en';
}
function isMomentLocale (locale) {
return _.isString(locale) && moment.locale() === moment.locale(locale);
}
function isConverseLocale (locale, supported_locales) {
return _.isString(locale) && _.includes(supported_locales, locale);
}
function getLocale (preferred_locale, isSupportedByLibrary) {
if (_.isString(preferred_locale)) {
if (preferred_locale === 'en' || isSupportedByLibrary(preferred_locale)) {
return preferred_locale;
}
}
return detectLocale(isSupportedByLibrary) || 'en';
}
function isLocaleAvailable (locale, available) {
/* Check whether the locale or sub locale (e.g. en-US, en) is supported.
*
* Parameters:
* (String) locale - The locale to check for
* (Function) available - returns a boolean indicating whether the locale is supported
*/
if (available(locale)) {
return locale;
} else {
var sublocale = locale.split("-")[0];
if (sublocale !== locale && available(sublocale)) {
return sublocale;
}
}
}
let jed_instance;
return {
setLocales (preferred_locale, _converse) {
_converse.locale = getLocale(
preferred_locale,
_.partial(isConverseLocale, _, _converse.locales)
);
moment.locale(getLocale(preferred_locale, isMomentLocale));
},
translate (str) {
if (_.isNil(jed_instance)) {
return Jed.sprintf.apply(Jed, arguments);
}
var t = jed_instance.translate(str);
if (arguments.length>1) {
return t.fetch.apply(t, [].slice.call(arguments, 1));
} else {
return t.fetch();
}
},
fetchTranslations (locale, supported_locales, locale_url) {
/* Fetch the translations for the given local at the given URL.
*
* Parameters:
* (String) locale: The given i18n locale
* (Array) supported_locales: List of locales supported
* (String) locale_url: The URL from which the translations
* should be fetched.
*/
return new Promise((resolve, reject) => {
if (!isConverseLocale(locale, supported_locales) || locale === 'en') {
return resolve();
}
const xhr = new XMLHttpRequest();
xhr.open('GET', locale_url, true);
xhr.setRequestHeader(
'Accept',
"application/json, text/javascript"
);
xhr.onload = function () {
if (xhr.status >= 200 && xhr.status < 400) {
jed_instance = new Jed(window.JSON.parse(xhr.responseText));
resolve();
} else {
xhr.onerror();
}
};
xhr.onerror = function () {
reject(xhr.statusText);
};
xhr.send();
});
}
};
}));

View File

@ -13,7 +13,6 @@
"es6-promise", "es6-promise",
"jquery.browser", "jquery.browser",
"lodash.noconflict", "lodash.noconflict",
"moment_with_locales",
"strophe", "strophe",
], factory); ], factory);
}(this, function ( }(this, function (
@ -21,7 +20,6 @@
Promise, Promise,
jQBrowser, jQBrowser,
_, _,
moment,
Strophe Strophe
) { ) {
"use strict"; "use strict";