From a189f9e90b26b5185d79f2fbb82372ce0b349bb2 Mon Sep 17 00:00:00 2001 From: Christian Kaisermann Date: Thu, 26 Jul 2018 23:24:17 -0300 Subject: [PATCH] Add deep merge for extensible locale dictionaries --- README.md | 59 +++++++++++++++++++++++++++++++++++++++- package-lock.json | 39 ++++++++++++-------------- package.json | 4 +++ src/interfaces.ts | 6 ++-- src/svelte-i18n.ts | 9 ++++-- test/svelte-i18n.test.ts | 17 ++++++++++-- 6 files changed, 104 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 6c4e3b3..eb4e09c 100644 --- a/README.md +++ b/README.md @@ -2,4 +2,61 @@ > Internationalization for svelte -**Work-in-progress** \ No newline at end of file +**Work-in-progress** + +## Usage + +### On the `store` + +```js +import i18n from 'svelte-i18n' +import { Store } from 'svelte/store' + +const store = new Store() + +/** i18n(svelteStore, arrayOfLocalesObjects) */ +i18n(store, [ + { + 'pt-BR': { + message: 'Mensagem', + messages: { + alert: 'Alerta', + error: 'Erro' + } + }, + 'en-US': { + message: 'Message', + messages: { + alert: 'Alert', + error: 'Error' + } + } + }, + /** Locales are deeply merged */ + { + 'pt-BR': { + messages: { + warn: 'Aviso', + success: 'Sucesso' + } + }, + 'en-US': { + messages: { + warn: 'Warn', + success: 'Success' + } + } + } +]) +``` + +### On `templates` + +```html +
+ {$_('message')}: {$_.upper('messages.success')} +
+ + +Message: SUCCESS +``` diff --git a/package-lock.json b/package-lock.json index 96e17e7..0c6ff0d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -360,6 +360,12 @@ "integrity": "sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow==", "dev": true }, + "@types/deepmerge": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/deepmerge/-/deepmerge-2.1.0.tgz", + "integrity": "sha512-/0Ct/q5g+SgaACZ+A0ylY3071nEBN7QDnTWiCtaB3fx24UpoAQXf25yNVloOYVUis7jytM1F1WC78+EOwXkQJQ==", + "dev": true + }, "@types/estree": { "version": "0.0.39", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", @@ -2254,6 +2260,11 @@ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, + "deepmerge": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.1.1.tgz", + "integrity": "sha512-urQxA1smbLZ2cBbXbaYObM1dJ82aJ2H57A1C/Kklfh/ZN1bgH4G/n5KWhdNfOK11W98gqZfyYj7W4frJJRwA2w==" + }, "default-require-extensions": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-1.0.0.tgz", @@ -3377,15 +3388,13 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -3402,22 +3411,19 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -3548,8 +3554,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -3563,7 +3568,6 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -3580,7 +3584,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -3589,15 +3592,13 @@ "version": "0.0.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.2.4.tgz", "integrity": "sha512-hzXIWWet/BzWhYs2b+u7dRHlruXhwdgvlTMDKC6Cb1U7ps6Ac6yQlR39xsbjWJE377YTCtKwIXIpJ5oP+j5y8g==", "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -3618,7 +3619,6 @@ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -3707,8 +3707,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -3722,7 +3721,6 @@ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -3860,7 +3858,6 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", diff --git a/package.json b/package.json index 1ff78af..c4cb00f 100644 --- a/package.json +++ b/package.json @@ -77,6 +77,7 @@ "singleQuote": true }, "devDependencies": { + "@types/deepmerge": "^2.1.0", "@types/jest": "^22.0.0", "@types/node": "^10.0.3", "colors": "^1.1.2", @@ -108,5 +109,8 @@ "typedoc": "^0.11.0", "typescript": "^2.6.2", "validate-commit-msg": "^2.12.2" + }, + "dependencies": { + "deepmerge": "^2.1.1" } } diff --git a/src/interfaces.ts b/src/interfaces.ts index c4edf03..46af013 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -22,9 +22,9 @@ export interface SvelteStore { [prop: string]: any } -export interface Locale { - [id: string | number]: string +export interface LocaleDictionary { + [id: string]: string | Array | LocaleDictionary } export interface Locales { - [locale: string]: Locale + [locale: string]: LocaleDictionary } diff --git a/src/svelte-i18n.ts b/src/svelte-i18n.ts index 1ddfa52..8df5ae0 100644 --- a/src/svelte-i18n.ts +++ b/src/svelte-i18n.ts @@ -1,10 +1,13 @@ -import { InterpolationObj, Sveltei18n, SvelteStore, Locale, Locales } from './interfaces' +import { InterpolationObj, Sveltei18n, SvelteStore, LocaleDictionary, Locales } from './interfaces' import { capitalize, titlelize, upper, lower, getNestedProp } from './utils' +import deepmerge from 'deepmerge' + +export default function(store: SvelteStore, localesList: Array) { + const locales: Locales = deepmerge.all(localesList) -export default function(store: SvelteStore, locales: Locales) { store.locale = (locale: string) => store.fire('locale', locale) store.on('locale', function(locale: string) { - const localeDict: Locale = locales[locale] + const localeDict: LocaleDictionary = locales[locale] const _ = function(id, values) { return getNestedProp(localeDict, id) || id } diff --git a/test/svelte-i18n.test.ts b/test/svelte-i18n.test.ts index 4b69218..801d4a7 100644 --- a/test/svelte-i18n.test.ts +++ b/test/svelte-i18n.test.ts @@ -7,7 +7,10 @@ const locales = { 'pt-br': { test: 'teste', phrase: 'Adoro banana', - phrases: ['Frase 1', 'Frase 2'] + phrases: ['Frase 1', 'Frase 2'], + obj: { + a: 'a' + } }, po: { test: 'prøve', @@ -16,7 +19,16 @@ const locales = { } } -i18n(store, locales) +i18n(store, [ + locales, + { + 'pt-br': { + obj: { + b: 'b' + } + } + } +]) /** * Dummy test @@ -38,6 +50,7 @@ describe('Dummy test', () => { expect(store.get()._).toBeInstanceOf(Function) expect(store.get()._('non-existent')).toBe('non-existent') expect(store.get()._('test')).toBe(locales['pt-br'].test) + expect(store.get()._('obj.b')).toBe('b') store.fire('locale', 'po') expect(store.get()._('test')).not.toBe(locales['pt-br'].test) expect(store.get()._('test')).toBe(locales.po.test)