feat: 🎸 also look for message in generic locale

Issues: #19
This commit is contained in:
Christian Kaisermann 2019-11-19 21:04:50 -03:00
parent 31d96f24ff
commit e5d7b84241
8 changed files with 55 additions and 75 deletions

View File

@ -2,7 +2,7 @@ import { writable, derived } from 'svelte/store'
import resolvePath from 'object-resolve-path'
import memoize from 'micro-memoize'
import { capital, title, upper, lower, getClientLocale } from './utils'
import { capital, title, upper, lower, getClientLocale, getGenericLocaleFrom } from './utils'
import { MessageObject, Formatter } from './types'
import {
getMessageFormatter,
@ -14,7 +14,9 @@ import {
let currentLocale: string
let currentDictionary: Record<string, any>
function getAvailableLocale(locale: string) {
const hasLocale = (locale: string) => locale in currentDictionary
function getAvailableLocale(locale: string): { locale: string; loader?: () => Promise<any> } {
if (currentDictionary[locale]) {
if (typeof currentDictionary[locale] === 'function') {
return { locale, loader: currentDictionary[locale] }
@ -22,22 +24,29 @@ function getAvailableLocale(locale: string) {
return { locale }
}
locale = locale.split('-').shift() //
if (currentDictionary[locale]) {
if (typeof currentDictionary[locale] === 'function') {
return { locale, loader: currentDictionary[locale] }
}
return { locale }
locale = getGenericLocaleFrom(locale)
if (locale != null) {
return getAvailableLocale(locale)
}
return { locale: null }
}
const lookupMessage = memoize((path: string, locale: string) => {
const lookupMessage = memoize((path: string, locale: string): string => {
if (path in currentDictionary[locale]) {
return currentDictionary[locale][path]
}
return resolvePath(currentDictionary[locale], path)
const message = resolvePath(currentDictionary[locale], path)
if (message == null) {
const genericLocale = getGenericLocaleFrom(locale)
if (genericLocale != null && hasLocale(genericLocale)) {
return lookupMessage(path, genericLocale)
}
return null
}
return message
})
const formatMessage: Formatter = (id, options = {}) => {

View File

@ -5,12 +5,15 @@ export const title = (str: string) =>
export const upper = (str: string) => str.toLocaleUpperCase()
export const lower = (str: string) => str.toLocaleLowerCase()
export function getGenericLocaleFrom(locale: string) {
return locale.length > 2 ? locale.split('-').shift() : null
}
export const getClientLocale = ({
navigator,
hash,
search,
default: defaultLocale,
fallback = defaultLocale,
}: {
navigator?: boolean
hash?: string
@ -47,28 +50,5 @@ export const getClientLocale = ({
}
}
return locale || defaultLocale || fallback
return locale || defaultLocale
}
// function mergeDeep(target: any, source: any) {
// const isObject = (obj: any) => obj && typeof obj === 'object'
// if (!isObject(target) || !isObject(source)) {
// return source
// }
// Object.keys(source).forEach(key => {
// const targetValue = target[key]
// const sourceValue = source[key]
// if (Array.isArray(targetValue) && Array.isArray(sourceValue)) {
// target[key] = targetValue.concat(sourceValue)
// } else if (isObject(targetValue) && isObject(sourceValue)) {
// target[key] = mergeDeep(Object.assign({}, targetValue), sourceValue)
// } else {
// target[key] = sourceValue
// }
// })
// return target
// }

View File

@ -16,6 +16,7 @@ let currentLocale: string
const dict = {
pt: require('../fixtures/pt.json'),
en: require('../fixtures/en.json'),
'en-GB': require('../fixtures/en-GB.json'),
}
format.subscribe(formatFn => {
@ -61,6 +62,13 @@ describe('dictionary', () => {
})
describe('formatting', () => {
it('should translate to current locale', () => {
locale.set('pt')
expect(_('switch.lang')).toBe('Trocar idioma')
locale.set('en')
expect(_('switch.lang')).toBe('Switch language')
})
it('should fallback to message id if id is not found', () => {
locale.set('en')
expect(_('batatinha.quente')).toBe('batatinha.quente')
@ -71,10 +79,9 @@ describe('formatting', () => {
expect(_('batatinha.quente', { default: 'Hot Potato' })).toBe('Hot Potato')
})
it('should translate to current locale', () => {
locale.set('pt')
expect(_('switch.lang')).toBe('Trocar idioma')
locale.set('en')
it('should fallback to generic locale XX if id not found in XX-YY', () => {
locale.set('en-GB')
expect(_('sneakers')).toBe('trainers')
expect(_('switch.lang')).toBe('Switch language')
})
@ -91,15 +98,15 @@ describe('formatting', () => {
})
it('should interpolate message with variables', () => {
expect(_('greeting.message', { values: { name: 'Chris' } })).toBe(
'Hello Chris, how are you?',
)
locale.set('en')
expect(_('greeting.message', { values: { name: 'Chris' } })).toBe('Hello Chris, how are you?')
})
it('should interpolate message with variables according to passed locale', () => {
expect(
_('greeting.message', { values: { name: 'Chris' }, locale: 'pt' }),
).toBe('Olá Chris, como vai?')
locale.set('en')
expect(_('greeting.message', { values: { name: 'Chris' }, locale: 'pt' })).toBe(
'Olá Chris, como vai?',
)
})
})
@ -126,9 +133,7 @@ describe('utilities', () => {
})
it('should get the locale based on the navigator language', () => {
expect(getClientLocale({ navigator: true })).toBe(
window.navigator.language,
)
expect(getClientLocale({ navigator: true })).toBe(window.navigator.language)
})
it('should get the fallback locale', () => {
@ -163,9 +168,7 @@ describe('utilities', () => {
locale.set('en')
expect(_.time(date)).toBe('11:45 PM')
expect(_.time(date, { format: 'medium' })).toBe('11:45:00 PM')
expect(_.time(date, { format: 'medium', locale: 'pt-BR' })).toBe(
'23:45:00',
)
expect(_.time(date, { format: 'medium', locale: 'pt-BR' })).toBe('23:45:00')
})
it('should format a date value', () => {
@ -224,12 +227,8 @@ describe('custom formats', () => {
expect(_.number(123123123, { format: 'usd' })).toContain('$123,123,123.00')
expect(_.number(123123123, { format: 'brl' })).toContain('R$123,123,123.00')
expect(_.date(new Date(2019, 0, 1), { format: 'customDate' })).toEqual(
'2019 AD',
)
expect(_.date(new Date(2019, 0, 1), { format: 'customDate' })).toEqual('2019 AD')
expect(
_.time(new Date(2019, 0, 1, 2, 0, 0), { format: 'customTime' }),
).toEqual('Jan, 02')
expect(_.time(new Date(2019, 0, 1, 2, 0, 0), { format: 'customTime' })).toEqual('Jan, 02')
})
})

3
test/fixtures/en-GB.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"sneakers": "trainers"
}

View File

@ -6,5 +6,6 @@
"message": "Hello {name}, how are you?"
},
"photos": "You have {n, plural, =0 {no photos.} =1 {one photo.} other {# photos.}}",
"cats": "I have {n, number} {n,plural,one{cat}other{cats}}"
"cats": "I have {n, number} {n,plural,one{cat}other{cats}}",
"sneakers": "sneakers"
}

View File

@ -6,5 +6,6 @@
"message": "Hola {name}, cómo estás?"
},
"photos": "Tienes {n, plural, =0 {0 fotos.} =1 {una foto.} other {# fotos.}}",
"cats": "Yo tengo {n, number} {n,plural,one{gato}other{gatos}}"
"cats": "Yo tengo {n, number} {n,plural,one{gato}other{gatos}}",
"sneakers": "zapatillas"
}

View File

@ -6,5 +6,6 @@
"message": "Olá {name}, como vai?"
},
"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}}",
"sneakers": "tênis"
}

View File

@ -4322,11 +4322,6 @@ require-main-filename@^2.0.0:
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
require-relative@^0.8.7:
version "0.8.7"
resolved "https://registry.yarnpkg.com/require-relative/-/require-relative-0.8.7.tgz#7999539fc9e047a37928fa196f8e1563dabd36de"
integrity sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4=
resolve-cwd@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a"
@ -4409,15 +4404,6 @@ rollup-plugin-commonjs@^10.1.0:
resolve "^1.11.0"
rollup-pluginutils "^2.8.1"
rollup-plugin-svelte@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/rollup-plugin-svelte/-/rollup-plugin-svelte-5.1.1.tgz#0094f94e7e6ff7579bcd9f7769b454751ba670e1"
integrity sha512-wP3CnKHjR4fZUgNm5Iey7eItnxwnH/nAw568WJ8dpMSchBxxZ/DmKSx8e6h8k/B6SwG1wfGvWehadFJHcuFFSw==
dependencies:
require-relative "^0.8.7"
rollup-pluginutils "^2.3.3"
sourcemap-codec "^1.4.4"
rollup-plugin-terser@^5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/rollup-plugin-terser/-/rollup-plugin-terser-5.1.2.tgz#3e41256205cb75f196fc70d4634227d1002c255c"
@ -4447,7 +4433,7 @@ rollup-pluginutils@2.8.1:
dependencies:
estree-walker "^0.6.1"
rollup-pluginutils@^2.3.3, rollup-pluginutils@^2.8.1:
rollup-pluginutils@^2.8.1:
version "2.8.2"
resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz#72f2af0748b592364dbd3389e600e5a9444a351e"
integrity sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==