mirror of
https://github.com/cupcakearmy/svelte-i18n.git
synced 2024-11-16 18:10:43 +01:00
Namespace i18n store methods with "i18n" property
This commit is contained in:
parent
1e8597150c
commit
07691f3a6d
@ -2,3 +2,4 @@
|
|||||||
/.*
|
/.*
|
||||||
/yarn.lock
|
/yarn.lock
|
||||||
/package-lock.json
|
/package-lock.json
|
||||||
|
/src
|
55
README.md
55
README.md
@ -12,60 +12,55 @@
|
|||||||
import i18n from 'svelte-i18n'
|
import i18n from 'svelte-i18n'
|
||||||
import { Store } from 'svelte/store'
|
import { Store } from 'svelte/store'
|
||||||
|
|
||||||
const store = new Store()
|
/** i18n(svelteStore, { dictionary }) */
|
||||||
|
const store = i18n(new Store(), {
|
||||||
/** i18n(svelteStore, arrayOfLocalesObjects) */
|
dictionary: {
|
||||||
i18n(store, [
|
|
||||||
{
|
|
||||||
'pt-BR': {
|
'pt-BR': {
|
||||||
message: 'Mensagem',
|
message: 'Mensagem',
|
||||||
messages: {
|
messages: {
|
||||||
alert: 'Alerta',
|
alert: 'Alerta',
|
||||||
error: 'Erro'
|
error: 'Erro',
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
'en-US': {
|
'en-US': {
|
||||||
message: 'Message',
|
message: 'Message',
|
||||||
messages: {
|
messages: {
|
||||||
alert: 'Alert',
|
alert: 'Alert',
|
||||||
error: 'Error'
|
error: 'Error',
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
/** Locales are deeply merged */
|
},
|
||||||
{
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extend the initial dictionary.
|
||||||
|
* Dictionaries are deeply merged.
|
||||||
|
* */
|
||||||
|
store.i18n.extendDictionary({
|
||||||
'pt-BR': {
|
'pt-BR': {
|
||||||
messages: {
|
messages: {
|
||||||
warn: 'Aviso',
|
warn: 'Aviso',
|
||||||
success: 'Sucesso'
|
success: 'Sucesso',
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
'en-US': {
|
'en-US': {
|
||||||
messages: {
|
messages: {
|
||||||
warn: 'Warn',
|
warn: 'Warn',
|
||||||
success: 'Success'
|
success: 'Success',
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
})
|
||||||
])
|
|
||||||
|
/** Set the initial locale */
|
||||||
|
store.i18n.setLocale('en-US')
|
||||||
```
|
```
|
||||||
|
|
||||||
### On `templates`
|
### On `templates`
|
||||||
|
|
||||||
```html
|
```html
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
{$_('message')}: {upper($_('messages.success'))}
|
{$_('message')}: {$_.upper('messages.success'))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
|
||||||
import { upper } from 'svelte-i18n';
|
|
||||||
export default {
|
|
||||||
helpers: {
|
|
||||||
upper,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Renders:
|
Renders:
|
||||||
|
3676
package-lock.json
generated
3676
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
33
package.json
33
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "svelte-i18n",
|
"name": "svelte-i18n",
|
||||||
"version": "0.0.0",
|
"version": "0.0.1",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "dist/i18n.js",
|
"main": "dist/i18n.js",
|
||||||
"types": "src/index.d.ts",
|
"types": "src/index.d.ts",
|
||||||
@ -13,9 +13,9 @@
|
|||||||
"start": "microbundle watch --name='svelte-i18n'",
|
"start": "microbundle watch --name='svelte-i18n'",
|
||||||
"test": "jest --no-cache --verbose",
|
"test": "jest --no-cache --verbose",
|
||||||
"test:watch": "jest --no-cache --verbose --watchAll",
|
"test:watch": "jest --no-cache --verbose --watchAll",
|
||||||
"lint": "eslint \"*.js\" \"src/**/*.js\"",
|
"lint": "eslint \"src/**/*.js\"",
|
||||||
"format": "prettier --loglevel silent --write \"*.js\" \"src/**/*.js\" && eslint --fix \"*.js\" \"src/**/*.js\"",
|
"format": "prettier --loglevel silent --write \"src/**/*.js\" && eslint --fix \"src/**/*.js\"",
|
||||||
"prepublishOnly": "npm run format && npm run test"
|
"prepublishOnly": "npm run format && npm run test && npm run build"
|
||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
"verbose": true,
|
"verbose": true,
|
||||||
@ -29,7 +29,8 @@
|
|||||||
],
|
],
|
||||||
"coveragePathIgnorePatterns": [
|
"coveragePathIgnorePatterns": [
|
||||||
"/node_modules/",
|
"/node_modules/",
|
||||||
"/test/"
|
"/test/",
|
||||||
|
"/src/formatter.js"
|
||||||
],
|
],
|
||||||
"coverageThreshold": {
|
"coverageThreshold": {
|
||||||
"global": {
|
"global": {
|
||||||
@ -43,26 +44,26 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.0.0-beta.56",
|
"@babel/core": "^7.0.0-beta.56",
|
||||||
|
"@babel/preset-env": "^7.0.0-beta.56",
|
||||||
"babel-core": "^7.0.0-bridge.0",
|
"babel-core": "^7.0.0-bridge.0",
|
||||||
"babel-jest": "^23.4.2",
|
"babel-jest": "^23.4.2",
|
||||||
"eslint": "^4.19.1",
|
"eslint": "^5.3.0",
|
||||||
"eslint-config-prettier": "^2.9.0",
|
"eslint-config-prettier": "^2.9.0",
|
||||||
"eslint-config-standard": "^11.0.0",
|
"eslint-config-standard": "^11.0.0",
|
||||||
"eslint-plugin-import": "^2.11.0",
|
"eslint-plugin-import": "^2.13.0",
|
||||||
"eslint-plugin-node": "^6.0.1",
|
"eslint-plugin-node": "^7.0.1",
|
||||||
"eslint-plugin-prettier": "^2.6.0",
|
"eslint-plugin-prettier": "^2.6.2",
|
||||||
"eslint-plugin-promise": "^3.7.0",
|
"eslint-plugin-promise": "^3.8.0",
|
||||||
"eslint-plugin-standard": "^3.1.0",
|
"eslint-plugin-standard": "^3.1.0",
|
||||||
"jest": "^22.4.3",
|
"jest": "^23.4.2",
|
||||||
"microbundle": "^0.4.4",
|
"microbundle": "^0.6.0",
|
||||||
"prettier": "^1.12.1",
|
"prettier": "^1.14.1",
|
||||||
"svelte": "^2.9.10"
|
"svelte": "^2.9.11"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"svelte": "^2.9.10"
|
"svelte": "^2.9.11"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/preset-env": "^7.0.0-beta.56",
|
|
||||||
"deepmerge": "^2.1.1",
|
"deepmerge": "^2.1.1",
|
||||||
"object-resolve-path": "^1.1.1"
|
"object-resolve-path": "^1.1.1"
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
|
/* istanbul ignore */
|
||||||
/**
|
/**
|
||||||
* Adapted from 'https://github.com/kazupon/vue-i18n/blob/dev/src/format.js'
|
* Adapted from 'https://github.com/kazupon/vue-i18n/blob/dev/src/format.js'
|
||||||
* Copyright (c) 2016 kazuya kawaguchi
|
* Copyright (c) 2016 kazuya kawaguchi
|
||||||
**/
|
**/
|
||||||
import { isObject, warn } from './utils'
|
import { isObject } from './utils'
|
||||||
|
|
||||||
const RE_TOKEN_LIST_VALUE = /^(\d)+/
|
const RE_TOKEN_LIST_VALUE = /^(\d)+/
|
||||||
const RE_TOKEN_NAMED_VALUE = /^(\w)+/
|
const RE_TOKEN_NAMED_VALUE = /^(\w)+/
|
||||||
@ -79,7 +80,11 @@ export function compile(tokens, values) {
|
|||||||
const compiled = []
|
const compiled = []
|
||||||
let index = 0
|
let index = 0
|
||||||
|
|
||||||
const mode = Array.isArray(values) ? 'list' : isObject(values) ? 'named' : 'unknown'
|
const mode = Array.isArray(values)
|
||||||
|
? 'list'
|
||||||
|
: isObject(values)
|
||||||
|
? 'named'
|
||||||
|
: 'unknown'
|
||||||
|
|
||||||
if (mode === 'unknown') {
|
if (mode === 'unknown') {
|
||||||
return compiled
|
return compiled
|
||||||
@ -99,14 +104,18 @@ export function compile(tokens, values) {
|
|||||||
compiled.push(values[token.value])
|
compiled.push(values[token.value])
|
||||||
} else {
|
} else {
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
warn(`Type of token '${token.type}' and format of value '${mode}' don't match!`)
|
console.warn(
|
||||||
|
`[svelte-i18n] Type of token '${
|
||||||
|
token.type
|
||||||
|
}' and format of value '${mode}' don't match!`,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
|
||||||
case 'unknown':
|
case 'unknown':
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
warn(`Detect 'unknown' type of token!`)
|
console.warn(`[svelte-i18n] Detect 'unknown' type of token!`)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
30
src/index.js
30
src/index.js
@ -1,12 +1,11 @@
|
|||||||
|
import deepmerge from 'deepmerge'
|
||||||
|
import resolvePath from 'object-resolve-path'
|
||||||
import { capital, title, upper, lower } from './utils'
|
import { capital, title, upper, lower } from './utils'
|
||||||
import Formatter from './formatter'
|
import Formatter from './formatter'
|
||||||
|
|
||||||
const resolvePath = a => a
|
export function i18n(store, { dictionary }) {
|
||||||
|
|
||||||
export function i18n(store, localesList) {
|
|
||||||
const formatter = new Formatter()
|
const formatter = new Formatter()
|
||||||
const locales = {} // deepmerge.all(localesList)
|
let dictionaries = {}
|
||||||
let currentLocale
|
let currentLocale
|
||||||
|
|
||||||
const getLocalizedMessage = (
|
const getLocalizedMessage = (
|
||||||
@ -15,7 +14,7 @@ export function i18n(store, localesList) {
|
|||||||
locale = currentLocale,
|
locale = currentLocale,
|
||||||
transformers = undefined,
|
transformers = undefined,
|
||||||
) => {
|
) => {
|
||||||
let message = resolvePath(locales[locale], path)
|
let message = resolvePath(dictionaries[locale], path)
|
||||||
|
|
||||||
if (!message) return path
|
if (!message) return path
|
||||||
|
|
||||||
@ -47,17 +46,32 @@ export function i18n(store, localesList) {
|
|||||||
},
|
},
|
||||||
plural(path, counter, interpolations, locale) {
|
plural(path, counter, interpolations, locale) {
|
||||||
return getLocalizedMessage(path, interpolations, locale, [
|
return getLocalizedMessage(path, interpolations, locale, [
|
||||||
message => message.split('|')[Math.min(Math.abs(counter), 2)],
|
message => {
|
||||||
|
const choice =
|
||||||
|
typeof counter === 'number' ? Math.min(Math.abs(counter), 2) : 0
|
||||||
|
return message.split('|')[choice]
|
||||||
|
},
|
||||||
])
|
])
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
store.setLocale = locale => store.fire('locale', locale)
|
store.i18n = {
|
||||||
|
setLocale(locale) {
|
||||||
|
store.fire('locale', locale)
|
||||||
|
},
|
||||||
|
extendDictionary(...list) {
|
||||||
|
dictionaries = deepmerge.all([dictionaries, ...list])
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
store.i18n.extendDictionary(dictionary)
|
||||||
|
|
||||||
store.on('locale', newLocale => {
|
store.on('locale', newLocale => {
|
||||||
currentLocale = newLocale
|
currentLocale = newLocale
|
||||||
const _ = getLocalizedMessage
|
const _ = getLocalizedMessage
|
||||||
|
|
||||||
Object.assign(_, utilities)
|
Object.assign(_, utilities)
|
||||||
|
|
||||||
store.set({ locale: newLocale, _ })
|
store.set({ locale: newLocale, _ })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
19
src/utils.js
19
src/utils.js
@ -1,15 +1,6 @@
|
|||||||
export const capital = (str) => str.replace(/(^|\s)\S/, l => l.toUpperCase())
|
export const capital = str => str.replace(/(^|\s)\S/, l => l.toUpperCase())
|
||||||
export const title = (str) => str.replace(/(^|\s)\S/g, l => l.toUpperCase())
|
export const title = str => str.replace(/(^|\s)\S/g, l => l.toUpperCase())
|
||||||
export const upper = (str) => str.toLocaleUpperCase()
|
export const upper = str => str.toLocaleUpperCase()
|
||||||
export const lower = (str) => str.toLocaleLowerCase()
|
export const lower = str => str.toLocaleLowerCase()
|
||||||
|
|
||||||
export const isObject = (obj) => obj !== null && typeof obj === 'object'
|
export const isObject = obj => obj !== null && typeof obj === 'object'
|
||||||
|
|
||||||
export function warn(msg, err) {
|
|
||||||
if (typeof console !== 'undefined') {
|
|
||||||
console.warn(`[svelte-i18n] ${msg}`)
|
|
||||||
if (err) {
|
|
||||||
console.warn(err.stack)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
// TODO: A more serious test
|
||||||
|
|
||||||
import { i18n } from '../src/index'
|
import { i18n } from '../src/index'
|
||||||
import { Store } from 'svelte/store.umd'
|
import { Store } from 'svelte/store.umd'
|
||||||
import { capital, title, upper, lower, isObject } from '../src/utils'
|
import { capital, title, upper, lower, isObject, warn } from '../src/utils'
|
||||||
|
|
||||||
const store = new Store()
|
const store = new Store()
|
||||||
const locales = {
|
const locales = {
|
||||||
@ -11,31 +13,23 @@ const locales = {
|
|||||||
pluralization: 'Zero | Um | Muito!',
|
pluralization: 'Zero | Um | Muito!',
|
||||||
interpolation: {
|
interpolation: {
|
||||||
key: 'Olá, {0}! Como está {1}?',
|
key: 'Olá, {0}! Como está {1}?',
|
||||||
named: 'Olá, {name}! Como está {time}?'
|
named: 'Olá, {name}! Como está {time}?',
|
||||||
},
|
},
|
||||||
wow: {
|
wow: {
|
||||||
much: {
|
much: {
|
||||||
deep: {
|
deep: {
|
||||||
list: ['Muito', 'muito profundo']
|
list: ['Muito', 'muito profundo'],
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
obj: {
|
obj: {
|
||||||
a: 'a'
|
a: 'a',
|
||||||
}
|
b: 'b',
|
||||||
}
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
i18n(store, [
|
i18n(store, { dictionary: locales })
|
||||||
locales,
|
|
||||||
{
|
|
||||||
'pt-br': {
|
|
||||||
obj: {
|
|
||||||
b: 'b'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dummy test
|
* Dummy test
|
||||||
@ -62,18 +56,18 @@ describe('Localization', () => {
|
|||||||
expect(_).toBeInstanceOf(Function)
|
expect(_).toBeInstanceOf(Function)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should have a .setLocale() method', () => {
|
it('should have a .i18n.setLocale() method', () => {
|
||||||
expect(store.setLocale).toBeInstanceOf(Function)
|
expect(store.i18n.setLocale).toBeInstanceOf(Function)
|
||||||
|
|
||||||
store.setLocale('pt-br')
|
store.i18n.setLocale('pt-br')
|
||||||
const { locale } = store.get()
|
const { locale } = store.get()
|
||||||
|
|
||||||
expect(locale).toBe('pt-br')
|
expect(locale).toBe('pt-br')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should return the message id when no message identified by it was found', () => {
|
it('should return the message id when no message identified by it was found', () => {
|
||||||
store.setLocale('pt-br')
|
store.i18n.setLocale('pt-br')
|
||||||
const { locale, _ } = store.get()
|
const { _ } = store.get()
|
||||||
|
|
||||||
expect(_('non.existent')).toBe('non.existent')
|
expect(_('non.existent')).toBe('non.existent')
|
||||||
})
|
})
|
||||||
@ -84,15 +78,15 @@ describe('Localization', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should get a deep nested message by its string path', () => {
|
it('should get a deep nested message by its string path', () => {
|
||||||
store.setLocale('pt-br')
|
store.i18n.setLocale('pt-br')
|
||||||
const { locale, _ } = store.get()
|
const { _ } = store.get()
|
||||||
|
|
||||||
expect(_('obj.b')).toBe('b')
|
expect(_('obj.b')).toBe('b')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should get a message within an array by its index', () => {
|
it('should get a message within an array by its index', () => {
|
||||||
store.setLocale('pt-br')
|
store.i18n.setLocale('pt-br')
|
||||||
const { locale, _ } = store.get()
|
const { _ } = store.get()
|
||||||
|
|
||||||
expect(_('phrases[1]')).toBe(locales['pt-br'].phrases[1])
|
expect(_('phrases[1]')).toBe(locales['pt-br'].phrases[1])
|
||||||
|
|
||||||
@ -101,28 +95,31 @@ describe('Localization', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should interpolate with {numeric} placeholders', () => {
|
it('should interpolate with {numeric} placeholders', () => {
|
||||||
store.setLocale('pt-br')
|
store.i18n.setLocale('pt-br')
|
||||||
const { locale, _ } = store.get()
|
const { _ } = store.get()
|
||||||
|
|
||||||
expect(_('interpolation.key', ['Chris', 'o dia'])).toBe('Olá, Chris! Como está o dia?')
|
expect(_('interpolation.key', ['Chris', 'o dia'])).toBe(
|
||||||
|
'Olá, Chris! Como está o dia?',
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should interpolate with {named} placeholders', () => {
|
it('should interpolate with {named} placeholders', () => {
|
||||||
store.setLocale('pt-br')
|
store.i18n.setLocale('pt-br')
|
||||||
const { locale, _ } = store.get()
|
const { _ } = store.get()
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
_('interpolation.named', {
|
_('interpolation.named', {
|
||||||
name: 'Chris',
|
name: 'Chris',
|
||||||
time: 'o dia'
|
time: 'o dia',
|
||||||
})
|
}),
|
||||||
).toBe('Olá, Chris! Como está o dia?')
|
).toBe('Olá, Chris! Como está o dia?')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should handle pluralization with _.plural()', () => {
|
it('should handle pluralization with _.plural()', () => {
|
||||||
store.setLocale('pt-br')
|
store.i18n.setLocale('pt-br')
|
||||||
const { locale, _ } = store.get()
|
const { _ } = store.get()
|
||||||
|
|
||||||
|
expect(_.plural('pluralization')).toBe('Zero')
|
||||||
expect(_.plural('pluralization', 0)).toBe('Zero')
|
expect(_.plural('pluralization', 0)).toBe('Zero')
|
||||||
expect(_.plural('pluralization', 1)).toBe('Um')
|
expect(_.plural('pluralization', 1)).toBe('Um')
|
||||||
expect(_.plural('pluralization', -1)).toBe('Um')
|
expect(_.plural('pluralization', -1)).toBe('Um')
|
||||||
@ -134,7 +131,7 @@ describe('Localization', () => {
|
|||||||
|
|
||||||
describe('Localization utilities', () => {
|
describe('Localization utilities', () => {
|
||||||
it('should capital a translated message', () => {
|
it('should capital a translated message', () => {
|
||||||
store.setLocale('pt-br')
|
store.i18n.setLocale('pt-br')
|
||||||
const { _ } = store.get()
|
const { _ } = store.get()
|
||||||
|
|
||||||
expect(capital('Adoro banana')).toBe('Adoro banana')
|
expect(capital('Adoro banana')).toBe('Adoro banana')
|
||||||
@ -142,7 +139,7 @@ describe('Localization utilities', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should title a translated message', () => {
|
it('should title a translated message', () => {
|
||||||
store.setLocale('pt-br')
|
store.i18n.setLocale('pt-br')
|
||||||
const { _ } = store.get()
|
const { _ } = store.get()
|
||||||
|
|
||||||
expect(title('Adoro Banana')).toBe('Adoro Banana')
|
expect(title('Adoro Banana')).toBe('Adoro Banana')
|
||||||
@ -150,7 +147,7 @@ describe('Localization utilities', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should lowercase a translated message', () => {
|
it('should lowercase a translated message', () => {
|
||||||
store.setLocale('pt-br')
|
store.i18n.setLocale('pt-br')
|
||||||
const { _ } = store.get()
|
const { _ } = store.get()
|
||||||
|
|
||||||
expect(lower('adoro banana')).toBe('adoro banana')
|
expect(lower('adoro banana')).toBe('adoro banana')
|
||||||
@ -158,7 +155,7 @@ describe('Localization utilities', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should uppercase a translated message', () => {
|
it('should uppercase a translated message', () => {
|
||||||
store.setLocale('pt-br')
|
store.i18n.setLocale('pt-br')
|
||||||
const { _ } = store.get()
|
const { _ } = store.get()
|
||||||
|
|
||||||
expect(upper('ADORO BANANA')).toBe('ADORO BANANA')
|
expect(upper('ADORO BANANA')).toBe('ADORO BANANA')
|
||||||
|
Loading…
Reference in New Issue
Block a user