refactor: 💡 split getClientLocale into multiple utilities

This commit is contained in:
Christian Kaisermann 2020-02-03 13:33:03 -03:00
parent a574444f60
commit 6b0ba2b902
10 changed files with 9720 additions and 183 deletions

View File

@ -64,7 +64,7 @@ After populating your [`$dictionary`](/docs/Dictionary.md) with [`addMessages()`
```js
// src/i18n.js
import { register, init, getClientLocale } from 'svelte-i18n'
import { register, init, getLocaleFromNavigator } from 'svelte-i18n'
register('en', () => import('./en.json'))
register('en-US', () => import('./en-US.json'))
@ -73,9 +73,7 @@ register('pt', () => import('./pt.json'))
init({
fallbackLocale: 'en',
initialLocale: getClientLocale({
navigator: true, // i.e 'en-US'
}),
initialLocale: getLocaleFromNavigator(),
})
// starts loading 'en-US' and 'en'
```

View File

@ -22,7 +22,7 @@ interface InitOptions {
**Example**:
```js
import { init, getClientLocale } from 'svelte-i18n'
import { init } from 'svelte-i18n'
init({
// fallback to en if current locale is not in the dictionary
@ -68,47 +68,100 @@ init({
<!-- 123.456,79 € -->
```
#### getClientLocale
#### getLocaleFromHostname
> `import { getClientLocale } from 'svelte-i18n'`
> `import { getLocaleFromHostname } from 'svelte-i18n'
`getClientLocale(options: GetClientLocaleOptions): void`
Optional utility method to help getting the initial locale of a user. Use it together with the [`init()`](#init) method.
```ts
interface GetClientLocaleOptions {
// the fallback locale to use if no message is found in the current one
fallback?: string
// when 'true', check the 'window.navigator.language' to set the current locale
navigator?: boolean
// key to look for a locale on 'window.location.search'
// 'example.com?locale=en-US'
search?: string
// key to look for a locale on 'window.location.hash'
// 'example.com#locale=en-US'
hash?: string
// pattern to look in the window.location.pathname.
// It returns the first capturing group.
pathname?: RegExp
// pattern to look in the window.location.hostname.
// It returns the first capturing group.
hostname?: RegExp
}
```
`getLocaleFromHostname(hostnamePattern: RegExp): string`
Utility method to help getting a initial locale based on a pattern of the current `hostname`.
**Example**:
```js
import { init, getClientLocale } from 'svelte-i18n'
import { init, getLocaleFromHostname } from 'svelte-i18n'
init({
// fallback to en if current locale is not in the dictionary
fallbackLocale: 'en',
initialLocale: getClientLocale({
// based on the user's browser
navigator: true,
}),
initialLocale: getLocaleFromHostname(/^(.*?)\./),
})
```
#### getLocaleFromPathname
> `import { getLocaleFromPathname } from 'svelte-i18n'
`getLocaleFromPathname(pathnamePattern: RegExp): string`
Utility method to help getting a initial locale based on a pattern of the current `pathname`.
**Example**:
```js
import { init, getLocaleFromPathname } from 'svelte-i18n'
init({
// fallback to en if current locale is not in the dictionary
fallbackLocale: 'en',
initialLocale: getLocaleFromPathname(/^\/(.*?)\//),
})
```
#### getLocaleFromNavigator
> `import { getLocaleFromNavigator } from 'svelte-i18n'
`getLocaleFromNavigator(): string`
Utility method to help getting a initial locale based on the browser's `navigator` settings.
**Example**:
```js
import { init, getLocaleFromNavigator } from 'svelte-i18n'
init({
// fallback to en if current locale is not in the dictionary
fallbackLocale: 'en',
initialLocale: getLocaleFromNavigator(),
})
```
#### getLocaleFromQueryString
> `import { getLocaleFromQueryString } from 'svelte-i18n'
`getLocaleFromQueryString(queryKey: string): string`
Utility method to help getting a initial locale based on a query string value.
```js
import { init, getLocaleFromQueryString } from 'svelte-i18n'
init({
// fallback to en if current locale is not in the dictionary
fallbackLocale: 'en',
initialLocale: getLocaleFromQueryString('lang'),
})
```
#### getLocaleFromHash
> `import { getLocaleFromHash } from 'svelte-i18n'
`getLocaleFromHash(): string`
Utility method to help getting a initial locale based on a hash `{key}={value}` string.
**Example**:
```js
import { init, getLocaleFromHash } from 'svelte-i18n'
init({
// fallback to en if current locale is not in the dictionary
fallbackLocale: 'en',
initialLocale: getLocaleFromHash('lang'),
})
```

View File

@ -71,7 +71,7 @@ capital($_('message.id'))
In `v2`, the [`init`](/docs/Methods.md#init) method could automatically set the initial locale based on some heuristcs from the client:
```js
import { init, getClientLocale } from 'svelte-i18n'
import { init } from 'svelte-i18n'
init({
initialLocale: {
@ -80,13 +80,19 @@ init({
})
```
However, many people didn't need that kind of extra weight in their apps. So in `v3`, to have the same behavior as `v2`, you have to import the [`getClientLocale`](/docs/Methods.md#getclientlocale) method.
However, many people didn't need that kind of extra weight in their apps. So in `v3` you have to explicitly import the utility desired:
- [`getLocaleFromHostname`](/docs/Methods.md#getlocalefromhostname)
- [`getLocaleFromPathname`](/docs/Methods.md#getlocalefrompathname)
- [`getLocaleFromNavigator`](/docs/Methods.md#getlocalefromnavigator)
- [`getLocaleFromQueryString`](/docs/Methods.md#getlocalefromquerystring)
- [`getLocaleFromHash`](/docs/Methods.md#getlocalefromhash)
```js
import { init, getClientLocale } from 'svelte-i18n'
import { init, getLocaleFromNavigator } from 'svelte-i18n'
init({
initialLocale: getClientLocale({ ... })
initialLocale: getLocaleFromNavigator(),
})
```

9555
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "svelte-i18n",
"version": "3.0.0",
"version": "3.0.1",
"main": "dist/runtime.cjs.js",
"module": "dist/runtime.esm.js",
"types": "types/runtime/index.d.ts",

View File

@ -1,59 +0,0 @@
import { GetClientLocaleOptions } from '../types'
const getFromQueryString = (queryString: string, key: string) => {
const keyVal = queryString.split('&').find(i => i.indexOf(`${key}=`) === 0)
if (keyVal) {
return keyVal.split('=').pop()
}
return null
}
const getFirstMatch = (base: string, pattern: RegExp) => {
const match = pattern.exec(base)
// istanbul ignore if
if (!match) return null
// istanbul ignore else
return match[1] || null
}
export const getClientLocale = ({
navigator,
hash,
search,
pathname,
hostname,
}: GetClientLocaleOptions) => {
let locale
// istanbul ignore next
if (typeof window === 'undefined') return null
if (hostname) {
locale = getFirstMatch(window.location.hostname, hostname)
if (locale) return locale
}
if (pathname) {
locale = getFirstMatch(window.location.pathname, pathname)
if (locale) return locale
}
if (navigator) {
// istanbul ignore else
locale = window.navigator.language || window.navigator.languages[0]
if (locale) return locale
}
if (search) {
locale = getFromQueryString(window.location.search.substr(1), search)
if (locale) return locale
}
if (hash) {
locale = getFromQueryString(window.location.hash.substr(1), hash)
if (locale) return locale
}
return null
}

View File

@ -0,0 +1,46 @@
const getFromQueryString = (queryString: string, key: string) => {
const keyVal = queryString.split('&').find(i => i.indexOf(`${key}=`) === 0)
if (keyVal) {
return keyVal.split('=').pop()
}
return null
}
const getFirstMatch = (base: string, pattern: RegExp) => {
const match = pattern.exec(base)
// istanbul ignore if
if (!match) return null
// istanbul ignore else
return match[1] || null
}
export const getLocaleFromHostname = (hostname: RegExp) => {
// istanbul ignore next
if (typeof window === 'undefined') return null
return getFirstMatch(window.location.hostname, hostname)
}
export const getLocaleFromPathname = (pathname: RegExp) => {
// istanbul ignore next
if (typeof window === 'undefined') return null
return getFirstMatch(window.location.pathname, pathname)
}
export const getLocaleFromNavigator = () => {
// istanbul ignore next
if (typeof window === 'undefined') return null
return window.navigator.language || window.navigator.languages[0]
}
export const getLocaleFromQueryString = (search: string) => {
// istanbul ignore next
if (typeof window === 'undefined') return null
return getFromQueryString(window.location.search.substr(1), search)
}
export const getLocaleFromHash = (hash: string) => {
// istanbul ignore next
if (typeof window === 'undefined') return null
return getFromQueryString(window.location.hash.substr(1), hash)
}

View File

@ -13,7 +13,13 @@ export function waitLocale(locale?: string) {
}
export { init } from './configs'
export { getClientLocale } from './includes/getClientLocale'
export {
getLocaleFromHostname,
getLocaleFromPathname,
getLocaleFromNavigator,
getLocaleFromQueryString,
getLocaleFromHash,
} from './includes/localeGetters'
export { $locale as locale } from './stores/locale'

View File

@ -47,17 +47,9 @@ export interface MessagesLoader {
(): Promise<any>
}
export interface GetClientLocaleOptions {
navigator?: boolean
hash?: string
search?: string
pathname?: RegExp
hostname?: RegExp
}
export interface ConfigureOptions {
fallbackLocale: string
initialLocale?: string | GetClientLocaleOptions
initialLocale?: string
formats?: Partial<Formats>
loadingDelay?: number
}

View File

@ -1,4 +1,10 @@
import { getClientLocale } from '../../../src/runtime/includes/getClientLocale'
import {
getLocaleFromQueryString,
getLocaleFromHash,
getLocaleFromNavigator,
getLocaleFromPathname,
getLocaleFromHostname,
} from '../../../src/runtime/includes/localeGetters'
import { flatObj } from '../../../src/runtime/includes/flatObj'
describe('getting client locale', () => {
@ -14,96 +20,30 @@ describe('getting client locale', () => {
test('gets the locale based on the passed hash parameter', () => {
window.location.hash = '#locale=en-US&lang=pt-BR'
expect(
getClientLocale({
hash: 'lang',
})
).toBe('pt-BR')
expect(getLocaleFromHash('lang')).toBe('pt-BR')
})
test('gets the locale based on the passed search parameter', () => {
window.location.search = '?locale=en-US&lang=pt-BR'
expect(
getClientLocale({
search: 'lang',
})
).toBe('pt-BR')
expect(getLocaleFromQueryString('lang')).toBe('pt-BR')
})
test('gets the locale based on the navigator language', () => {
expect(
getClientLocale({
navigator: true,
})
).toBe(window.navigator.language)
expect(getLocaleFromNavigator()).toBe(window.navigator.language)
})
test('gets the locale based on the pathname', () => {
window.location.pathname = '/en-US/foo/'
expect(
getClientLocale({
pathname: /^\/(.*?)\//,
})
).toBe('en-US')
expect(getLocaleFromPathname(/^\/(.*?)\//)).toBe('en-US')
})
test('gets the locale base on the hostname', () => {
window.location.hostname = 'pt.example.com'
expect(
getClientLocale({
hostname: /^(.*?)\./,
})
).toBe('pt')
})
test('hostname precedes pathname', () => {
window.location.pathname = '/en-US/foo/'
window.location.hostname = 'pt.example.com'
expect(
getClientLocale({
hostname: /^(.*?)\./,
pathname: /^\/(.*?)\//,
})
).toBe('pt')
})
test('pathname precedes navigator', () => {
window.location.pathname = '/it-IT/foo/'
expect(
getClientLocale({
pathname: /^\/(.*?)\//,
navigator: true,
})
).toBe('it-IT')
})
test('navigator precedes search', () => {
window.location.search = '?lang=pt-BR'
expect(
getClientLocale({
navigator: true,
search: 'lang',
})
).toBe('en-US')
})
test('search precedes hash', () => {
window.location.hash = '#lang=pt-BR'
window.location.search = '?lang=it-IT'
expect(
getClientLocale({
hash: 'lang',
search: 'lang',
})
).toBe('it-IT')
expect(getLocaleFromHostname(/^(.*?)\./)).toBe('pt')
})
test('returns null if no locale was found', () => {
expect(
getClientLocale({
search: 'lang',
})
).toBe(null)
expect(getLocaleFromQueryString('lang')).toBe(null)
})
})