You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nodebb/public/src/modules/translator.js

337 lines
9.6 KiB
JavaScript

;(function(translator) {
12 years ago
"use strict";
/* globals RELATIVE_PATH, config, define */
12 years ago
// export the class if we are in a Node-like system.
if (typeof module === 'object' && module.exports === translator) {
exports = module.exports = translator;
}
var languages = {},
regexes = {
match: /\[\[\w+:[^\[]*?\]\]/g,
split: /[,][\s]*/,
replace: /\]+$/
};
12 years ago
translator.addTranslation = function(language, filename, translations) {
languages[language] = languages[language] || {};
languages[language].loaded = languages[language].loaded || {};
languages[language].loaded[filename] = translations;
languages[language].loading = languages[language].loading || {};
};
translator.getTranslations = function(language, filename, callback) {
if (languages[language] && languages[language].loaded[filename]) {
callback(languages[language].loaded[filename]);
} else {
translator.load(language, filename, function() {
callback(languages[language].loaded[filename]);
});
}
10 years ago
};
translator.escape = function(text) {
return typeof text === 'string' ? text.replace(/\[\[([\S]*?)\]\]/g, '\\[\\[$1\\]\\]') : text;
10 years ago
};
translator.unescape = function(text) {
return typeof text === 'string' ? text.replace(/\\\[\\\[([\S]*?)\\\]\\\]/g, '[[$1]]') : text;
};
translator.getLanguage = function() {
return config.defaultLang;
};
translator.prepareDOM = function() {
10 years ago
// Load the appropriate timeago locale file, and correct NodeBB language codes to timeago codes, if necessary
var languageCode;
switch(config.userLang) {
case 'en_GB':
case 'en_US':
languageCode = 'en';
break;
case 'cs':
languageCode = 'cz';
break;
case 'fa_IR':
languageCode = 'fa';
break;
case 'pt_BR':
languageCode = 'pt-br';
break;
case 'nb':
languageCode = 'no';
break;
case 'zh_TW':
languageCode = 'zh-TW';
break;
case 'zh_CN':
languageCode = 'zh-CN';
break;
default:
languageCode = config.userLang;
break;
10 years ago
}
$.getScript(RELATIVE_PATH + '/vendor/jquery/timeago/locales/jquery.timeago.' + languageCode + '.js').success(function() {
$('.timeago').timeago();
translator.timeagoShort = $.extend({}, jQuery.timeago.settings.strings);
10 years ago
// Retrieve the shorthand timeago values as well
$.getScript(RELATIVE_PATH + '/vendor/jquery/timeago/locales/jquery.timeago.' + languageCode + '-short.js').success(function() {
// Switch back to long-form
translator.toggleTimeagoShorthand();
}).fail(function() {
10 years ago
$.getScript(RELATIVE_PATH + '/vendor/jquery/timeago/locales/jquery.timeago.en-short.js').success(function() {
// Switch back to long-form
translator.toggleTimeagoShorthand();
});
});
}).fail(function() {
$.getScript(RELATIVE_PATH + '/vendor/jquery/timeago/locales/jquery.timeago.en-short.js').success(function() {
// Switch back to long-form
translator.toggleTimeagoShorthand();
$.getScript(RELATIVE_PATH + '/vendor/jquery/timeago/locales/jquery.timeago.en.js');
});
10 years ago
});
10 years ago
// Add directional code if necessary
translator.translate('[[language:dir]]', function(value) {
if (value) {
$('html').css('direction', value).attr('data-dir', value);
}
});
};
translator.toggleTimeagoShorthand = function() {
10 years ago
var tmp = $.extend({}, jQuery.timeago.settings.strings);
jQuery.timeago.settings.strings = $.extend({}, translator.timeagoShort);
translator.timeagoShort = $.extend({}, tmp);
};
10 years ago
translator.translate = function (text, language, callback) {
if (typeof language === 'function') {
callback = language;
if ('undefined' !== typeof window && config) {
10 years ago
language = utils.params().lang || config.userLang || 'en_GB';
} else {
var meta = require('../../../src/meta');
language = meta.config.defaultLang || 'en_GB';
}
}
10 years ago
if (!text) {
return callback(text);
}
10 years ago
var keys = text.match(regexes.match);
if (!keys) {
10 years ago
return callback(text);
}
10 years ago
translateKeys(keys, text, language, callback);
};
function translateKeys(keys, text, language, callback) {
10 years ago
var count = keys.length;
if (!count) {
return callback(text);
}
10 years ago
var data = {text: text};
keys.forEach(function(key) {
translateKey(key, data, language, function(translated) {
--count;
if (count <= 0) {
keys = translated.text.match(regexes.match);
if (!keys) {
callback(translated.text);
} else {
translateKeys(keys, translated.text, language, callback);
}
10 years ago
}
});
});
}
10 years ago
function translateKey(key, data, language, callback) {
key = '' + key;
var variables = key.split(regexes.split);
10 years ago
var parsedKey = key.replace('[[', '').replace(']]', '').split(':');
parsedKey = [parsedKey[0]].concat(parsedKey.slice(1).join(':'));
10 years ago
if (!(parsedKey[0] && parsedKey[1])) {
return callback(data);
12 years ago
}
10 years ago
var languageFile = parsedKey[0];
parsedKey = ('' + parsedKey[1]).split(',')[0];
translator.load(language, languageFile, function(languageData) {
data.text = insertLanguage(data.text, key, languageData[parsedKey], variables);
callback(data);
});
}
12 years ago
10 years ago
function insertLanguage(text, key, value, variables) {
if (value) {
for (var i = 1, ii = variables.length; i < ii; i++) {
var variable = variables[i].replace(']]', '');
value = value.replace('%' + i, variable);
12 years ago
}
10 years ago
text = text.replace(key, value);
} else {
var string = key.split(':');
text = text.replace(key, string[string.length-1].replace(regexes.replace, ''));
12 years ago
}
10 years ago
return text;
}
12 years ago
translator.compile = function() {
var args = Array.prototype.slice.call(arguments, 0);
return '[[' + args.join(', ') + ']]';
};
translator.load = function (language, filename, callback) {
if (isLanguageFileLoaded(language, filename)) {
if (callback) {
callback(languages[language].loaded[filename]);
}
} else if (isLanguageFileLoading(language, filename)) {
if (callback) {
addLanguageFileCallback(language, filename, callback);
}
} else {
languages[language] = languages[language] || {loading: {}, loaded: {}, callbacks: []};
languages[language].loading[filename] = true;
load(language, filename, function(translations) {
languages[language].loaded[filename] = translations;
if (callback) {
callback(translations);
}
while (languages[language].callbacks && languages[language].callbacks[filename] && languages[language].callbacks[filename].length) {
languages[language].callbacks[filename].pop()(translations);
}
languages[language].loading[filename] = false;
});
}
};
function isLanguageFileLoaded(language, filename) {
var languageObj = languages[language];
return languageObj && languageObj.loaded && languageObj.loaded[filename] && !languageObj.loading[filename];
}
function isLanguageFileLoading(language, filename) {
return languages[language] && languages[language].loading && languages[language].loading[filename];
}
function addLanguageFileCallback(language, filename, callback) {
languages[language].callbacks = languages[language].callbacks || {};
languages[language].callbacks[filename] = languages[language].callbacks[filename] || [];
languages[language].callbacks[filename].push(callback);
}
function load(language, filename, callback) {
if ('undefined' !== typeof window) {
loadClient(language, filename, callback);
} else {
loadServer(language, filename, callback);
}
}
function loadClient(language, filename, callback) {
$.getJSON(config.relative_path + '/language/' + language + '/' + filename + '.json?v=' + config['cache-buster'], callback);
}
function loadServer(language, filename, callback) {
var fs = require('fs'),
path = require('path'),
winston = require('winston'),
10 years ago
file = require('../../../src/file'),
9 years ago
plugins = require('../../../src/plugins'),
meta = require('../../../src/meta');
11 years ago
language = language || meta.config.defaultLang || 'en_GB';
10 years ago
if (!file.existsSync(path.join(__dirname, '../../language', language))) {
winston.warn('[translator] Language \'' + meta.config.defaultLang + '\' not found. Defaulting to \'en_GB\'');
language = 'en_GB';
}
fs.readFile(path.join(__dirname, '../../language', language, filename + '.json'), function(err, data) {
9 years ago
var onData = function(data) {
try {
data = JSON.parse(data.toString());
} catch (e) {
winston.error('Could not parse `' + filename + '.json`, syntax error? Skipping...');
data = {};
}
callback(data);
}
9 years ago
if (err) {
if (err.code === 'ENOENT' && plugins.customLanguageFallbacks.hasOwnProperty(filename)) {
// Resource non-existant but fallback exists
return fs.readFile(plugins.customLanguageFallbacks[filename], {
encoding: 'utf-8'
}, function(err, data) {
if (err) {
return winston.error('[translator] Could not load fallback language file for resource ' + filename);
}
onData(data);
})
} else {
winston.error('[translator] Could not load `' + filename + '`: ' + err.message + '. Skipping...');
return callback({});
}
}
9 years ago
onData(data);
});
}
// Use the define() function if we're in AMD land
if (typeof define === 'function' && define.amd) {
define('translator', translator);
12 years ago
var _translator = translator;
// Expose a global `translator` object for backwards compatibility
window.translator = {
translate: function() {
10 years ago
if (typeof console !== 'undefined' && console.warn) {
console.warn('[translator] Global invocation of the translator is now deprecated, please `require` the module instead.');
}
_translator.translate.apply(_translator, arguments);
}
}
12 years ago
}
})(
typeof exports === 'object' ? exports :
typeof define === 'function' && define.amd ? {} :
translator = {}
);