From 36bf7f503807c8629e950dad0db2c90b77128dcf Mon Sep 17 00:00:00 2001 From: Christian Kaisermann Date: Mon, 25 Nov 2019 23:04:10 -0300 Subject: [PATCH] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20shrink=20configs=20A?= =?UTF-8?q?PI=20contact=20points?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- example/src/i18n.js | 17 ++++- example/src/routes/_layout.svelte | 8 +-- src/client/configs.ts | 67 ++++++++++++++++++ .../includes/{formats.ts => formatters.ts} | 27 ++------ src/client/includes/loaderQueue.ts | 11 +-- src/client/includes/utils.ts | 4 +- src/client/index.ts | 14 +--- src/client/stores/format.ts | 6 +- src/client/stores/locale.ts | 27 ++------ src/client/types/index.ts | 10 ++- test/client/locale.test.ts | 68 +++++++++++-------- 11 files changed, 156 insertions(+), 103 deletions(-) create mode 100644 src/client/configs.ts rename src/client/includes/{formats.ts => formatters.ts} (66%) diff --git a/example/src/i18n.js b/example/src/i18n.js index 349adb7..700dc2d 100644 --- a/example/src/i18n.js +++ b/example/src/i18n.js @@ -1,7 +1,18 @@ -import { register, setFallbackLocale } from 'svelte-i18n' +import { register, configure } from 'svelte-i18n' + +configure({ + fallbackLocale: 'en', + initialLocale: { + navigator: true, + }, + formats: { + number: { + BRL: { style: 'currency', currency: 'BRL' }, + }, + }, + loadingDelay: 200, +}) register('en', () => import('../messages/en.json')) register('pt-BR', () => import('../messages/pt-BR.json')) register('es-ES', () => import('../messages/es-ES.json')) - -setFallbackLocale('en') diff --git a/example/src/routes/_layout.svelte b/example/src/routes/_layout.svelte index 7bf9195..bc62040 100644 --- a/example/src/routes/_layout.svelte +++ b/example/src/routes/_layout.svelte @@ -1,12 +1,8 @@ diff --git a/src/client/configs.ts b/src/client/configs.ts new file mode 100644 index 0000000..83e575a --- /dev/null +++ b/src/client/configs.ts @@ -0,0 +1,67 @@ +import { getClientLocale } from './includes/utils' +import { ConfigureOptions } from './types' +import { $locale } from './stores/locale' + +let fallbackLocale: string = null +let loadingDelay = 200 +const formats: any = { + number: { + scientific: { notation: 'scientific' }, + engineering: { notation: 'engineering' }, + compactLong: { notation: 'compact', compactDisplay: 'long' }, + compactShort: { notation: 'compact', compactDisplay: 'short' }, + }, + date: { + short: { month: 'numeric', day: 'numeric', year: '2-digit' }, + medium: { month: 'short', day: 'numeric', year: 'numeric' }, + long: { month: 'long', day: 'numeric', year: 'numeric' }, + full: { weekday: 'long', month: 'long', day: 'numeric', year: 'numeric' }, + }, + time: { + short: { hour: 'numeric', minute: 'numeric' }, + medium: { hour: 'numeric', minute: 'numeric', second: 'numeric' }, + long: { + hour: 'numeric', + minute: 'numeric', + second: 'numeric', + timeZoneName: 'short', + }, + full: { + hour: 'numeric', + minute: 'numeric', + second: 'numeric', + timeZoneName: 'short', + }, + }, +} + +export function getFallbackLocale() { + return fallbackLocale +} + +export function getLoadingDelay() { + return loadingDelay +} + +export function getFormats() { + return formats +} + +export function configure(opts: ConfigureOptions) { + fallbackLocale = opts.fallbackLocale + + if (opts.initialLocale) { + $locale.set(getClientLocale(opts.initialLocale) || fallbackLocale) + } else { + $locale.set(fallbackLocale) + } + + if (opts.formats) { + if ('number' in opts.formats) + Object.assign(formats.number, opts.formats.number) + if ('date' in opts.formats) Object.assign(formats.date, opts.formats.date) + if ('time' in opts.formats) Object.assign(formats.time, opts.formats.time) + } + + if (loadingDelay != null) loadingDelay = loadingDelay +} diff --git a/src/client/includes/formats.ts b/src/client/includes/formatters.ts similarity index 66% rename from src/client/includes/formats.ts rename to src/client/includes/formatters.ts index 93029ba..0a8a40d 100644 --- a/src/client/includes/formats.ts +++ b/src/client/includes/formatters.ts @@ -1,32 +1,17 @@ -import IntlMessageFormat, { Formats } from 'intl-messageformat' +import IntlMessageFormat from 'intl-messageformat' import memoize from 'fast-memoize' import { MemoizedIntlFormatter } from '../types' import { getCurrentLocale } from '../stores/locale' - -export const customFormats: any = { - number: { - scientific: { notation: 'scientific' }, - engineering: { notation: 'engineering' }, - compactLong: { notation: 'compact', compactDisplay: 'long' }, - compactShort: { notation: 'compact', compactDisplay: 'short' }, - }, - date: {}, - time: {}, -} - -export function addCustomFormats(formats: Partial) { - if ('number' in formats) Object.assign(customFormats.number, formats.number) - if ('date' in formats) Object.assign(customFormats.date, formats.date) - if ('time' in formats) Object.assign(customFormats.time, formats.time) -} +import { getFormats } from '../configs' const getIntlFormatterOptions = ( type: 'time' | 'number' | 'date', name: string ): any => { - if (type in customFormats && name in customFormats[type]) { - return customFormats[type][name] + const formats = getFormats() + if (type in formats && name in formats[type]) { + return formats[type][name] } if ( @@ -77,5 +62,5 @@ export const getTimeFormatter: MemoizedIntlFormatter< export const getMessageFormatter = memoize( (message: string, locale: string) => - new IntlMessageFormat(message, locale, customFormats) + new IntlMessageFormat(message, locale, getFormats()) ) diff --git a/src/client/includes/loaderQueue.ts b/src/client/includes/loaderQueue.ts index 37e0d97..710aa8a 100644 --- a/src/client/includes/loaderQueue.ts +++ b/src/client/includes/loaderQueue.ts @@ -4,8 +4,9 @@ import { $dictionary, addMessages, } from '../stores/dictionary' -import { getCurrentLocale, getFallbacksOf } from '../stores/locale' +import { getCurrentLocale, getRelatedLocalesOf } from '../stores/locale' import { $isLoading } from '../stores/loading' +import { getLoadingDelay } from '../configs' type Queue = Set const loaderQueue: Record = {} @@ -23,7 +24,7 @@ function getLocaleQueue(locale: string) { } function getLocalesQueues(locale: string) { - return getFallbacksOf(locale) + return getRelatedLocalesOf(locale) .reverse() .map<[string, MessagesLoader[]]>(localeItem => { const queue = getLocaleQueue(localeItem) @@ -33,7 +34,7 @@ function getLocalesQueues(locale: string) { } export function hasLocaleQueue(locale: string) { - return getFallbacksOf(locale) + return getRelatedLocalesOf(locale) .reverse() .some(getLocaleQueue) } @@ -45,14 +46,14 @@ export function addLoaderToQueue(locale: string, loader: MessagesLoader) { const activeLocaleFlushes: { [key: string]: Promise } = {} export async function flushQueue(locale: string = getCurrentLocale()) { if (!hasLocaleQueue(locale)) return - if (activeLocaleFlushes[locale]) return activeLocaleFlushes[locale] + if (locale in activeLocaleFlushes) return activeLocaleFlushes[locale] // get queue of XX-YY and XX locales const queues = getLocalesQueues(locale) if (queues.length === 0) return removeLocaleFromQueue(locale) - const loadingDelay = setTimeout(() => $isLoading.set(true), 200) + const loadingDelay = setTimeout(() => $isLoading.set(true), getLoadingDelay()) // TODO what happens if some loader fails activeLocaleFlushes[locale] = Promise.all( diff --git a/src/client/includes/utils.ts b/src/client/includes/utils.ts index e3335d1..0256125 100644 --- a/src/client/includes/utils.ts +++ b/src/client/includes/utils.ts @@ -39,8 +39,6 @@ export const getClientLocale = ({ search, pathname, hostname, - default: defaultLocale, - fallback = defaultLocale, }: GetClientLocaleOptions) => { let locale @@ -78,5 +76,5 @@ export const getClientLocale = ({ if (locale) return locale } - return fallback + return null } diff --git a/src/client/index.ts b/src/client/index.ts index ea022dc..2f94c4f 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -1,5 +1,3 @@ -import merge from 'deepmerge' - import { MessageObject } from './types' // defineMessages allow us to define and extract dynamic message ids @@ -7,11 +5,8 @@ export function defineMessages(i: Record) { return i } -export { - $locale as locale, - setInitialLocale, - setFallbackLocale, -} from './stores/locale' +export { configure } from './configs' +export { $locale as locale } from './stores/locale' export { $dictionary as dictionary, $locales as locales, @@ -21,12 +16,7 @@ export { $isLoading as isLoading } from './stores/loading' export { $format as format, $format as _, $format as t } from './stores/format' // utilities -export { merge } -export { customFormats, addCustomFormats } from './includes/formats' export { flushQueue as waitLocale, registerLocaleLoader as register, } from './includes/loaderQueue' - -// @deprecated -export { getClientLocale } from './includes/utils' diff --git a/src/client/stores/format.ts b/src/client/stores/format.ts index ff18ea6..f85ee70 100644 --- a/src/client/stores/format.ts +++ b/src/client/stores/format.ts @@ -9,10 +9,10 @@ import { getTimeFormatter, getDateFormatter, getNumberFormatter, -} from '../includes/formats' +} from '../includes/formatters' import { $dictionary } from './dictionary' -import { getCurrentLocale, getFallbacksOf, $locale } from './locale' +import { getCurrentLocale, getRelatedLocalesOf, $locale } from './locale' const formatMessage: Formatter = (id, options = {}) => { if (typeof id === 'object') { @@ -32,7 +32,7 @@ const formatMessage: Formatter = (id, options = {}) => { if (!message) { console.warn( - `[svelte-i18n] The message "${id}" was not found in "${getFallbacksOf( + `[svelte-i18n] The message "${id}" was not found in "${getRelatedLocalesOf( locale ).join('", "')}". ${ hasLocaleQueue(getCurrentLocale()) diff --git a/src/client/stores/locale.ts b/src/client/stores/locale.ts index ccb65ef..b4822ee 100644 --- a/src/client/stores/locale.ts +++ b/src/client/stores/locale.ts @@ -5,19 +5,11 @@ import { getClientLocale } from '../includes/utils' import { GetClientLocaleOptions } from '../types' import { getClosestAvailableLocale } from './dictionary' +import { setFallbackLocale, getFallbackLocale } from '../configs' -let fallbackLocale: string = null let current: string const $locale = writable(null) -export function getFallbackLocale() { - return fallbackLocale -} - -export function setFallbackLocale(locale: string) { - fallbackLocale = locale -} - export function isFallbackLocaleOf(localeA: string, localeB: string) { return localeB.indexOf(localeA) === 0 && localeA !== localeB } @@ -33,19 +25,19 @@ export function isRelatedLocale(localeA: string, localeB: string) { export function getFallbackOf(locale: string) { const index = locale.lastIndexOf('-') if (index > 0) return locale.slice(0, index) - if (fallbackLocale && !isRelatedLocale(locale, fallbackLocale)) { - return fallbackLocale + if (getFallbackLocale() && !isRelatedLocale(locale, getFallbackLocale())) { + return getFallbackLocale() } return null } -export function getFallbacksOf(locale: string): string[] { +export function getRelatedLocalesOf(locale: string): string[] { const locales = locale .split('-') .map((_, i, arr) => arr.slice(0, i + 1).join('-')) - if (fallbackLocale && !isRelatedLocale(locale, fallbackLocale)) { - return locales.concat(getFallbacksOf(fallbackLocale)) + if (getFallbackLocale() && !isRelatedLocale(locale, getFallbackLocale())) { + return locales.concat(getRelatedLocalesOf(getFallbackLocale())) } return locales } @@ -54,13 +46,6 @@ export function getCurrentLocale() { return current } -export function setInitialLocale(options: GetClientLocaleOptions) { - if (typeof options.fallback === 'string') { - setFallbackLocale(options.fallback) - } - return $locale.set(getClientLocale(options)) -} - $locale.subscribe((newLocale: string) => { current = newLocale diff --git a/src/client/types/index.ts b/src/client/types/index.ts index e3bdcf4..edf8638 100644 --- a/src/client/types/index.ts +++ b/src/client/types/index.ts @@ -1,3 +1,5 @@ +import { Formats } from 'intl-messageformat' + export interface Dictionary { [key: string]: string | string[] | Dictionary | Dictionary[] } @@ -50,8 +52,14 @@ export interface GetClientLocaleOptions { navigator?: boolean hash?: string | RegExp search?: string | RegExp - fallback?: string default?: string pathname?: RegExp hostname?: RegExp } + +export interface ConfigureOptions { + fallbackLocale: string + initialLocale?: GetClientLocaleOptions + formats?: Partial + loadingDelay?: number +} diff --git a/test/client/locale.test.ts b/test/client/locale.test.ts index d5e5d89..f85eff0 100644 --- a/test/client/locale.test.ts +++ b/test/client/locale.test.ts @@ -1,23 +1,22 @@ +import { get } from 'svelte/store' + import { - getFallbackLocale, - setFallbackLocale, isFallbackLocaleOf, getFallbackOf, - getFallbacksOf, - setInitialLocale, + getRelatedLocalesOf, getCurrentLocale, $locale, isRelatedLocale, } from '../../src/client/stores/locale' -import { get } from 'svelte/store' +import { getFallbackLocale, configure } from '../../src/client/configs' beforeEach(() => { - setFallbackLocale(undefined) + configure({ fallbackLocale: undefined }) $locale.set(undefined) }) test('sets and gets the fallback locale', () => { - setFallbackLocale('en') + configure({ fallbackLocale: 'en' }) expect(getFallbackLocale()).toBe('en') }) @@ -42,31 +41,35 @@ test('gets the next fallback locale of a locale', () => { }) test('gets the global fallback locale if set', () => { - setFallbackLocale('en') + configure({ fallbackLocale: 'en' }) expect(getFallbackOf('it')).toBe('en') }) test('should not get the global fallback as the fallback of itself', () => { - setFallbackLocale('en') + configure({ fallbackLocale: 'en' }) expect(getFallbackOf('en')).toBe(null) }) test('if global fallback locale has a fallback, it should return it', () => { - setFallbackLocale('en-US') + configure({ fallbackLocale: 'en-US' }) expect(getFallbackOf('en-US')).toBe('en') }) test('gets all fallback locales of a locale', () => { - expect(getFallbacksOf('en-US')).toEqual(['en', 'en-US']) - expect(getFallbacksOf('en-US')).toEqual(['en', 'en-US']) - expect(getFallbacksOf('az-Cyrl-AZ')).toEqual(['az', 'az-Cyrl', 'az-Cyrl-AZ']) + expect(getRelatedLocalesOf('en-US')).toEqual(['en', 'en-US']) + expect(getRelatedLocalesOf('en-US')).toEqual(['en', 'en-US']) + expect(getRelatedLocalesOf('az-Cyrl-AZ')).toEqual([ + 'az', + 'az-Cyrl', + 'az-Cyrl-AZ', + ]) }) test('gets all fallback locales of a locale including the global fallback locale', () => { - setFallbackLocale('pt') - expect(getFallbacksOf('en-US')).toEqual(['en', 'en-US', 'pt']) - expect(getFallbacksOf('en-US')).toEqual(['en', 'en-US', 'pt']) - expect(getFallbacksOf('az-Cyrl-AZ')).toEqual([ + configure({ fallbackLocale: 'pt' }) + expect(getRelatedLocalesOf('en-US')).toEqual(['en', 'en-US', 'pt']) + expect(getRelatedLocalesOf('en-US')).toEqual(['en', 'en-US', 'pt']) + expect(getRelatedLocalesOf('az-Cyrl-AZ')).toEqual([ 'az', 'az-Cyrl', 'az-Cyrl-AZ', @@ -74,10 +77,10 @@ test('gets all fallback locales of a locale including the global fallback locale ]) }) test('gets all fallback locales of a locale including the global fallback locale and its fallbacks', () => { - setFallbackLocale('pt-BR') - expect(getFallbacksOf('en-US')).toEqual(['en', 'en-US', 'pt', 'pt-BR']) - expect(getFallbacksOf('en-US')).toEqual(['en', 'en-US', 'pt', 'pt-BR']) - expect(getFallbacksOf('az-Cyrl-AZ')).toEqual([ + configure({ fallbackLocale: 'pt-BR' }) + expect(getRelatedLocalesOf('en-US')).toEqual(['en', 'en-US', 'pt', 'pt-BR']) + expect(getRelatedLocalesOf('en-US')).toEqual(['en', 'en-US', 'pt', 'pt-BR']) + expect(getRelatedLocalesOf('az-Cyrl-AZ')).toEqual([ 'az', 'az-Cyrl', 'az-Cyrl-AZ', @@ -87,9 +90,9 @@ test('gets all fallback locales of a locale including the global fallback locale }) test("don't list fallback locale twice", () => { - setFallbackLocale('pt-BR') - expect(getFallbacksOf('pt-BR')).toEqual(['pt', 'pt-BR']) - expect(getFallbacksOf('pt')).toEqual(['pt']) + configure({ fallbackLocale: 'pt-BR' }) + expect(getRelatedLocalesOf('pt-BR')).toEqual(['pt', 'pt-BR']) + expect(getRelatedLocalesOf('pt')).toEqual(['pt']) }) test('gets the current locale', () => { @@ -98,10 +101,19 @@ test('gets the current locale', () => { expect(getCurrentLocale()).toBe('es-ES') }) -test('sets the global fallback when defining initial locale', () => { - setInitialLocale({ - fallback: 'pt', - }) +test('if no initial locale is set, set the locale to the fallback', () => { + configure({ fallbackLocale: 'pt' }) expect(get($locale)).toBe('pt') expect(getFallbackLocale()).toBe('pt') }) + +test('if no initial locale was found, set to the fallback locale', () => { + configure({ + fallbackLocale: 'en', + initialLocale: { + hash: 'lang', + }, + }) + expect(get($locale)).toBe('en') + expect(getFallbackLocale()).toBe('en') +})