i18n-abide
Advanced tools
Comparing version 0.0.7 to 0.0.8
@@ -24,3 +24,3 @@ var fs = require('fs'), | ||
} | ||
path.exists(path.join(__dirname, '..', 'locale', locale, 'LC_MESSAGES', 'messages.po'), function (m_exists) { | ||
fs.exists(path.join(__dirname, '..', 'locale', locale, 'LC_MESSAGES', 'messages.po'), function (m_exists) { | ||
if (! m_exists) { | ||
@@ -27,0 +27,0 @@ console.error("Language ", lang, " doesn't exist. Expected", |
@@ -11,5 +11,5 @@ var fs = require('fs'), | ||
files.forEach(function (file, i) { | ||
path.exists(path.join(localeDir, file, 'LC_MESSAGES', 'client.po'), function (c_exists) { | ||
fs.exists(path.join(localeDir, file, 'LC_MESSAGES', 'client.po'), function (c_exists) { | ||
if (c_exists) { | ||
path.exists(path.join(localeDir, file, 'LC_MESSAGES', 'messages.po'), function (m_exists) { | ||
fs.exists(path.join(localeDir, file, 'LC_MESSAGES', 'messages.po'), function (m_exists) { | ||
if (m_exists) { | ||
@@ -16,0 +16,0 @@ allLocales.push(i18n.languageFrom(file)); |
@@ -92,3 +92,3 @@ #!/usr/bin/env node | ||
// comments | ||
} else if (/^(#[^~]|#$)/.test(lines[i])) { | ||
} else if (/^(#|#$)/.test(lines[i])) { | ||
continue; | ||
@@ -95,0 +95,0 @@ |
@@ -14,3 +14,3 @@ # i18n Support | ||
In JavaScript or EJS templates use `gettext` or `ngettext`. If you need to do string interpolation, use the | ||
In JavaScript or EJS templates use `gettext`. If you need to do string interpolation, use the | ||
[format](../lib/i18n.js) function, which is kind of like node.js' util.format, except crappier. | ||
@@ -30,3 +30,3 @@ | ||
* locale - OS level locale code | ||
* gettext, ngettext - Gettext functions | ||
* gettext - Gettext function | ||
* format - for string interpolation | ||
@@ -45,10 +45,14 @@ | ||
## Contributions | ||
``scripts/check-po.sh`` - Prints statistics on various .po files, assumes `locale` directory. | ||
## Debugging | ||
If code is evaluated in node.js (server-side) then node-gettext is providing the string | ||
translation. Strings are from the messages.mo files under the locale directory. MO files | ||
are binary, compiled from the PO files. | ||
If code is evaluated in node.js (server-side) then translation is provided from the | ||
`i18n/{locale}/messages.json` file which contains the translation. These JSON files come | ||
from PO files. | ||
If code is evaluated on the client-side, then resources/static/shared/gettext.js is in | ||
the house... strings are from resources/static/i18n JSON files. | ||
If code is evaluated on the client-side, then `static/gettext.js` is in | ||
the house... strings are `i18n/{locale}/client.json` files. | ||
@@ -58,2 +62,6 @@ If code is evaluated in your head, then clearly we are post-singularity. Why are you | ||
You can change `i18n` in the above to examples by setting the `translation_directory` in your | ||
options to i18n-abide. If you do server side and client side strings, it is recommended that you | ||
put your translation_directory under a web accessible directory like `static`. | ||
Use the eo locale for development and debugging. It is auto-translated with: | ||
@@ -60,0 +68,0 @@ for catalog in messages client; do |
260
lib/i18n.js
@@ -18,11 +18,20 @@ /* This Source Code Form is subject to the terms of the Mozilla Public | ||
var Gettext = require('node-gettext'), | ||
fs = require('fs'), | ||
var fs = require('fs'), | ||
gobbledygook = require('gobbledygook'), | ||
path = require('path'), | ||
util = require('util'); | ||
const BIDI_RTL_LANGS = ['ar', 'db-LB', 'fa', 'he']; | ||
const DAVID_B_LABYRN = 'db-LB'; | ||
const BIDI_RTL_LANGS = ['ar', DAVID_B_LABYRN, 'fa', 'he']; | ||
var mo_cache = {}; | ||
// Number of characters before and after valid JSON in messages.json files | ||
const JS_PRE_LEN = 24; | ||
const JS_POST_LEN = 3; | ||
var translations = {}; | ||
var logger; | ||
// forward references | ||
var localeFrom, parseAcceptLanguage, bestLanguage, format, languageFrom; | ||
/** | ||
@@ -35,54 +44,48 @@ * Connect middleware which is i18n aware. | ||
default_lang: 'en-US', | ||
locale_directory: 'locale' | ||
translation_directory: 'locale' | ||
})); | ||
* | ||
* Other valid options: gettext_alias, ngettext_alias | ||
* Other valid options: gettext_alias, debug_lang, disable_locale_check, | ||
* or logger | ||
*/ | ||
exports.abide = function (options) { | ||
exports.abide = function(options) { | ||
options = options || {}; | ||
if (! options.gettext_alias) options.gettext_alias = 'gettext'; | ||
if (! options.ngettext_alias) options.ngettext_alias = 'ngettext'; | ||
if (! options.supported_languages) options.supported_languages = ['en-US']; | ||
if (! options.default_lang) options.default_lang = 'en-US'; | ||
if (! options.debug_lang) options.debug_lang = 'it-CH'; | ||
if (! options.disable_locale_check) options.disable_locale_check = false; | ||
if (! options.locale_directory) options.locale_directory = 'locale'; | ||
if (! options.i18n_json_dir) options.i18n_json_dir = 'resources/static/i18n/'; | ||
if (! options.logger) options.logger = console; | ||
var logger = options.logger; | ||
if (! options.gettext_alias) options.gettext_alias = 'gettext'; | ||
if (! options.supported_languages) options.supported_languages = ['en-US']; | ||
if (! options.default_lang) options.default_lang = 'en-US'; | ||
if (! options.debug_lang) options.debug_lang = 'it-CH'; | ||
if (! options.disable_locale_check) options.disable_locale_check = false; | ||
if (! options.translation_directory) options.translation_directory = 'l18n/'; | ||
if (! options.logger) options.logger = console; | ||
var mo_file_path = function (locale) { | ||
return path.resolve( | ||
path.join(__dirname, '..', '..', '..'), | ||
options.locale_directory, | ||
path.join(locale, 'LC_MESSAGES', 'messages.mo')); | ||
}, /* TODO This would be a good check if we're using client side gettext | ||
json_file_path = function (locale) { | ||
return path.resolve( | ||
path.join(__dirname, '..', '..', '..'), | ||
path.join(options.i18n_json_dir, locale, 'messages.json')); | ||
}, */ | ||
debug_locale = localeFrom(options.debug_lang); | ||
logger = options.logger; | ||
options.supported_languages.forEach(function (lang, i) { | ||
var l = localeFrom(lang), | ||
default_locale = localeFrom(options.default_lang); | ||
function json_file_path(locale) { | ||
return path.resolve(path.join(__dirname, '..', '..', '..'), | ||
options.translation_directory, | ||
path.join(locale, 'messages.json')); | ||
} | ||
mo_cache[l] = { | ||
mo_exists: path.existsSync(mo_file_path(l)), | ||
/* json_exists: path.existsSync(json_file_path(l)), */ | ||
gt: null | ||
}; | ||
if (l !== debug_locale) { | ||
if (! mo_cache[l] || ! mo_cache[l].mo_exists /* || ! mo_cache[l].json_exists*/ ) { | ||
var msg = util.format('Bad locale=[%s] file(s) do not exist [%s]. See locale/README', | ||
l, mo_file_path(l)/*, json_file_path(l)*/); | ||
if (/* mo_cache[l].json_exists && */ l == default_locale) { | ||
// mo files aren't critical... carry on | ||
if (! options.disable_locale_check) logger.warn(msg); | ||
} else { | ||
logger.error(msg); | ||
throw msg; | ||
} | ||
options.supported_languages.forEach(function(lang) { | ||
var l = localeFrom(lang); | ||
// populate the in-memory translation cache with client.json, which contains | ||
// strings relevant on the server | ||
try { | ||
var rawMessages = fs.readFileSync(json_file_path(l)).toString(); | ||
translations[l] = JSON.parse(rawMessages).messages; | ||
} catch (e) { | ||
// an exception here means that there was a problem with the translation | ||
// files for this locale! | ||
if (options.default_lang === lang || options.debug_lang === lang) return; | ||
var msg = util.format( | ||
'Bad locale=[%s] missing .json files in [%s]. See locale/README (%s)', | ||
l, json_file_path(l), e); | ||
if (!options.disable_locale_check) { | ||
logger.warn(msg); | ||
} else { | ||
logger.error(msg); | ||
throw msg; | ||
} | ||
@@ -98,13 +101,28 @@ } | ||
debug_lang = options.debug_lang.toLowerCase(), | ||
locale; | ||
locale, | ||
locals = {}, | ||
gt; | ||
if (lang && lang.toLowerCase && lang.toLowerCase() == debug_lang) { | ||
lang = 'db-LB'; // What? http://www.youtube.com/watch?v=rJLnGjhPT1Q | ||
if (lang && lang.toLowerCase && lang.toLowerCase() === debug_lang) { | ||
// What? http://www.youtube.com/watch?v=rJLnGjhPT1Q | ||
lang = DAVID_B_LABYRN; | ||
} | ||
// Express 2 support | ||
if (!! resp.local) { | ||
resp.locals = function(args, orValue) { | ||
if ('string' === typeof args) { | ||
resp.local(args, orValue); | ||
} else { | ||
Object.keys(args).forEach(function(key) { | ||
resp.local(key, args[key]); | ||
}); | ||
} | ||
}; | ||
} | ||
resp.local('lang', lang); | ||
locals.lang = lang; | ||
// BIDI support, which direction does text flow? | ||
lang_dir = BIDI_RTL_LANGS.indexOf(lang) >= 0 ? 'rtl' : 'ltr'; | ||
resp.local('lang_dir', lang_dir); | ||
locals.lang_dir = lang_dir; | ||
req.lang = lang; | ||
@@ -114,34 +132,54 @@ | ||
resp.local('locale', locale); | ||
locals.locale = locale; | ||
req.locale = locale; | ||
resp.local('format', format); | ||
locals.format = format; | ||
req.format = format; | ||
if (mo_cache[locale].mo_exists) { | ||
if (mo_cache[locale].gt === null) { | ||
mo_cache[locale].gt = new Gettext(); | ||
mo_path = mo_file_path(locale); | ||
mo_cache[locale].gt.addTextdomain(locale, | ||
fs.readFileSync(mo_path)); | ||
mo_cache[locale].gt.textdomain(locale); | ||
locals.setLocale = function(assignedLocale) { | ||
if (translations[assignedLocale]) { | ||
locale = assignedLocale; | ||
var newLocals = {} | ||
newLocals.locale = assignedLocale; | ||
req.locale = assignedLocale; | ||
newLocals.lang = languageFrom(assignedLocale); | ||
req.lang = newLocals.lang; | ||
newLocals.lang_dir = BIDI_RTL_LANGS.indexOf(newLocals.lang) >= 0 ? 'rtl' : 'ltr'; | ||
req.lang_dir = newLocals.lang_dir; | ||
resp.locals(newLocals); | ||
} | ||
var gt = mo_cache[locale].gt; | ||
resp.local(options.gettext_alias, gt.gettext.bind(gt)); | ||
req.gettext = gt.gettext.bind(gt); | ||
resp.local(options.ngettext_alias, gt.ngettext.bind(gt)); | ||
req.ngettext = gt.ngettext.bind(gt); | ||
} else { | ||
// en-US in a non gettext environment... fake it | ||
var identity = function (a, b) { return a; }; | ||
resp.local(options.gettext_alias, identity); | ||
req.gettext = identity; | ||
resp.local(options.ngettext_alias, identity); | ||
req.ngettext = identity; | ||
}; | ||
req.setLocale = locals.setLocale; | ||
if (lang.toLowerCase() === DAVID_B_LABYRN.toLowerCase()) { | ||
gt = gobbledygook; | ||
locals.lang = DAVID_B_LABYRN; | ||
} else if (translations[locale]) { | ||
gt = function(sid) { | ||
if (translations[locale][sid] && translations[locale][sid][1].length) { | ||
sid = translations[locale][sid][1]; | ||
} | ||
return sid; | ||
}; | ||
} else { | ||
// default lang in a non gettext environment... fake it | ||
gt = function(a) { return a; }; | ||
} | ||
locals[options.gettext_alias] = gt; | ||
req.gettext = gt; | ||
// resp.locals(string, value) doesn't seem to work with EJS | ||
resp.locals(locals); | ||
next(); | ||
}; | ||
}; | ||
function qualityCmp(a, b) { | ||
if (a.quality == b.quality) { | ||
if (a.quality === b.quality) { | ||
return 0; | ||
@@ -153,3 +191,3 @@ } else if (a.quality < b.quality) { | ||
} | ||
}; | ||
} | ||
@@ -163,21 +201,21 @@ /** | ||
*/ | ||
exports.parseAcceptLanguage = parseAcceptLanguage = function (header) { | ||
// pl,fr-FR;q=0.3,en-US;q=0.1 | ||
if (! header || ! header.split) { | ||
return []; | ||
exports.parseAcceptLanguage = parseAcceptLanguage = function(header) { | ||
// pl,fr-FR;q=0.3,en-US;q=0.1 | ||
if (! header || ! header.split) { | ||
return []; | ||
} | ||
var raw_langs = header.split(','); | ||
var langs = raw_langs.map(function(raw_lang) { | ||
var parts = raw_lang.split(';'); | ||
var q = 1; | ||
if (parts.length > 1 && parts[1].indexOf('q=') === 0) { | ||
var qval = parseFloat(parts[1].split('=')[1]); | ||
if (isNaN(qval) === false) { | ||
q = qval; | ||
} | ||
} | ||
var raw_langs = header.split(','); | ||
var langs = raw_langs.map(function (raw_lang) { | ||
var parts = raw_lang.split(';'); | ||
var q = 1; | ||
if (parts.length > 1 && parts[1].indexOf('q=') == 0) { | ||
qval = parseFloat(parts[1].split('=')[1]); | ||
if (isNaN(qval) === false) { | ||
q = qval; | ||
} | ||
} | ||
return { lang: parts[0].trim(), quality: q }; | ||
}); | ||
langs.sort(qualityCmp); | ||
return langs; | ||
return { lang: parts[0].trim(), quality: q }; | ||
}); | ||
langs.sort(qualityCmp); | ||
return langs; | ||
}; | ||
@@ -191,3 +229,3 @@ | ||
exports.bestLanguage = bestLanguage = function(languages, supported_languages, defaultLanguage) { | ||
var lower = supported_languages.map(function (l) { return l.toLowerCase(); }); | ||
var lower = supported_languages.map(function(l) { return l.toLowerCase(); }); | ||
for(var i=0; i < languages.length; i++) { | ||
@@ -198,4 +236,4 @@ var lq = languages[i]; | ||
// Issue#1128 match locale, even if region isn't supported | ||
} else if (lower.indexOf(lq.lang.slice(0, 2).toLowerCase()) !== -1) { | ||
return lq.lang.slice(0, 2); | ||
} else if (lower.indexOf(lq.lang.split('-')[0].toLowerCase()) !== -1) { | ||
return lq.lang.split('-')[0]; | ||
} | ||
@@ -212,5 +250,5 @@ } | ||
*/ | ||
exports.localeFrom = localeFrom = function (language) { | ||
exports.localeFrom = localeFrom = function(language) { | ||
if (! language || ! language.split) { | ||
return ""; | ||
return ""; | ||
} | ||
@@ -226,3 +264,4 @@ var parts = language.split('-'); | ||
} else { | ||
logger.error(util.format("Unable to map a local from language code [%s]", language)); | ||
logger.error( | ||
util.format("Unable to map a local from language code [%s]", language)); | ||
return language; | ||
@@ -235,3 +274,3 @@ } | ||
*/ | ||
exports.languageFrom = function (locale) { | ||
exports.languageFrom = languageFrom = function(locale) { | ||
if (!locale || !locale.split) { | ||
@@ -249,6 +288,7 @@ return ""; | ||
} else { | ||
logger.error(util.format("Unable to map a language from locale code [%s]", locale)); | ||
logger.error( | ||
util.format("Unable to map a language from locale code [%s]", locale)); | ||
return locale; | ||
} | ||
} | ||
}; | ||
@@ -265,9 +305,13 @@ /** | ||
*/ | ||
exports.format = format = function (fmt, obj, named) { | ||
if (! fmt) return ""; | ||
if (named) { | ||
return fmt.replace(/%\(\w+\)s/g, function(match){return String(obj[match.slice(2,-2)])}); | ||
exports.format = format = function(fmt, obj, named) { | ||
if (!fmt) return ""; | ||
if (Array.isArray(obj) || named === false) { | ||
return fmt.replace(/%s/g, function(){return String(obj.shift());}); | ||
} else if (typeof obj === 'object' || named === true) { | ||
return fmt.replace(/%\(\s*([^)]+)\s*\)s/g, function(m, v){ | ||
return String(obj[v.trim()]); | ||
}); | ||
} else { | ||
return fmt.replace(/%s/g, function(match){return String(obj.shift())}); | ||
return fmt; | ||
} | ||
}; |
@@ -5,3 +5,3 @@ { | ||
"description": "Express/connect module for Node i18n and l10n support", | ||
"version": "0.0.7", | ||
"version": "0.0.8", | ||
"homepage": "https://github.com/mozilla/i18n-abide", | ||
@@ -21,4 +21,4 @@ "repository": { | ||
"async": "0.1.22", | ||
"jsxgettext": "0.0.4", | ||
"node-gettext": "0.2.8", | ||
"gobbledygook": "0.0.3", | ||
"jsxgettext": "0.1.3", | ||
"optimist": "0.3.4" | ||
@@ -30,4 +30,14 @@ }, | ||
"scripts": { | ||
"test": "NODE_PATH=lib node_modules/.bin/vows tests/*-tests.js" | ||
"test": "NODE_PATH=lib node_modules/.bin/vows tests/*-tests.js --spec" | ||
}, | ||
"bin": { | ||
"check-l10n-config": "bin/check-l10n-config.js", | ||
"check-po": "bin/check-po.sh", | ||
"compile-json": "bin/compile-json", | ||
"compile-mo": "bin/compile-mo.sh", | ||
"every-locale.js": "bin/every-locale.js", | ||
"extract-pot": "bin/extract-pot", | ||
"merge-po": "bin/merge-po.sh", | ||
"po2json.js": "bin/po2json.js" | ||
}, | ||
"licenses": [ { | ||
@@ -34,0 +44,0 @@ "type": "MPL 2.0", |
@@ -12,8 +12,22 @@ # i18n-abide | ||
This module is under development, currently being extracted from [BrowserID](https://github.com/mozilla/browserid). | ||
This module is under development, but frozen parts of it power the [Mozilla Persona](https://github.com/mozilla/browserid) service in 40+ languages. | ||
# Pre-requisites | ||
# Tutorial | ||
Mozilla Hacks blog has a three part introduction. | ||
* [Localize Your Node.js Service](https://hacks.mozilla.org/2013/04/localize-your-node-js-service-part-1-of-3-a-node-js-holiday-season-part-9/) | ||
* [Localization community, tools & process](https://hacks.mozilla.org/2013/04/localization-community-tools-process-part-2-of-3-a-node-js-holiday-season-part-10/) | ||
* [Localization in Action](https://hacks.mozilla.org/2013/04/localization-in-action-part-3-of-3-a-node-js-holiday-season-part-11/) | ||
# Pre-requisites for Developers | ||
`npm install` has got your back | ||
# Pre-requisites for String Wranglers | ||
You should install Gnu gettext to get msginit, xgettext, and other tools. | ||
What is a string wrangler? A person or an automated build process that will merge and delete strings between releases. | ||
# Usage | ||
@@ -31,3 +45,3 @@ | ||
debug_lang: 'it-CH', | ||
locale_directory: 'locale' | ||
translation_directory: 'i18n' | ||
})); | ||
@@ -40,6 +54,13 @@ | ||
exports.homepage = function (req, resp) { | ||
exports.homepage = function(req, resp) { | ||
resp.render('home', {title: req.gettext("Hey, careful, man, there's a beverage here!")}); | ||
}; | ||
You can set locale in the scope of per-request instead of letting ``i18n-abide`` decide the locale for you. The following example shows how to set the locale of the request to ``zh_TW`` (Traditional Chinese): | ||
exports.homepage = function(req, resp) { | ||
req.setLocale('zh_TW'); | ||
resp.render('home', {title: "my title"}); | ||
}; | ||
In your layout files, you can add | ||
@@ -59,3 +80,6 @@ | ||
These are both server side translations with ``node-gettext``. If you also want to do client-side translations, | ||
These are both server side translations and client side translations. Server side works out of the box | ||
and is the most common use case. | ||
If you also want to do client-side translations, | ||
i18n-abide provides ``lib/gettext.js`` and you can do the same in ``.js`` and ``.ejs`` files. | ||
@@ -66,3 +90,3 @@ | ||
$ mkdir -p locale/templates/LC_MESSAGES | ||
$ ./node_modules/.bin/extract-pot --locale locale ./server | ||
$ ./node_modules/.bin/extract-pot --locale locale . | ||
@@ -74,3 +98,3 @@ If you look in ``locale/templates/LC_MESSAGES/messages.pot`` you will see your strings have been extracted. | ||
$ extract-pot --locale locale ./server --exclude tests --exclude examples | ||
$ extract-pot --locale locale . --exclude tests --exclude examples | ||
@@ -82,7 +106,7 @@ Example messages.pot: | ||
#: server/routes.js:81 | ||
#: ./routes.js:81 | ||
msgid "Hey, careful, man, there's a beverage here!" | ||
msgstr "" | ||
#: server/views/404.ejs:5 | ||
#: views/404.ejs:5 | ||
msgid "This will not stand, ya know, this aggression will not stand, man." | ||
@@ -125,7 +149,7 @@ msgstr "" | ||
#: server/routes.js:81 | ||
#: routes.js:81 | ||
msgid "Hey, careful, man, there's a beverage here!" | ||
msgstr "Hǝʎ´ ɔɐɹǝɟnʅ´ ɯɐu´ ʇɥǝɹǝ,s ɐ qǝʌǝɹɐƃǝ ɥǝɹǝ¡" | ||
#: server/views/404.ejs:5 | ||
#: views/404.ejs:5 | ||
msgid "This will not stand, ya know, this aggression will not stand, man." | ||
@@ -140,2 +164,2 @@ msgstr "⊥ɥıs ʍıʅʅ uoʇ sʇɐup´ ʎɐ ʞuoʍ´ ʇɥıs ɐƃƃɹǝssıou ʍıʅʅ uoʇ sʇɐup´ ɯɐu·" | ||
See docs/USAGE.md for full details. | ||
See docs/USAGE.md for full details. |
@@ -15,6 +15,6 @@ #!/usr/bin/env node | ||
"format a string with place values": { | ||
topic: function () { | ||
topic: function() { | ||
return i18n.format("%s %s!", ["Hello", "World"]); | ||
}, | ||
"was interpolated": function (err, str) { | ||
"was interpolated": function(err, str) { | ||
assert.equal(str, "Hello World!"); | ||
@@ -27,9 +27,18 @@ } | ||
"format a string with named values": { | ||
topic: function () { | ||
topic: function() { | ||
var params = { salutation: "Hello", place: "World" }; | ||
return i18n.format("%(salutation)s %(place)s!", params, true); | ||
}, | ||
"was interpolated": function (err, str) { | ||
"was interpolated": function(err, str) { | ||
assert.equal(str, "Hello World!"); | ||
} | ||
}, | ||
"named values can have spaces": { | ||
topic: function() { | ||
var params = { salutation: "Hello", place: "World" }; | ||
return i18n.format("%( salutation )s %( place )s!", params, true); | ||
}, | ||
"was interpolated": function(err, str) { | ||
assert.equal(str, "Hello World!"); | ||
} | ||
} | ||
@@ -40,6 +49,6 @@ }); | ||
"format a string without interpolation": { | ||
topic: function () { | ||
topic: function() { | ||
return i18n.format("Hello World!"); | ||
}, | ||
"was interpolated": function (err, str) { | ||
"was interpolated": function(err, str) { | ||
assert.equal(str, "Hello World!"); | ||
@@ -49,6 +58,6 @@ } | ||
"format a null": { | ||
topic: function () { | ||
topic: function() { | ||
return i18n.format(null); | ||
}, | ||
"was interpolated": function (err, str) { | ||
"was interpolated": function(err, str) { | ||
assert.equal(str, ""); | ||
@@ -61,3 +70,3 @@ } | ||
"We find exact language match": { | ||
topic: function () { | ||
topic: function() { | ||
var accept = 'pa,sv;q=0.8,fi;q=0.7,it-ch;q=0.5,en-us;q=0.3,en;q=0.2'; | ||
@@ -67,6 +76,6 @@ var supported = ['af', 'en-US', 'pa']; | ||
return i18n.bestLanguage( | ||
parseAcceptLanguage(accept), | ||
i18n.parseAcceptLanguage(accept), | ||
supported, def); | ||
}, | ||
"For Punjabi": function (err, locale) { | ||
"For Punjabi": function(err, locale) { | ||
assert.equal(locale, "pa"); | ||
@@ -76,3 +85,3 @@ } | ||
"Issue#1128 We find best locale even if region doesn't match": { | ||
topic: function () { | ||
topic: function() { | ||
var accept = 'pa-it,sv;q=0.8,fi;q=0.7,it-ch;q=0.5,en-us;q=0.3,en;q=0.2'; | ||
@@ -82,6 +91,6 @@ var supported = ['af', 'en-US', 'pa']; | ||
return i18n.bestLanguage( | ||
parseAcceptLanguage(accept), | ||
i18n.parseAcceptLanguage(accept), | ||
supported, def); | ||
}, | ||
"For Punjabi (India) serve Punjabi": function (err, locale) { | ||
"For Punjabi (India) serve Punjabi": function(err, locale) { | ||
assert.equal(locale, "pa"); | ||
@@ -91,3 +100,3 @@ } | ||
"We don't extend into a region, unless we have an exact match": { | ||
topic: function () { | ||
topic: function() { | ||
var accept = 'pa,sv;q=0.8,fi;q=0.7,it-ch;q=0.5,en-us;q=0.3,en;q=0.2'; | ||
@@ -97,8 +106,65 @@ var supported = ['af', 'en-US', 'pa-IT']; | ||
return i18n.bestLanguage( | ||
parseAcceptLanguage(accept), | ||
i18n.parseAcceptLanguage(accept), | ||
supported, def); | ||
}, | ||
"Don't choose Punjabi (India)": function (err, locale) { | ||
"Don't choose Punjabi (India)": function(err, locale) { | ||
assert.equal(locale, "en-us"); | ||
} | ||
}, | ||
"Issue#806649 Don't match Finnish to Ligurian": { | ||
topic: function() { | ||
var accept = 'fil-PH,fil;q=0.97,en-US;q=0.94,en;q=0.91,en-ph;' + | ||
'q=0.89,en-gb;q=0.86,hu-HU;q=0.83,hu;q=0.8,en-AU;q=0.77,en-nl;' + | ||
'q=0.74,nl-en;q=0.71,nl;q=0.69,en-HK;q=0.66,en-sg;q=0.63,en-th;' + | ||
'q=0.6,pl-PL;q=0.57,pl;q=0.54,fr-FR;q=0.51,fr;q=0.49,en-AE;' + | ||
'q=0.46,zh-CN;q=0.43,zh;q=0.4,ja-JP;q=0.37,ja;q=0.34,id-ID;' + | ||
'q=0.31,id;q=0.29,ru-RU;q=0.26,ru;q=0.23,de-DE;q=0.2,de;' + | ||
'q=0.17,ko-KR;q=0.14,ko;q=0.11,es-ES;q=0.09,es;q=0.06,en-AP;q=0.0'; | ||
var supported = ['en-US', 'fi']; | ||
var def = 'en-US'; | ||
return i18n.bestLanguage( | ||
i18n.parseAcceptLanguage(accept), | ||
supported, def); | ||
}, | ||
"Don't choose fi (Finnish)": function(err, locale) { | ||
assert.equal(locale, "en-US"); | ||
} | ||
}, | ||
"Do support Ligurian": { | ||
topic: function() { | ||
var accept = 'fil-PH,fil;q=0.97,en-US;q=0.94,en;q=0.91,en-ph;' + | ||
'q=0.89,en-gb;q=0.86,hu-HU;q=0.83,hu;q=0.8,en-AU;q=0.77,en-nl;' + | ||
'q=0.74,nl-en;q=0.71,nl;q=0.69,en-HK;q=0.66,en-sg;q=0.63,en-th;' + | ||
'q=0.6,pl-PL;q=0.57,pl;q=0.54,fr-FR;q=0.51,fr;q=0.49,en-AE;' + | ||
'q=0.46,zh-CN;q=0.43,zh;q=0.4,ja-JP;q=0.37,ja;q=0.34,id-ID;' + | ||
'q=0.31,id;q=0.29,ru-RU;q=0.26,ru;q=0.23,de-DE;q=0.2,de;' + | ||
'q=0.17,ko-KR;q=0.14,ko;q=0.11,es-ES;q=0.09,es;q=0.06,en-AP;q=0.0'; | ||
var supported = ['en-US', 'fi', 'fil-PH']; | ||
var def = 'en-US'; | ||
return i18n.bestLanguage( | ||
i18n.parseAcceptLanguage(accept), | ||
supported, def); | ||
}, | ||
"Matches full locale": function(err, locale) { | ||
assert.equal(locale, "fil-PH"); | ||
} | ||
}, | ||
"Do support Ligurian without region": { | ||
topic: function() { | ||
var accept = 'fil-PH,fil;q=0.97,en-US;q=0.94,en;q=0.91,en-ph;' + | ||
'q=0.89,en-gb;q=0.86,hu-HU;q=0.83,hu;q=0.8,en-AU;q=0.77,en-nl;' + | ||
'q=0.74,nl-en;q=0.71,nl;q=0.69,en-HK;q=0.66,en-sg;q=0.63,en-th;' + | ||
'q=0.6,pl-PL;q=0.57,pl;q=0.54,fr-FR;q=0.51,fr;q=0.49,en-AE;' + | ||
'q=0.46,zh-CN;q=0.43,zh;q=0.4,ja-JP;q=0.37,ja;q=0.34,id-ID;' + | ||
'q=0.31,id;q=0.29,ru-RU;q=0.26,ru;q=0.23,de-DE;q=0.2,de;' + | ||
'q=0.17,ko-KR;q=0.14,ko;q=0.11,es-ES;q=0.09,es;q=0.06,en-AP;q=0.0'; | ||
var supported = ['en-US', 'fi', 'fil']; | ||
var def = 'en-US'; | ||
return i18n.bestLanguage( | ||
i18n.parseAcceptLanguage(accept), | ||
supported, def); | ||
}, | ||
"Matches partial locale": function(err, locale) { | ||
assert.equal(locale, "fil"); | ||
} | ||
} | ||
@@ -105,0 +171,0 @@ }); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 3 instances in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 2 instances in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 6 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
Mixed license
License(Experimental) Package contains multiple licenses.
Found 1 instance in 1 package
Non-existent author
Supply chain riskThe package was published by an npm account that no longer exists.
Found 1 instance in 1 package
Mixed license
License(Experimental) Package contains multiple licenses.
Found 1 instance in 1 package
1662984
710
37583
157
0
3
160
41
+ Addedgobbledygook@0.0.3
+ Addedamdefine@1.0.1(transitive)
+ Addedbalanced-match@1.0.2(transitive)
+ Addedbrace-expansion@2.0.1(transitive)
+ Addedcharacter-parser@1.0.2(transitive)
+ Addedcommander@1.1.1(transitive)
+ Addedcss@1.0.8(transitive)
+ Addedcss-parse@1.0.4(transitive)
+ Addedcss-stringify@1.0.5(transitive)
+ Addedescodegen@0.0.23(transitive)
+ Addedesprima@1.0.3(transitive)
+ Addedestraverse@0.0.41.1.2-1(transitive)
+ Addedgobbledygook@0.0.3(transitive)
+ Addedis-promise@1.0.1(transitive)
+ Addedjade@0.30.0(transitive)
+ Addedjsxgettext@0.1.3(transitive)
+ Addedkeypress@0.1.0(transitive)
+ Addedminimatch@10.0.1(transitive)
+ Addedmkdirp@0.3.5(transitive)
+ Addedmonocle@0.1.50(transitive)
+ Addedoptimist@0.3.7(transitive)
+ Addedpromise@2.0.0(transitive)
+ Addedreaddirp@0.2.5(transitive)
+ Addedsource-map@0.1.430.7.4(transitive)
+ Addedtransformers@2.0.1(transitive)
+ Addeduglify-js@2.2.5(transitive)
- Removednode-gettext@0.2.8
- Removedescodegen@2.1.0(transitive)
- Removedesprima@4.0.1(transitive)
- Removedestraverse@5.3.0(transitive)
- Removedesutils@2.0.3(transitive)
- Removediconv@1.1.3(transitive)
- Removedjsxgettext@0.0.4(transitive)
- Removednode-gettext@0.2.8(transitive)
- Removedsource-map@0.6.1(transitive)
Updatedjsxgettext@0.1.3