diff --git a/.versions b/.versions index 84dbd71..0a99a77 100644 --- a/.versions +++ b/.versions @@ -1,35 +1,21 @@ -babel-compiler@6.24.7 -babel-runtime@1.1.1 +babel-compiler@7.0.7 +babel-runtime@1.2.0 base64@1.0.10 -blaze@2.1.8 -blaze-tools@1.0.10 -caching-compiler@1.1.9 -caching-html-compiler@1.0.6 -check@1.2.5 -deps@1.0.12 -diff-sequence@1.0.7 -ecmascript@0.9.0 +check@1.3.0 +dynamic-import@0.3.0 +ecmascript@0.10.6 ecmascript-runtime@0.5.0 -ecmascript-runtime-client@0.5.0 +ecmascript-runtime-client@0.6.0 ecmascript-runtime-server@0.5.0 ejson@1.1.0 -html-tools@1.0.11 -htmljs@1.0.11 -id-map@1.0.9 -jquery@1.11.10 -meteor@1.8.0 -modules@0.11.0 -modules-runtime@0.9.0 -mongo-id@1.0.6 -observe-sequence@1.0.16 +http@1.4.1 +meteor@1.8.2 +modules@0.11.3 +modules-runtime@0.9.1 ostrio:cstorage@2.2.1 -ostrio:i18n@3.0.3 -promise@0.10.0 -random@1.0.10 +ostrio:i18n@3.1.0 +promise@0.10.1 reactive-var@1.0.11 -spacebars@1.0.12 -spacebars-compiler@1.1.0 -templating@1.2.13 -templating-tools@1.1.1 tracker@1.1.3 underscore@1.0.10 +url@1.2.0 diff --git a/README.md b/README.md index 49721c6..aa6f7d4 100755 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ Reactive i18n and l10n isomorphic driver ======== -Object based, fast, lightweight (*306 lines with comments*) and reactive internationalization isomorphic driver for Meteor with support of placeholders. +Object based, fast, lightweight (*306 lines with comments*) and reactive internationalization isomorphic driver for Meteor with support of placeholders, and user's locale auto-detection. + +Not tied to Blaze, can be used with Vue.js, React.js or any other JS solution. Install: ======== @@ -10,12 +12,12 @@ meteor add ostrio:i18n Import: ======== -```jsx +```js import I18N from 'meteor/ostrio:i18n'; ``` ### Object-based structure -```jsx +```js /* Isomorphic (Both Server and Client) */ const i18nConfig = { settings: { //--> Config object @@ -37,7 +39,7 @@ const i18nConfig = { nestedProp: "value" }, dynamicProperty(){ - return 'info'; + return `info`; } }, en:{ //--> Localization with key of country two-letter code @@ -46,7 +48,7 @@ const i18nConfig = { nestedProp: "value" }, dynamicProperty(){ - return 'info'; + return `info`; } } ... @@ -58,7 +60,7 @@ const i18n = new I18N({i18n: i18nConfig}); Initialization ======== -```jsx +```js import I18N from 'meteor/ostrio:i18n'; const i18n = new I18N(config); ``` @@ -74,7 +76,7 @@ API - `locale` {*String*} - [Optional] Two-letter locale string, used to force locale, if not set __current locale__ is used - `key` {*String*} - l10n key like: `folder.file.object.key` - `replacements..` {*String*|[*String*]|*Object*} - [Optional] Replacements for placeholders in l10n string -```jsx +```js i18n.get('file.obj.key'); // Current locale, no replacements i18n.get(locale, param); // Force locale, no replacements @@ -93,7 +95,7 @@ i18n.get('en', 'file.obj.key', 'User Name'); // Hello {{username}} -> Hello User - `locale` {*String*} - [Optional] Two-letter locale string, used to force locale, if not set __current locale__ is used - `key` {*String*} - l10n key like: `folder.file.object.key` -```jsx +```js i18n.has('file.obj.key'); // Current locale i18n.has(locale, param); // Force locale i18n.has('ca', 'file.obj.key'); //false @@ -102,22 +104,32 @@ i18n.has('en', 'file.obj.key'); //true ##### `setLocale(locale)` - `locale` {*String*} - Two-letter locale code -```jsx +```js i18n.setLocale(locale); ``` +##### `addl10n(l10n)` + - `l10n` {*Object*} - Object with language set +```js +i18n.addl10n({ + en: { // <- Object's root is the language two-letter code + newKey: "New Value" + } +}); +``` + ##### Get current localization at any environment -```jsx +```js i18n.currentLocale.get(); // Reactive on Client ``` ##### Get current default locale -```jsx +```js i18n.defaultLocale; ``` ##### Get configuration object -```jsx +```js i18n.langugeSet(); /* Returns: { @@ -148,22 +160,24 @@ i18n.langugeSet(); ##### Get specific key from configuration object - `key` {*String*} - One of the keys: `current`, `all`, `other`, `locales`, `currentISO`, `currentName`, `currentPath` -```jsx +```js i18n.getSetting('current'); // en ``` Client specific usage ================ ##### Client's browser locale -```jsx +```js i18n.userLocale; // en-US ``` Template helpers ================ -`i18n` - accepts `locale`, `key` and `replacements`: +__Template helpers requires__ `templating` __package to be installed__. + +`i18n` helper - accepts `locale`, `key` and `replacements`: *You may change name of the helper via config object: `config.helperName`* -```html +```handlebars
{{i18n 'sample.hello'}}
{{{i18n 'sample.html'}}}
{{i18n 'sample.fullName'}}
@@ -176,25 +190,25 @@ Template helpers `i18nSettings` - accepts configuration object key, one of `current`, `all`, `other`, `locales` *You may change name of the helper via config object: `config.helperSettingsName`* -```html +```handlebars {{#each i18nSettings 'all'}} ... {{/each}} ``` ##### Template language switcher example -```html +```handlebars {{#each i18nSettings 'all'}} {{#if compare code '==' currentLocale}} {{name}} {{else}} - {{name}} + {{name}} {{/if}} {{/each}} ``` -```jsx +```js Template.langSwitch.helpers({ currentLocale(){ return i18n.currentLocale.get() @@ -202,12 +216,19 @@ Template.langSwitch.helpers({ }); Template.langSwitch.events({ - 'click .switch-language'(e, template) { + 'click [data-switch-language]'(e) { e.preventDefault(); - i18n.setLocale(e.currentTarget.dataset.code); + i18n.setLocale(e.currentTarget.dataset.switchLanguage); return false; } }); ``` -Template helpers `compare`, `==`, `Session` and many more comes from: [ostrio:templatehelpers](https://atmospherejs.com/ostrio/templatehelpers) package \ No newline at end of file +Template helpers `compare`, `==`, `Session` and many more comes from: [`ostrio:templatehelpers`](https://atmospherejs.com/ostrio/templatehelpers) package. + + +Support this project: +======== +This project wouldn't be possible without [ostr.io](https://ostr.io). + +Using [ostr.io](https://ostr.io) you are not only [protecting domain names](https://ostr.io/info/domain-names-protection), [monitoring websites and servers](https://ostr.io/info/monitoring), using [Prerendering for better SEO](https://ostr.io/info/prerendering) of your JavaScript website, but support our Open Source activity, and great packages like this one could be available for free. diff --git a/i18n.js b/i18n.js index 6ff4bc2..0470c81 100755 --- a/i18n.js +++ b/i18n.js @@ -1,17 +1,16 @@ import { _ } from 'meteor/underscore'; import { Meteor } from 'meteor/meteor'; -import { Template } from 'meteor/templating'; import { ReactiveVar } from 'meteor/reactive-var'; import { check, Match } from 'meteor/check'; import { ClientStorage } from 'meteor/ostrio:cstorage'; /* -@private -@locus Anywhere -@name toDottedString -@summary Convert object nested keys into dotted string -*/ -const toDottedString = function(obj, prepend = 'i18n') { + * @private + * @locus Anywhere + * @name toDottedString + * @summary Convert object nested keys into dotted string + */ +const toDottedString = function (obj, prepend = 'i18n') { let final = {}; for (let key in obj) { if (_.isFunction(obj[key]) || _.isString(obj[key])) { @@ -24,12 +23,12 @@ const toDottedString = function(obj, prepend = 'i18n') { }; /* -@private -@locus Anywhere -@name proceedPlaceholders -@summary Replace placeholders with replacements in l10n strings -*/ -const proceedPlaceholders = function(string, replacements) { + * @private + * @locus Anywhere + * @name proceedPlaceholders + * @summary Replace placeholders with replacements in l10n strings + */ +const proceedPlaceholders = function (string, replacements) { if (string) { let key; for (let replacement of replacements) { @@ -52,20 +51,19 @@ const proceedPlaceholders = function(string, replacements) { export default class I18N { /* - @locus Anywhere - @name I18N - @constructor - @summary Initialize I18N object with `config` - @param config {Object} - @param config.i18n {Object} - Internalization object - @param config.returnKey {Boolean} - Return key if l10n value not found - @param config.helperName {String} - Template main i18n helper name - @param config.helperSettingsName {String} - Template i18nSettings helper name - */ + * @locus Anywhere + * @name I18N + * @constructor + * @summary Initialize I18N object with `config` + * @param config {Object} + * @param config.i18n {Object} - Internalization object + * @param config.returnKey {Boolean} - Return key if l10n value not found + * @param config.helperName {String} - Template main i18n helper name + * @param config.helperSettingsName {String} - Template i18nSettings helper name + */ constructor(config = {}) { check(config, Object); - let object; let key; const self = this; this.returnKey = config.returnKey || true; @@ -80,19 +78,14 @@ export default class I18N { this.locales = []; this.strings = {}; - for (key in config.i18n) { - if (key !== 'settings') { - object = toDottedString.call(this, config.i18n[key], key); - for (let k in object) { - this.strings[k] = object[k]; - } - } - } + + this.addl10n(config.i18n); if (_.isObject(config.i18n)) { check(config.i18n.settings, Object); this.settings = config.i18n.settings; this.defaultLocale = this.settings.defaultLocale; + check(this.defaultLocale, String); this.strings['__settings.__langSet__'] = []; this.strings['__settings.__langConfig__'] = []; const dotted = toDottedString.call(this, this.settings, '__settings'); @@ -113,19 +106,21 @@ export default class I18N { this.userLocale = ((Meteor.isClient) ? window.navigator.userLanguage || window.navigator.language || navigator.userLanguage : this.settings.defaultLocale); if (Meteor.isClient) { - /* - @summary Main `i18n` template helper - */ - Template.registerHelper(this.helperName, function () { - return self.get.apply(self, arguments); - }); - - /* - @summary Settings `i18n` template helper, might be used to build language switcher (see demo folder). - */ - Template.registerHelper(this.helperSettingsName, function () { - return self.getSetting.apply(self, arguments); - }); + if (typeof Template !== 'undefined' && Template !== null) { + /* + * @summary Main `i18n` template helper + */ + Template.registerHelper(this.helperName, function () { + return self.get.apply(self, arguments); + }); + + /* + * @summary Settings `i18n` template helper, might be used to build language switcher (see demo folder). + */ + Template.registerHelper(this.helperSettingsName, function () { + return self.getSetting.apply(self, arguments); + }); + } const savedLocale = ClientStorage.get('___i18n.locale___'); if (!this.currentLocale.get()) { @@ -158,14 +153,14 @@ export default class I18N { } /* - @locus Anywhere - @memberOf I18N - @name get - @summary Get l10n value by key - @param locale {String} - [Optional] Two-letter locale string - @param key {String} - l10n key like: `folder.file.object.key` - @param replacements... {String|[String]|Object} - [Optional] Replacements for placeholders in l10n string - */ + * @locus Anywhere + * @memberOf I18N + * @name get + * @summary Get l10n value by key + * @param locale {String} - [Optional] Two-letter locale string + * @param key {String} - l10n key like: `folder.file.object.key` + * @param replacements... {String|[String]|Object} - [Optional] Replacements for placeholders in l10n string + */ get(...args) { let key; let lang; @@ -204,13 +199,13 @@ export default class I18N { } /* - @locus Anywhere - @memberOf I18N - @name has - @summary Check if key exists in current locale - @param locale {String} - [Optional] Two-letter locale string - @param key {String} - l10n key like: `folder.file.object.key` - */ + * @locus Anywhere + * @memberOf I18N + * @name has + * @summary Check if key exists in current locale + * @param locale {String} - [Optional] Two-letter locale string + * @param key {String} - l10n key like: `folder.file.object.key` + */ has(...args) { let key; let lang; @@ -236,12 +231,12 @@ export default class I18N { } /* - @locus Anywhere - @memberOf I18N - @name setLocale - @summary Set another locale - @param locale {String} - Two-letter locale string - */ + * @locus Anywhere + * @memberOf I18N + * @name setLocale + * @summary Set another locale + * @param locale {String} - Two-letter locale string + */ setLocale(locale) { check(locale, String); @@ -257,12 +252,12 @@ export default class I18N { } /* - @locus Anywhere - @memberOf I18N - @name getSetting - @summary Get parsed data by key from i18n.json file - @param key {String} - One of the keys: 'current', 'all', 'other', 'locales' - */ + * @locus Anywhere + * @memberOf I18N + * @name getSetting + * @summary Get parsed data by key from i18n.json file + * @param key {String} - One of the keys: 'current', 'all', 'other', 'locales' + */ getSetting(key) { check(key, Match.Optional(Match.OneOf('current', 'all', 'other', 'locales', 'currentISO', 'currentName'))); if (key) { @@ -272,18 +267,18 @@ export default class I18N { } /* - @locus Anywhere - @memberOf I18N - @name langugeSet - @summary Get data from i18n config - */ + * @locus Anywhere + * @memberOf I18N + * @name langugeSet + * @summary Get data from i18n config + */ langugeSet() { let key; - const current = this.settings[this.currentLocale.get()]; + const locale = this.currentLocale.get(); return { - current: this.currentLocale.get(), - currentISO: current.isoCode, - currentName: current.name, + current: locale, + currentISO: this.settings[locale].isoCode, + currentName: this.settings[locale].name, all: (() => { const result = []; for (key in this.settings) { @@ -296,7 +291,7 @@ export default class I18N { other: (() => { const result = []; for (key in this.settings) { - if (_.isObject(this.settings[key]) && (key !== this.currentLocale.get())) { + if (_.isObject(this.settings[key]) && (key !== locale)) { result.push(this.settings[key]); } } @@ -313,4 +308,27 @@ export default class I18N { })() }; } + + /* + * @locus Anywhere + * @memberOf I18N + * @name addl10n + * @summary add l10n data + * @example { en: { newKey: "new data" } } + */ + addl10n(l10n) { + check(l10n, Object); + + let k; + let key; + let object; + for (key in l10n) { + if (key !== 'settings') { + object = toDottedString.call(this, l10n[key], key); + for (k in object) { + this.strings[k] = object[k]; + } + } + } + } } diff --git a/package.js b/package.js index c6b7992..e378340 100755 --- a/package.js +++ b/package.js @@ -1,14 +1,13 @@ Package.describe({ name: 'ostrio:i18n', summary: 'Super-Lightweight and fast i18n isomorphic driver for Meteor with support of placeholders.', - version: '3.0.3', + version: '3.1.0', git: 'https://github.com/VeliovGroup/Meteor-Internationalization', documentation: 'README.md' }); Package.onUse(function (api) { - api.versionsFrom('1.4'); + api.versionsFrom('1.6.1'); api.use(['underscore', 'check', 'reactive-var', 'ecmascript', 'ostrio:cstorage@2.2.1'], ['client', 'server']); - api.use(['templating', 'tracker'], 'client'); api.mainModule('i18n.js', ['client', 'server']); });