(function() { // when updating dep/i18next.xx.js update version here var i18nVersion = '1.7.3'; var i18n = require('./dep/i18next')//-' + i18nVersion) , url = require('url') , Cookies = require('cookies') , wrapper = {}; wrapper.version = require('../package.json').version; wrapper.clientVersion = i18nVersion; if (typeof module !== 'undefined' && module.exports) { module.exports = wrapper; } // detection subfunctions function detectLanguageFromUserland(req, res, options) { var locale, locales = []; locale = options.detectLanguageFn(req, res); if (locale) locales.push({lng: cleanLngString(locale, options), q: 1}); return locales; } function detectLanguageFromPath(req, res, options) { var locale, locales = []; var parts = req.originalUrl.split('/'); if (parts.length > options.detectLngFromPath + 1) { var part = parts[options.detectLngFromPath + 1]; var lookUp = wrapper.pluralExtensions.rules[part.split('-')[0]]; if (lookUp) locale = part; } if (locale) locales.push({lng: cleanLngString(locale, options), q: 1}); return locales; } function detectLanguageFromQuerystring(req, res, options) { var locale, locales = []; if (req.query) { locale = req.query[options.detectLngQS]; } else { var querystring = url.parse(req.url, true); locale = querystring.query[options.detectLngQS]; } if (locale) locales.push({lng: cleanLngString(locale, options), q: 1}); return locales; } function detectLanguageFromCookie(req, res, options) { var locale, locales = []; if (req.cookies) { locale = req.cookies[options.cookieName]; } else { var cookies = new Cookies(req, res); locale = cookies.get(options.cookieName); } if (locale) locales.push({lng: cleanLngString(locale, options), q: 1}); return locales; } var detectLanguageFromHeader = function(req, res, options) { var headers = req.headers; var locales = []; if (!headers) { return locales; } var acceptLanguage = headers['accept-language']; if (acceptLanguage) { var lngs = [], i; // associate language tags by their 'q' value (between 1 and 0) acceptLanguage.split(',').forEach(function(l) { var parts = l.split(';'); // 'en-GB;q=0.8' -> ['en-GB', 'q=0.8'] // get the language tag qvalue: 'q=0.8' -> 0.8 var qvalue = 1; // default qvalue for (i = 0; i < parts.length; i++) { var part = parts[i].split('='); if (part[0] === 'q' && !isNaN(part[1])) { qvalue = Number(part[1]); break; } } // add the tag and primary subtag to the qvalue associations lngs.push({lng: cleanLngString(parts[0], options), q: qvalue}); }); lngs.sort(function(a,b) { return b.q - a.q; }); for (i = 0; i < lngs.length; i++) { locales.push(lngs[i]); } } return locales; }; function checkAgainstSupportedLng(locales, supportedLngs) { if (!supportedLngs.length && locales.length) { return [locales[0]]; } else { for (var i = 0, len = locales.length; i < len; i++) { var locale = locales[i]; if (supportedLngs.indexOf(locale.lng) > -1) { return [locale]; } if (locale.lng.indexOf('-') > -1) { var unspecific = locale.lng.split('-')[0]; if (supportedLngs.indexOf(unspecific) > -1) { locale.lng = unspecific; return [locale]; } } } } return []; } function cleanLngString(lng, options) { if (typeof lng === 'string' && lng.indexOf('-') > -1) { var parts = lng.split('-'); lng = options.lowerCaseLng ? parts[0].toLowerCase() + '-' + parts[1].toLowerCase() : parts[0].toLowerCase() + '-' + parts[1].toUpperCase(); } return lng; } // overriding detect language function var detectLanguage = function(req, res) { var locales = [] , opts = wrapper.options; if (typeof req === 'object') { // user-land fn if (!locales.length && opts.detectLanguageFn) { locales = detectLanguageFromUserland(req, res, opts); locales = checkAgainstSupportedLng(locales, opts.supportedLngs); } // from path if (!locales.length && opts.detectLngFromPath !== false) { locales = detectLanguageFromPath(req, res, opts); locales = checkAgainstSupportedLng(locales, opts.supportedLngs); if (!locales.length && opts.forceDetectLngFromPath) { locales = [{lng: opts.fallbackLng[0], q: 1}]; } } // from querystring if (!locales.length) { locales = detectLanguageFromQuerystring(req, res, opts); locales = checkAgainstSupportedLng(locales, opts.supportedLngs); } // from cookie if (!locales.length && opts.useCookie) { locales = detectLanguageFromCookie(req, res, opts); locales = checkAgainstSupportedLng(locales, opts.supportedLngs); } // from headers if (!locales.length && opts.detectLngFromHeaders) { locales = detectLanguageFromHeader(req, res, opts); locales = checkAgainstSupportedLng(locales, opts.supportedLngs); } } if (!locales.length) locales.push({lng: opts.fallbackLng[0] || 'en', q: 1}); return locales[0].lng; }; // overriding for the functions in i18next.js var f = { extend: function(target, source) { if (!source || typeof source === 'function') { return target; } for (var attr in source) { target[attr] = source[attr]; } return target; }, each: function( object, callback, args ) { var name, i = 0, length = object.length, isObj = length === undefined || typeof object === "function"; if (args) { if (isObj) { for (name in object) { if (callback.apply(object[name], args) === false) { break; } } } else { for ( ; i < length; ) { if (callback.apply(object[i++], args) === false) { break; } } } // A special, fast, case for the most common use of each } else { if (isObj) { for (name in object) { if (callback.call(object[name], name, object[name]) === false) { break; } } } else { for ( ; i < length; ) { if (callback.call(object[i], i, object[i++]) === false) { break; } } } } return object; }, ajax: function() { return null; }, detectLanguage: detectLanguage, detectLanguageFromPath: detectLanguageFromPath, detectLanguageFromQuerystring: detectLanguageFromQuerystring, detectLanguageFromCookie: detectLanguageFromCookie, detectLanguageFromHeader: detectLanguageFromHeader, checkAgainstSupportedLng: checkAgainstSupportedLng, cookie: { create: function() {}, read: function() {}, remove: function() {} } }; f.extend(i18n.functions, f); // override unused sync stuff i18n.functions.extend(i18n.sync, { reload: function (cb) { this.resStore = {}; i18n.setLng(i18n.lng, cb); }, configure: function(rstore, options, functions) { this.resStore = rstore || {}; this.functions = functions; this.options = options; this._fetch = this.fetch; this._fetchOne = this.fetchOne; }, load: function(lngs, options, cb) { var self = this , missingLngs = []; for (var i = 0, len = lngs.length; i < len; i++) { if (!this.resStore[lngs[i]]) missingLngs.push(lngs[i]); } if (missingLngs.length > 0) { this.fetch(missingLngs, options, function(err, fetched) { if (err) i18n.functions.log(err); self.functions.extend(self.resStore, fetched); cb(err, self.resStore); }); } else { cb(null, self.resStore); } }, fetch: function(lngs, options, cb) { var self = this , ns = options.ns , store = {}; var todo = ns.namespaces.length * lngs.length , errors; if(ns.namespaces.length === 0) cb('namespaces not found', null); else { // load each file individual f.each(ns.namespaces, function(nsIndex, nsValue) { f.each(lngs, function(lngIndex, lngValue) { self.fetchOne(lngValue, nsValue, function(err, data) { if(err) { errors = errors || []; errors.push(err); } store[lngValue] = store[lngValue] || {}; if(err) { store[lngValue][nsValue] = {}; } else { store[lngValue][nsValue] = data; } todo--; // wait for all done befor callback if(todo === 0) { cb(errors, store); } }); }); }); } }, postMissing: function(lng, ns, key, defaultValue) { var self = this , lngs = []; if (this.options.sendMissingTo === 'fallback' && this.options.fallbackLng !== false) { lngs.push(this.options.fallbackLng[0]); } else if (this.options.sendMissingTo === 'current' || (this.options.sendMissingTo === 'fallback' && this.options.fallbackLng[0] !== false)) { lngs.push(lng); } else if (this.options.sendMissingTo === 'all') { lngs = i18n.functions.toLanguages(lng); } f.each(lngs, function(i, l) { if (!self.resStore[l] || !self.resStore[l][ns]) { self.functions.log('error saving missingKey `' + key + '` cause namespace `' + ns + '` was not loaded for language `' + l + '`.' ); } else { self.saveMissing(l, ns, key, defaultValue); } }); }, _loadLocal: function(lngs, cb) { cb('not supported'); }, _storeLocal: function(store) { return; } }); f.extend(wrapper, i18n); wrapper.detectLanguage = function () { // use the real f.detectLanguage() in case .init() overwrote it return wrapper.functions.detectLanguage.apply(wrapper, arguments); }; })();