mirror of
https://github.com/cupcakearmy/svelte-i18n.git
synced 2024-06-30 18:54:49 +02:00
Add method to get client locale
This commit is contained in:
parent
563b400193
commit
8de3d27e09
20
README.md
20
README.md
|
@ -15,7 +15,7 @@
|
||||||
The `locale` store defines what is the current locale.
|
The `locale` store defines what is the current locale.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import { locale, dictionary } from 'svelte-i18n'
|
import { locale, dictionary, getClientLocale } from 'svelte-i18n'
|
||||||
|
|
||||||
// Set the current locale to en-US
|
// Set the current locale to en-US
|
||||||
locale.set('en-US')
|
locale.set('en-US')
|
||||||
|
@ -24,8 +24,26 @@ locale.set('en-US')
|
||||||
locale.subscribe(() => {
|
locale.subscribe(() => {
|
||||||
console.log('locale change')
|
console.log('locale change')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// svelte-i18n exports a method to help getting the current client locale
|
||||||
|
locale.set(
|
||||||
|
getClientLocale({
|
||||||
|
// the fallback locale, if didn't find any
|
||||||
|
fallback: 'en-US',
|
||||||
|
// set to 'true' to check the 'window.navigator.language'
|
||||||
|
navigator: true,
|
||||||
|
// set the key name to look for a locale on 'window.location.search'
|
||||||
|
// 'example.com?locale=en-US'
|
||||||
|
search: 'lang',
|
||||||
|
// set the key name to look for a locale on 'window.location.hash'
|
||||||
|
// 'example.com#locale=en-US'
|
||||||
|
hash: 'locale',
|
||||||
|
}),
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If a locale with the format `xx-YY` is not found, `svelte-i18n` looks for the locale `xx` as well.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### The dictionary
|
### The dictionary
|
||||||
|
|
|
@ -1,12 +1,4 @@
|
||||||
import { locale, dictionary } from 'svelte-i18n'
|
import { locale, dictionary, getClientLocale } from 'svelte-i18n'
|
||||||
|
|
||||||
// setting the locale
|
|
||||||
locale.set('pt')
|
|
||||||
|
|
||||||
// subscribe to locale changes
|
|
||||||
locale.subscribe(() => {
|
|
||||||
console.log('locale change')
|
|
||||||
})
|
|
||||||
|
|
||||||
// defining a locale dictionary
|
// defining a locale dictionary
|
||||||
dictionary.set({
|
dictionary.set({
|
||||||
|
@ -16,7 +8,8 @@ dictionary.set({
|
||||||
ask: 'Por favor, digite seu nome',
|
ask: 'Por favor, digite seu nome',
|
||||||
message: 'Olá {name}, como vai?',
|
message: 'Olá {name}, como vai?',
|
||||||
},
|
},
|
||||||
photos: 'Você {n, plural, =0 {não tem fotos.} =1 {tem uma foto.} other {tem # fotos.}}',
|
photos:
|
||||||
|
'Você {n, plural, =0 {não tem fotos.} =1 {tem uma foto.} other {tem # fotos.}}',
|
||||||
cats: 'Tenho {n, number} {n,plural,=0{gatos}one{gato}other{gatos}}',
|
cats: 'Tenho {n, number} {n,plural,=0{gatos}one{gato}other{gatos}}',
|
||||||
},
|
},
|
||||||
en: {
|
en: {
|
||||||
|
@ -25,7 +18,20 @@ dictionary.set({
|
||||||
ask: 'Please type your name',
|
ask: 'Please type your name',
|
||||||
message: 'Hello {name}, how are you?',
|
message: 'Hello {name}, how are you?',
|
||||||
},
|
},
|
||||||
photos: 'You have {n, plural, =0 {no photos.} =1 {one photo.} other {# photos.}}',
|
photos:
|
||||||
cats: 'I have {n, number} {n,plural,one{cat}other{cats}}'
|
'You have {n, plural, =0 {no photos.} =1 {one photo.} other {# photos.}}',
|
||||||
|
cats: 'I have {n, number} {n,plural,one{cat}other{cats}}',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
locale.set(
|
||||||
|
getClientLocale({
|
||||||
|
navigator: true,
|
||||||
|
hash: 'lang',
|
||||||
|
default: 'pt',
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
locale.subscribe(l => {
|
||||||
|
console.log('locale change', l)
|
||||||
|
})
|
||||||
|
|
2
package-lock.json
generated
2
package-lock.json
generated
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "svelte-i18n",
|
"name": "svelte-i18n",
|
||||||
"version": "1.0.6-beta",
|
"version": "1.0.7",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
38
src/index.js
38
src/index.js
|
@ -2,11 +2,26 @@ import { writable, derived } from 'svelte/store'
|
||||||
import resolvePath from 'object-resolve-path'
|
import resolvePath from 'object-resolve-path'
|
||||||
import IntlMessageFormat from 'intl-messageformat'
|
import IntlMessageFormat from 'intl-messageformat'
|
||||||
import memoize from 'micro-memoize'
|
import memoize from 'micro-memoize'
|
||||||
import { capital, title, upper, lower } from './utils.js'
|
import { capital, title, upper, lower, getClientLocale } from './utils.js'
|
||||||
|
|
||||||
let currentLocale
|
let currentLocale
|
||||||
let currentDictionary
|
let currentDictionary
|
||||||
|
|
||||||
|
const getAvailableLocale = newLocale => {
|
||||||
|
if (currentDictionary[newLocale]) return newLocale
|
||||||
|
|
||||||
|
// istanbul ignore else
|
||||||
|
if (typeof newLocale === 'string') {
|
||||||
|
const fallbackLocale = newLocale.split('-').shift()
|
||||||
|
|
||||||
|
if (currentDictionary[fallbackLocale]) {
|
||||||
|
return fallbackLocale
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
const getMessageFormatter = memoize(
|
const getMessageFormatter = memoize(
|
||||||
(message, locale, formats) => new IntlMessageFormat(message, locale, formats),
|
(message, locale, formats) => new IntlMessageFormat(message, locale, formats),
|
||||||
)
|
)
|
||||||
|
@ -37,22 +52,16 @@ const getLocalizedMessage = (path, interpolations, locale = currentLocale) => {
|
||||||
|
|
||||||
getLocalizedMessage.time = (t, format = 'short', locale) =>
|
getLocalizedMessage.time = (t, format = 'short', locale) =>
|
||||||
formatMessage(`{t,time,${format}}`, { t }, locale)
|
formatMessage(`{t,time,${format}}`, { t }, locale)
|
||||||
|
|
||||||
getLocalizedMessage.date = (d, format = 'short', locale) =>
|
getLocalizedMessage.date = (d, format = 'short', locale) =>
|
||||||
formatMessage(`{d,date,${format}}`, { d }, locale)
|
formatMessage(`{d,date,${format}}`, { d }, locale)
|
||||||
|
|
||||||
getLocalizedMessage.number = (n, locale) =>
|
getLocalizedMessage.number = (n, locale) =>
|
||||||
formatMessage('{n,number}', { n }, locale)
|
formatMessage('{n,number}', { n }, locale)
|
||||||
|
|
||||||
getLocalizedMessage.capital = (path, interpolations, locale) =>
|
getLocalizedMessage.capital = (path, interpolations, locale) =>
|
||||||
capital(getLocalizedMessage(path, interpolations, locale))
|
capital(getLocalizedMessage(path, interpolations, locale))
|
||||||
|
|
||||||
getLocalizedMessage.title = (path, interpolations, locale) =>
|
getLocalizedMessage.title = (path, interpolations, locale) =>
|
||||||
title(getLocalizedMessage(path, interpolations, locale))
|
title(getLocalizedMessage(path, interpolations, locale))
|
||||||
|
|
||||||
getLocalizedMessage.upper = (path, interpolations, locale) =>
|
getLocalizedMessage.upper = (path, interpolations, locale) =>
|
||||||
upper(getLocalizedMessage(path, interpolations, locale))
|
upper(getLocalizedMessage(path, interpolations, locale))
|
||||||
|
|
||||||
getLocalizedMessage.lower = (path, interpolations, locale) =>
|
getLocalizedMessage.lower = (path, interpolations, locale) =>
|
||||||
lower(getLocalizedMessage(path, interpolations, locale))
|
lower(getLocalizedMessage(path, interpolations, locale))
|
||||||
|
|
||||||
|
@ -62,10 +71,21 @@ dictionary.subscribe(newDictionary => {
|
||||||
})
|
})
|
||||||
|
|
||||||
const locale = writable({})
|
const locale = writable({})
|
||||||
|
const localeSet = locale.set
|
||||||
|
locale.set = newLocale => {
|
||||||
|
const availableLocale = getAvailableLocale(newLocale)
|
||||||
|
if (availableLocale) {
|
||||||
|
return localeSet(availableLocale)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.warn(`[svelte-i18n] Locale "${newLocale}" not found.`)
|
||||||
|
return localeSet(newLocale)
|
||||||
|
}
|
||||||
|
locale.update = fn => localeSet(fn(currentLocale))
|
||||||
locale.subscribe(newLocale => {
|
locale.subscribe(newLocale => {
|
||||||
currentLocale = newLocale
|
currentLocale = newLocale
|
||||||
})
|
})
|
||||||
|
|
||||||
const format = derived(locale, () => getLocalizedMessage)
|
const format = derived([locale, dictionary], () => getLocalizedMessage)
|
||||||
|
|
||||||
export { locale, format as _, format, dictionary }
|
export { locale, format as _, format, dictionary, getClientLocale }
|
||||||
|
|
33
src/utils.js
33
src/utils.js
|
@ -2,3 +2,36 @@ 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 getClientLocale = ({ navigator, hash, search, fallback } = {}) => {
|
||||||
|
let locale
|
||||||
|
|
||||||
|
const getFromURL = (urlPart, key) => {
|
||||||
|
const keyVal = urlPart
|
||||||
|
.substr(1)
|
||||||
|
.split('&')
|
||||||
|
.find(i => i.indexOf(key) === 0)
|
||||||
|
|
||||||
|
if (keyVal) {
|
||||||
|
return keyVal.split('=').pop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// istanbul ignore else
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
if (navigator) {
|
||||||
|
// istanbul ignore next
|
||||||
|
locale = window.navigator.language || window.navigator.languages[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
if (search) {
|
||||||
|
locale = getFromURL(window.location.search, search)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hash) {
|
||||||
|
locale = getFromURL(window.location.hash, hash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return locale || fallback
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { dictionary, locale, format } from '../src/index'
|
import { dictionary, locale, format, getClientLocale } from '../src/index'
|
||||||
|
|
||||||
let _
|
let _
|
||||||
let currentLocale
|
let currentLocale
|
||||||
|
@ -42,7 +42,19 @@ it('should change locale', () => {
|
||||||
expect(currentLocale).toBe('en')
|
expect(currentLocale).toBe('en')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should fallback to message id if id is not found', () => {
|
it('should fallback to existing locale', () => {
|
||||||
|
locale.set('pt-BR')
|
||||||
|
expect(currentLocale).toBe('pt')
|
||||||
|
|
||||||
|
locale.set('en-US')
|
||||||
|
expect(currentLocale).toBe('en')
|
||||||
|
|
||||||
|
locale.set('non-existent')
|
||||||
|
expect(currentLocale).toBe('non-existent')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should fallback to message id if id is not found', () => {
|
||||||
|
locale.set('en')
|
||||||
expect(_('batatinha')).toBe('batatinha')
|
expect(_('batatinha')).toBe('batatinha')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -71,39 +83,74 @@ it('should interpolate message with variables according to passed locale', () =>
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('utilities', () => {
|
describe('utilities', () => {
|
||||||
beforeAll(() => {
|
describe('get locale', () => {
|
||||||
locale.set('en')
|
beforeEach(() => {
|
||||||
|
delete window.location
|
||||||
|
window.location = {
|
||||||
|
hash: '',
|
||||||
|
search: '',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should get the locale based on the passed hash parameter', () => {
|
||||||
|
window.location.hash = '#locale=en-US&lang=pt-BR'
|
||||||
|
expect(getClientLocale({ hash: 'locale' })).toBe('en-US')
|
||||||
|
expect(getClientLocale({ hash: 'lang' })).toBe('pt-BR')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should get the locale based on the passed search parameter', () => {
|
||||||
|
window.location.search = '?locale=en-US&lang=pt-BR'
|
||||||
|
expect(getClientLocale({ search: 'locale' })).toBe('en-US')
|
||||||
|
expect(getClientLocale({ search: 'lang' })).toBe('pt-BR')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should get the locale based on the navigator language', () => {
|
||||||
|
expect(getClientLocale({ navigator: true })).toBe(
|
||||||
|
window.navigator.language,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should get the fallback locale', () => {
|
||||||
|
expect(getClientLocale({ navigator: false, fallback: 'pt' })).toBe('pt')
|
||||||
|
expect(getClientLocale({ hash: 'locale', fallback: 'pt' })).toBe('pt')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should capital a translated message', () => {
|
describe('format utils', () => {
|
||||||
expect(_.capital('hi')).toBe('Hi yo')
|
beforeAll(() => {
|
||||||
})
|
locale.set('en')
|
||||||
|
})
|
||||||
|
|
||||||
it('should title a translated message', () => {
|
it('should capital a translated message', () => {
|
||||||
expect(_.title('hi')).toBe('Hi Yo')
|
expect(_.capital('hi')).toBe('Hi yo')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should lowercase a translated message', () => {
|
it('should title a translated message', () => {
|
||||||
expect(_.lower('hi')).toBe('hi yo')
|
expect(_.title('hi')).toBe('Hi Yo')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should uppercase a translated message', () => {
|
it('should lowercase a translated message', () => {
|
||||||
expect(_.upper('hi')).toBe('HI YO')
|
expect(_.lower('hi')).toBe('hi yo')
|
||||||
})
|
})
|
||||||
|
|
||||||
const date = new Date(2019, 3, 24, 23, 45)
|
it('should uppercase a translated message', () => {
|
||||||
it('should format a time value', () => {
|
expect(_.upper('hi')).toBe('HI YO')
|
||||||
locale.set('en')
|
})
|
||||||
expect(_.time(date)).toBe('11:45 PM')
|
|
||||||
expect(_.time(date, 'medium')).toBe('11:45:00 PM')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should format a date value', () => {
|
const date = new Date(2019, 3, 24, 23, 45)
|
||||||
expect(_.date(date)).toBe('4/24/19')
|
it('should format a time value', () => {
|
||||||
expect(_.date(date, 'medium')).toBe('Apr 24, 2019')
|
locale.set('en')
|
||||||
})
|
expect(_.time(date)).toBe('11:45 PM')
|
||||||
// number
|
expect(_.time(date, 'medium')).toBe('11:45:00 PM')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should format a date value', () => {
|
||||||
|
expect(_.date(date)).toBe('4/24/19')
|
||||||
|
expect(_.date(date, 'medium')).toBe('Apr 24, 2019')
|
||||||
|
})
|
||||||
|
// number
|
||||||
it('should format a date value', () => {
|
it('should format a date value', () => {
|
||||||
expect(_.number(123123123)).toBe('123,123,123')
|
expect(_.number(123123123)).toBe('123,123,123')
|
||||||
})
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue
Block a user