index.js 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. var _ = require('lodash');
  2. var path = require('path');
  3. var fs = require('fs');
  4. function I18n(opts) {
  5. if (!(this instanceof I18n)) return new I18n(opts);
  6. this.opts = _.defaults(opts || {}, {
  7. defaultLocale: 'en'
  8. });
  9. this._locales = {};
  10. _.bindAll(this, _.functionsIn(this));
  11. }
  12. // Extend locales
  13. I18n.prototype.set = function(_locales) {
  14. this._locales = _.extend(this._locales, _locales);
  15. };
  16. // Return a locales or all
  17. I18n.prototype.get = function(lang) {
  18. if (lang) {
  19. lang = this.resolve(lang);
  20. return this._locales[lang];
  21. } else {
  22. return this._locales;
  23. }
  24. };
  25. // Return list of locales
  26. I18n.prototype.locales = function() {
  27. return _.keys(this._locales);
  28. };
  29. // Load locales from a folder
  30. I18n.prototype.load = function(root) {
  31. var locales = _.chain(fs.readdirSync(root))
  32. .map(function(filename) {
  33. var ext = path.extname(filename);
  34. if (ext != '.json' && ext != '.js') return;
  35. var lang = path.basename(filename, ext);
  36. var filepath = path.resolve(root, filename);
  37. return [
  38. lang,
  39. require(filepath)
  40. ];
  41. })
  42. .compact()
  43. .fromPairs()
  44. .value();
  45. this.set(locales);
  46. };
  47. // Resolve a language to an existing locale
  48. I18n.prototype.resolve = function(lang, defaultLocale) {
  49. defaultLocale = _.isUndefined(defaultLocale)? this.opts.defaultLocale : defaultLocale;
  50. return _.chain(this.locales())
  51. .map(function(locale) {
  52. return {
  53. locale: locale,
  54. score: compareLocales(lang, locale)
  55. };
  56. })
  57. .filter(function(lang) {
  58. return lang.score > 0;
  59. })
  60. .sortBy('score')
  61. .map('locale')
  62. .last()
  63. .value() || defaultLocale;
  64. };
  65. // Translate a phrase
  66. I18n.prototype.t = function(lang, phrase) {
  67. var args = _.toArray(arguments).slice(2);
  68. var kwargs = {};
  69. var locale = this.get(lang);
  70. if (_.isObject(_.last(args))) {
  71. kwargs = _.last(args);
  72. args = args.slice(0, -1);
  73. }
  74. var tpl = locale[phrase];
  75. if (_.isUndefined(tpl)) {
  76. tpl = this.get(this.opts.defaultLocale)[phrase];
  77. }
  78. return interpolate(tpl || phrase, args, kwargs);
  79. };
  80. // Compare two language to find the most adequate
  81. function compareLocales(lang, locale) {
  82. var langMain = _.first(lang.split('-'));
  83. var langSecond = _.last(lang.split('-'));
  84. var localeMain = _.first(locale.split('-'));
  85. var localeSecond = _.last(locale.split('-'));
  86. if (locale == lang) return 100;
  87. if (localeMain == langMain && localeSecond == localeMain) return 51;
  88. if (localeMain == langMain) return 50;
  89. if (localeSecond == langSecond) return 20;
  90. return 0;
  91. }
  92. // Interpolate a template
  93. function interpolate(tpl, args, kwargs) {
  94. var value = tpl;
  95. kwargs = _.extend(kwargs, args);
  96. return _.reduce(kwargs, function(value, val, key) {
  97. var re = /{{([\s\S]*?[^\$])}}/g;
  98. return value.replace(re, val);
  99. }, tpl);
  100. }
  101. module.exports = I18n;