mirror of
https://github.com/cupcakearmy/svelte-i18n.git
synced 2024-09-28 15:14:45 +02:00
refactor: 💡 replace memoization with manual object cache
This commit is contained in:
parent
0a0e4b3bab
commit
31faccec3b
@ -76,9 +76,9 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"commander": "^4.0.1",
|
"commander": "^4.0.1",
|
||||||
|
"deepmerge": "^4.2.2",
|
||||||
"estree-walker": "^0.9.0",
|
"estree-walker": "^0.9.0",
|
||||||
"intl-messageformat": "^7.5.2",
|
"intl-messageformat": "^7.5.2",
|
||||||
"micro-memoize": "^4.0.8",
|
|
||||||
"object-resolve-path": "^1.1.1",
|
"object-resolve-path": "^1.1.1",
|
||||||
"tiny-glob": "^0.2.6"
|
"tiny-glob": "^0.2.6"
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { writable, derived } from 'svelte/store'
|
import { writable, derived } from 'svelte/store'
|
||||||
import resolvePath from 'object-resolve-path'
|
import resolvePath from 'object-resolve-path'
|
||||||
import memoize from 'micro-memoize'
|
import merge from 'deepmerge'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
capital,
|
capital,
|
||||||
@ -21,24 +21,49 @@ import {
|
|||||||
|
|
||||||
let currentLocale: string
|
let currentLocale: string
|
||||||
let currentDictionary: Record<string, Record<string, any>>
|
let currentDictionary: Record<string, Record<string, any>>
|
||||||
|
const dictQueue: Record<string, any[]> = {}
|
||||||
|
|
||||||
const hasLocale = (locale: string) => locale in currentDictionary
|
const hasLocale = (locale: string) => locale in currentDictionary
|
||||||
|
|
||||||
|
async function registerLocaleLoader(locale: string, loader: any) {
|
||||||
|
if (!(locale in currentDictionary)) {
|
||||||
|
$dictionary.update(d => {
|
||||||
|
d[locale] = {}
|
||||||
|
return d
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (!(locale in dictQueue)) dictQueue[locale] = []
|
||||||
|
dictQueue[locale].push(loader)
|
||||||
|
}
|
||||||
function getAvailableLocale(locale: string): string | null {
|
function getAvailableLocale(locale: string): string | null {
|
||||||
if (locale in currentDictionary || locale == null) return locale
|
if (locale in currentDictionary || locale in dictQueue || locale == null) return locale
|
||||||
return getAvailableLocale(getGenericLocaleFrom(locale))
|
return getAvailableLocale(getGenericLocaleFrom(locale))
|
||||||
}
|
}
|
||||||
|
|
||||||
const lookupMessage = memoize((path: string, locale: string): string => {
|
const lookupCache: Record<string, Record<string, string>> = {}
|
||||||
|
const addToCache = (path: string, locale: string, message: string) => {
|
||||||
|
if (!(locale in lookupCache)) lookupCache[locale] = {}
|
||||||
|
if (!(path in lookupCache[locale])) lookupCache[locale][path] = message
|
||||||
|
return message
|
||||||
|
}
|
||||||
|
const invalidateLookupCache = (locale: string) => {
|
||||||
|
delete lookupCache[locale]
|
||||||
|
}
|
||||||
|
const lookupMessage = (path: string, locale: string): string => {
|
||||||
if (locale == null) return null
|
if (locale == null) return null
|
||||||
|
if (locale in lookupCache && path in lookupCache[locale]) {
|
||||||
|
return lookupCache[locale][path]
|
||||||
|
}
|
||||||
if (hasLocale(locale)) {
|
if (hasLocale(locale)) {
|
||||||
if (path in currentDictionary[locale]) return currentDictionary[locale][path]
|
if (path in currentDictionary[locale]) {
|
||||||
|
return addToCache(path, locale, currentDictionary[locale][path])
|
||||||
|
}
|
||||||
const message = resolvePath(currentDictionary[locale], path)
|
const message = resolvePath(currentDictionary[locale], path)
|
||||||
if (message) return message
|
if (message) return addToCache(path, locale, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
return lookupMessage(path, getGenericLocaleFrom(locale))
|
return lookupMessage(path, getGenericLocaleFrom(locale))
|
||||||
})
|
}
|
||||||
|
|
||||||
const formatMessage: Formatter = (id, options = {}) => {
|
const formatMessage: Formatter = (id, options = {}) => {
|
||||||
if (typeof id === 'object') {
|
if (typeof id === 'object') {
|
||||||
@ -47,13 +72,20 @@ const formatMessage: Formatter = (id, options = {}) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { values, locale = currentLocale, default: defaultValue } = options
|
const { values, locale = currentLocale, default: defaultValue } = options
|
||||||
|
|
||||||
|
if (locale == null) {
|
||||||
|
throw new Error(
|
||||||
|
'[svelte-i18n] Cannot format a message without first setting the initial locale.'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const message = lookupMessage(id, locale)
|
const message = lookupMessage(id, locale)
|
||||||
|
|
||||||
if (!message) {
|
if (!message) {
|
||||||
console.warn(
|
console.warn(
|
||||||
`[svelte-i18n] The message "${id}" was not found in "${getGenericLocalesFrom(locale).join(
|
`[svelte-i18n] The message "${id}" was not found in "${getGenericLocalesFrom(locale).join(
|
||||||
'", "',
|
'", "'
|
||||||
)}".`,
|
)}".`
|
||||||
)
|
)
|
||||||
return defaultValue || id
|
return defaultValue || id
|
||||||
}
|
}
|
||||||
@ -73,36 +105,27 @@ formatMessage.lower = (id, options) => lower(formatMessage(id, options))
|
|||||||
const $dictionary = writable<Record<string, Record<string, any>>>({})
|
const $dictionary = writable<Record<string, Record<string, any>>>({})
|
||||||
$dictionary.subscribe(newDictionary => (currentDictionary = newDictionary))
|
$dictionary.subscribe(newDictionary => (currentDictionary = newDictionary))
|
||||||
|
|
||||||
const loadLocale = (localeToLoad: string) => {
|
function loadLocale(localeToLoad: string) {
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
getGenericLocalesFrom(localeToLoad)
|
getGenericLocalesFrom(localeToLoad).map(localeItem =>
|
||||||
.map(localeItem => {
|
flushLocaleQueue(localeItem)
|
||||||
const loader = currentDictionary[localeItem]
|
.then(() => [localeItem, { err: undefined }])
|
||||||
if (loader == null && localeItem !== localeToLoad) {
|
.catch(e => [localeItem, { err: e }])
|
||||||
console.warn(
|
|
||||||
`[svelte-i18n] No dictionary or loader were found for the locale "${localeItem}". It's the fallback locale of "${localeToLoad}."`,
|
|
||||||
)
|
)
|
||||||
return
|
|
||||||
}
|
|
||||||
if (typeof loader !== 'function') return
|
|
||||||
return loader().then((dict: any) => [localeItem, dict.default || dict])
|
|
||||||
})
|
|
||||||
.filter(Boolean),
|
|
||||||
)
|
)
|
||||||
.then(updates => {
|
}
|
||||||
if (updates.length > 0) {
|
|
||||||
// update dictionary only once
|
async function flushLocaleQueue(locale: string = currentLocale) {
|
||||||
|
if (!(locale in dictQueue)) return
|
||||||
|
return Promise.all(dictQueue[locale].map((loader: any) => loader())).then(partials => {
|
||||||
|
dictQueue[locale] = []
|
||||||
|
|
||||||
|
partials = partials.map(partial => partial.default || partial)
|
||||||
|
invalidateLookupCache(locale)
|
||||||
$dictionary.update(d => {
|
$dictionary.update(d => {
|
||||||
updates.forEach(([localeItem, localeDict]) => {
|
d[locale] = merge.all<any>([d[locale] || {}].concat(partials))
|
||||||
d[localeItem] = localeDict
|
|
||||||
})
|
|
||||||
return d
|
return d
|
||||||
})
|
})
|
||||||
}
|
|
||||||
return updates
|
|
||||||
})
|
|
||||||
.catch((e: Error) => {
|
|
||||||
throw e
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,10 +134,8 @@ const localeSet = $locale.set
|
|||||||
$locale.set = (newLocale: string): void | Promise<void> => {
|
$locale.set = (newLocale: string): void | Promise<void> => {
|
||||||
const locale = getAvailableLocale(newLocale)
|
const locale = getAvailableLocale(newLocale)
|
||||||
if (locale) {
|
if (locale) {
|
||||||
if (typeof currentDictionary[locale] === 'function') {
|
if (locale in dictQueue && dictQueue[locale].length > 0) {
|
||||||
// load all locales related to the passed locale
|
return flushLocaleQueue(locale).then(() => localeSet(newLocale))
|
||||||
// i.e en-GB loads en, but en doesn't load en-GB
|
|
||||||
return loadLocale(locale).then(() => localeSet(newLocale))
|
|
||||||
}
|
}
|
||||||
return localeSet(newLocale)
|
return localeSet(newLocale)
|
||||||
}
|
}
|
||||||
@ -136,8 +157,10 @@ export {
|
|||||||
$dictionary as dictionary,
|
$dictionary as dictionary,
|
||||||
$format as _,
|
$format as _,
|
||||||
$format as format,
|
$format as format,
|
||||||
$locales,
|
$locales as locales,
|
||||||
getClientLocale,
|
getClientLocale,
|
||||||
defineMessages,
|
defineMessages,
|
||||||
loadLocale as preloadLocale,
|
loadLocale as preloadLocale,
|
||||||
|
registerLocaleLoader,
|
||||||
|
flushLocaleQueue,
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user