Permalink
Browse files

Removed all sync FS operations, added pattern options for files stora…

…ge, added package.json
  • Loading branch information...
1 parent 11254e3 commit 0273f6877e71d7a5534ddfc3e2f5c17471f8ee17 @naholyr committed May 15, 2011
Showing with 211 additions and 102 deletions.
  1. +1 −0 .gitignore
  2. +29 −0 lib/async.js
  3. +51 −0 lib/readdir.js
  4. +20 −0 package.json
  5. +73 −67 stores/file.js
  6. +2 −35 test/index.js
  7. +35 −0 test/test-file.js
View
@@ -1,2 +1,3 @@
.project
.settings
+node_modules
View
@@ -0,0 +1,29 @@
+exports.map = function map(items, exec, execCallback, finalCallback) {
+ var error = undefined;
+ var done = 0;
+ function markDone() {
+ done++;
+ }
+ function execOnItem(item) {
+ exec.call(items, item, function(err) {
+ if (!isDone()) {
+ if (err) error = err;
+ else {
+ var args = [].slice.call(arguments, 1);
+ args.unshift(item);
+ execCallback.apply(item, args);
+ markDone();
+ }
+ }
+ });
+ }
+ function isDone() {
+ return done == items.length || error !== undefined;
+ }
+ function checkDone() {
+ if (isDone()) finalCallback.call(items, error);
+ else setTimeout(checkDone, 1);
+ }
+ items.forEach(execOnItem);
+ checkDone();
+};
View
@@ -0,0 +1,51 @@
+const fs = require('fs');
+const path = require('path');
+const async = require('./async');
+
+const readdirs = exports.readdirs = function readdirs(dirs, callback) {
+ var files = [];
+ async.map(dirs,
+ fs.readdir,
+ function(dir, dirFiles) {
+ dirFiles.forEach(function(file) {
+ files.push(path.join(dir, file));
+ });
+ },
+ function(err) {
+ callback(err, files);
+ }
+ );
+};
+
+const glob = exports.glob = function glob(paths, re, callback) {
+ if (!(paths instanceof Array)) {
+ paths = [paths];
+ }
+ readdirs(paths, function(err, files) {
+ if (err) callback(err);
+ else {
+ var matchingFiles = [];
+ var matches = [];
+ files.forEach(function(file) {
+ var m = file.match(re);
+ if (m) {
+ matches.push(m);
+ matchingFiles.push(file);
+ }
+ });
+ callback(err, matchingFiles, matches);
+ }
+ });
+};
+
+
+
+if (require.main === module) {
+ // Simple test when executed directly
+ readdirs(['../test/i18n-data', '.', '../stores'], function(err, files) {
+ console.log('readdirs', files);
+ });
+ glob(['../test/i18n-data', '.', '../stores'], /\.js$/, function(err, files) {
+ console.log('glob', files);
+ });
+}
View
@@ -0,0 +1,20 @@
+{
+ "author": "Nicolas Chambrier <naholyr@gmail.com> (http://naholyr.fr)",
+ "name": "jus-i18n",
+ "description": "Real I18N implementation, with a true support for plural forms, and many storage engines. Works best with Express or Jus Framework",
+ "version": "0.1.0",
+ "homepage": "https://github.com/naholyr/node-i18n",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/naholyr/node-i18n.git"
+ },
+ "main": "index.js",
+ "scripts": {
+ "test": "node test"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "dependencies": {},
+ "devDependencies": {}
+}
View
@@ -1,89 +1,71 @@
+const readdir = require('../lib/readdir');
+const async = require('../lib/async');
+const fs = require('fs');
+const path = require('path');
var options = {
- "paths": [ process.cwd() + "/i18n-data" ],
- "encoding": "utf8"
-},
-data = {},
-FS = require("fs"),
-Path = require("path");
+ "paths": [ path.join(process.cwd(), 'i18n-data') ],
+ "encoding": 'utf8',
+ "pattern": /([a-z0-9\-_]+)\.([a-z\-_]+)\.[a-z]+$/i,
+ "patternIndexCatalogue": 1,
+ "patternIndexLocale": 2
+};
+var data = {};
-function readdirs(paths, callback) {
- setTimeout(function() {
- var files = [], error = undefined;
- paths.forEach(function(path) {
- if (error) return;
- try {
- var path_files = FS.readdirSync(path);
- path_files.forEach(function(file) {
- files.push(Path.join(path, file));
- });
- } catch (e) {
- if (e instanceof Error && e.errno == 2) { // Not found
- // Ignore error
- } else {
- error = e;
+function listCatalogueFiles(catalogue, callback) {
+ readdir.glob(options.paths, options.pattern, function(err, files, matches) {
+ if (err) callback(err);
+ else {
+ var catalogueFiles = [];
+ var catalogueMatches = [];
+ files.forEach(function(file, i) {
+ if (matches[i][options.patternIndexCatalogue] == catalogue) {
+ catalogueFiles.push(file);
+ catalogueMatches.push(matches[i]);
}
- }
- });
- callback(error, files);
- }, 0);
-}
-
-function glob(paths, re, callback) {
- if (!(paths instanceof Array)) {
- paths = [paths];
- }
- readdirs(paths, function(err, files) {
- if (err) {
- callback(err);
- } else {
- callback(undefined, files.filter(function(file) {
- return re.test(file);
- }));
+ });
+ callback(err, catalogueFiles, catalogueMatches);
}
});
}
-function loadFiles(files) {
- return files.map(loadFile);
+function loadFiles(files, callback) {
+ var locales = [];
+ async.map(files, loadFile, function(file, locale) { locales.push(locale) }, function(err) { callback(err, locales) });
}
-function loadFile(file) {
- var parts = Path.basename(file).split(/\./);
- if (parts.length >= 3) {
- var catalogue = parts[0], locale = parts[1];
+function loadFile(file, callback) {
+ var m = file.match(options.pattern);
+ if (m) {
+ var catalogue = m[options.patternIndexCatalogue];
+ var locale = m[options.patternIndexLocale];
if (typeof data[catalogue] == 'undefined') {
data[catalogue] = {};
}
if (typeof data[catalogue][locale] == 'undefined') {
data[catalogue][locale] = {};
}
translations = data[catalogue][locale];
- var content = FS.readFileSync(file, options.encoding);
- content.split(/[\r\n]+/).forEach(function(line) {
- var trans = line.split("="), source = trans.shift(), target = trans.join("=");
- if (target) {
- source = source.replace(/^\s*(["']?)(.*?)\1\s*$/, '$2');
- target = target.replace(/^\s*(["']?)(.*?)\1\s*$/, '$2');
- translations[source] = target;
- }
+ fs.readFile(file, options.encoding, function(err, content) {
+ if (!err) content.split(/[\r\n]+/).forEach(function(line) {
+ var trans = line.split("="), source = trans.shift(), target = trans.join("=");
+ if (target) {
+ source = source.replace(/^\s*(["']?)(.*?)\1\s*$/, '$2');
+ target = target.replace(/^\s*(["']?)(.*?)\1\s*$/, '$2');
+ translations[source] = target;
+ }
+ });
+ callback(err, locale);
});
- return locale;
}
}
exports.load = function load(catalogue, locales, i18n, callback) {
+ var self = this;
if (typeof locales == 'undefined') {
// No locales: load by catalogue
- glob(options.paths, new RegExp("\/"+catalogue.toLowerCase()+"\.[a-z\-_]+\.[a-z0-9\-_]+$", "i"), function(err, files) {
- var locales, error;
- try {
- locales = loadFiles(files);
- } catch (e) {
- error = e;
- }
- callback(error, locales, this);
-console.log(data);
+ listCatalogueFiles(catalogue, function(err, files) {
+ loadFiles(files, function(err, locales) { callback(err, locales, self) });
});
} else {
// Load specified locales
@@ -100,20 +82,44 @@ exports.get = function get(key, locale, catalogue) {
return ((data[catalogue] || {})[locale] || {})[key];
};
-exports.configure = function configure(options, callback) {
+exports.configure = function configure(newOptions, callback) {
// Too dumb to be configured
if (typeof options != 'object') {
return callback(new Error('Invalid options'), this);
}
+ for (var option in newOptions) {
+ options[option] = newOptions[option];
+ }
return callback(undefined, this);
};
exports.locales = function locales(prefix, catalogue, callback) {
- // Too dumb...
- return callback(new Error('Too dumb to know what locales I can load'), undefined, this);
+ function hasPrefix(locale) {
+ return locale && (locale.substring(0, prefix.length) == prefix);
+ }
+ listCatalogueFiles(catalogue, function(err, files, matches) {
+ if (err) callback(err);
+ else {
+ var allLocales = [];
+ files.forEach(function(file, i) {
+ var locale = matches[i][options.patternIndexLocale];
+ if (hasPrefix(locale) && allLocales.indexOf(locale) == -1) allLocales.push(locale);
+ });
+ callback(err, allLocales);
+ }
+ });
};
exports.catalogues = function catalogues(callback) {
- // Too dumb...
- return callback(new Error('Too dumb to know what catalogues I can load'), undefined, this);
+ readdir.glob(options.paths, options.pattern, function(err, files, matches) {
+ if (err) callback(err);
+ else {
+ var catalogues = [];
+ matches.forEach(function(match) {
+ var catalogue = match[options.patternIndexCatalogue];
+ if (catalogues.indexOf(catalogue) == -1) catalogues.push(catalogue);
+ });
+ callback(err, catalogues);
+ }
+ });
};
View
@@ -1,35 +1,2 @@
-var i18n = require(__dirname + '/..');
-
-i18n.defaultLocale = 'fr';
-i18n.debug();
-
-i18n.setStore('file', function(err) {
- if (err) {
- throw err;
- }
- console.log("Configured store");
- i18n.load("messages", function(err, locales) {
- if (err) {
- throw err.ALL || err;
- }
- console.log("Loaded locales", locales);
- console.log(i18n.translate('x')); // x (en français)
- console.log(i18n.translate('y')); // [T]y[/T]
- i18n.debug(false);
- console.log(i18n.translate('y')); // y
- i18n.debug(true);
- console.log(i18n.translate('x', 'en')); // x (in English)
- for (var n=0; n<6; n++) {
- console.log(i18n.plural('You have {n} messages', n, 'fr'));
- }
- i18n.defaultLocale = 'fr';
- console.log(i18n.translate('Hello, {name}', {name:'Jones', context:'male'})); // Bonjour, monsieur Jones
- console.log(i18n.translate('Hello, {name}', {name:'Jones', context:'female'})); // Bonjour, mademoiselle Jones
- console.log(i18n.translate('Hello, {name}', {name:'Jones', context:'unknown'})); // Bonjour, Jones
- i18n.defaultLocale = 'en';
- console.log(i18n.translate('Hello, {name}', {name:'Jones', context:'male'})); // Hello, mister Jones
- console.log(i18n.translate('Hello, {name}', {name:'Jones', context:'female'})); // Hello, miss Jones
- console.log(i18n.translate('Hello, {name}', {name:'Jones', context:'unknown'})); // Hello, Jones
- });
-});
-
+require('./test-file');
+require('./test-gettext');
View
@@ -0,0 +1,35 @@
+var i18n = require(__dirname + '/..');
+
+i18n.defaultLocale = 'fr';
+i18n.debug();
+
+i18n.setStore('file', { "paths": [__dirname + "/i18n-data"] }, function(err) {
+ if (err) {
+ throw err;
+ }
+ console.log("Configured store");
+ i18n.load("messages", function(err, locales) {
+ if (err) {
+ throw err.ALL || err;
+ }
+ console.log("Loaded locales", locales);
+ console.log(i18n.translate('x')); // x (en français)
+ console.log(i18n.translate('y')); // [T]y[/T]
+ i18n.debug(false);
+ console.log(i18n.translate('y')); // y
+ i18n.debug(true);
+ console.log(i18n.translate('x', 'en')); // x (in English)
+ for (var n=0; n<6; n++) {
+ console.log(i18n.plural('You have {n} messages', n, 'fr'));
+ }
+ i18n.defaultLocale = 'fr';
+ console.log(i18n.translate('Hello, {name}', {name:'Jones', context:'male'})); // Bonjour, monsieur Jones
+ console.log(i18n.translate('Hello, {name}', {name:'Jones', context:'female'})); // Bonjour, mademoiselle Jones
+ console.log(i18n.translate('Hello, {name}', {name:'Jones', context:'unknown'})); // Bonjour, Jones
+ i18n.defaultLocale = 'en';
+ console.log(i18n.translate('Hello, {name}', {name:'Jones', context:'male'})); // Hello, mister Jones
+ console.log(i18n.translate('Hello, {name}', {name:'Jones', context:'female'})); // Hello, miss Jones
+ console.log(i18n.translate('Hello, {name}', {name:'Jones', context:'unknown'})); // Hello, Jones
+ });
+});
+

0 comments on commit 0273f68

Please sign in to comment.