mirror of
https://github.com/gotson/komga.git
synced 2026-05-07 12:01:40 +02:00
use navigator preferred languages to select locale
This commit is contained in:
parent
d95acc203f
commit
648f71d908
6 changed files with 83 additions and 50 deletions
1
next-ui/package-lock.json
generated
1
next-ui/package-lock.json
generated
|
|
@ -8,6 +8,7 @@
|
|||
"name": "next-ui",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@formatjs/intl-localematcher": "^0.6.1",
|
||||
"@pinia/colada": "^0.17.1",
|
||||
"@pinia/colada-plugin-auto-refetch": "^0.2.0",
|
||||
"@vueuse/core": "^13.5.0",
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
"chromatic": "npm run chromatic"
|
||||
},
|
||||
"dependencies": {
|
||||
"@formatjs/intl-localematcher": "^0.6.1",
|
||||
"@pinia/colada": "^0.17.1",
|
||||
"@pinia/colada-plugin-auto-refetch": "^0.2.0",
|
||||
"@vueuse/core": "^13.5.0",
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { createIntl } from 'vue-intl'
|
||||
import { currentLocale, defaultLocale, loadLocale } from '@/utils/i18n/locale-helper'
|
||||
import { currentLocale, fallbackLocale, loadLocale } from '@/utils/i18n/locale-helper'
|
||||
|
||||
const messages = loadLocale(currentLocale)
|
||||
|
||||
export const vueIntl = createIntl({
|
||||
locale: currentLocale,
|
||||
defaultLocale: defaultLocale,
|
||||
defaultLocale: fallbackLocale,
|
||||
messages,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import { md3 } from 'vuetify/blueprints'
|
|||
import { VIconBtn } from 'vuetify/labs/components'
|
||||
import { createRulesPlugin } from 'vuetify/labs/rules'
|
||||
|
||||
import { availableLocales, currentLocale, defaultLocale } from '@/utils/i18n/locale-helper'
|
||||
import { availableLocales, currentLocale, fallbackLocale } from '@/utils/i18n/locale-helper'
|
||||
|
||||
// load vuetify locales only for the available locales in i18n
|
||||
async function loadVuetifyLocale(locale: string) {
|
||||
|
|
@ -34,7 +34,7 @@ void (async () => {
|
|||
export const vuetify = createVuetify({
|
||||
locale: {
|
||||
locale: currentLocale,
|
||||
fallback: defaultLocale,
|
||||
fallback: fallbackLocale,
|
||||
messages,
|
||||
},
|
||||
icons: {
|
||||
|
|
|
|||
|
|
@ -1,42 +1,64 @@
|
|||
import { beforeEach, expect, test, vi } from 'vitest'
|
||||
import { loadLocale, defaultLocale, setLocale, getLocale, availableLocales } from './locale-helper'
|
||||
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'
|
||||
import { loadLocale, fallbackLocale, setLocale, getLocale, availableLocales } from './locale-helper'
|
||||
|
||||
beforeEach(() => {
|
||||
// mock the available locales, as locales are checked against what's available
|
||||
vi.mock('@/i18n?dir2json&ext=.json&1', () => {
|
||||
return {
|
||||
default: {
|
||||
en: {
|
||||
sample: 'sample',
|
||||
},
|
||||
fr: {
|
||||
sample: 'échantillon',
|
||||
},
|
||||
} as Record<string, Record<string, string>>,
|
||||
}
|
||||
describe('locale', () => {
|
||||
beforeEach(() => {
|
||||
// mock the available locales, as locales are checked against what's available
|
||||
vi.mock('@/i18n?dir2json&ext=.json&1', () => {
|
||||
return {
|
||||
default: {
|
||||
en: {
|
||||
sample: 'sample',
|
||||
},
|
||||
fr: {
|
||||
sample: 'échantillon',
|
||||
},
|
||||
} as Record<string, Record<string, string>>,
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
test('given available locales when getting available locales then they are returned', () => {
|
||||
expect(Object.keys(availableLocales)).toStrictEqual(['en', 'fr'])
|
||||
})
|
||||
|
||||
test('when trying to load unknown locale then fallback locale is loaded', () => {
|
||||
const localeFallback = loadLocale(fallbackLocale)
|
||||
const localeUnknown = loadLocale('unknown')
|
||||
|
||||
expect(localeUnknown).toBe(localeFallback)
|
||||
})
|
||||
|
||||
test('when setting locale then it is persisted', () => {
|
||||
const spy = vi.fn(() => {})
|
||||
vi.spyOn(window, 'location', 'get').mockReturnValue({
|
||||
reload: spy,
|
||||
} as unknown as Location)
|
||||
|
||||
setLocale('fr')
|
||||
const newLocale = getLocale()
|
||||
|
||||
expect(newLocale).toBe('fr')
|
||||
expect(spy).toHaveBeenCalledOnce()
|
||||
})
|
||||
|
||||
test('given browser preferred languages in available locales when getting locale then preferred language is used', () => {
|
||||
vi.spyOn(navigator, 'languages', 'get').mockReturnValue(['fr-XX', 'zh'])
|
||||
|
||||
const locale = getLocale()
|
||||
|
||||
expect(locale).toBe('fr')
|
||||
})
|
||||
|
||||
test('given browser preferred languages not in available locales when getting locale then fallback locale is used', () => {
|
||||
vi.spyOn(navigator, 'languages', 'get').mockReturnValue(['ja-JP', 'zh-CN'])
|
||||
|
||||
const locale = getLocale()
|
||||
|
||||
expect(locale).toBe(fallbackLocale)
|
||||
})
|
||||
})
|
||||
|
||||
test('given available locales when getting available locales then they are returned', () => {
|
||||
expect(Object.keys(availableLocales)).toStrictEqual(['en', 'fr'])
|
||||
})
|
||||
|
||||
test('when trying to load unknown locale then default locale is loaded', () => {
|
||||
const localeDefault = loadLocale(defaultLocale)
|
||||
const localeUnknown = loadLocale('unknown')
|
||||
|
||||
expect(localeUnknown).toBe(localeDefault)
|
||||
})
|
||||
|
||||
test('when setting locale then it is persisted', () => {
|
||||
const spy = vi.fn(() => {})
|
||||
vi.spyOn(window, 'location', 'get').mockReturnValue({
|
||||
reload: spy,
|
||||
} as unknown as Location)
|
||||
|
||||
setLocale('fr')
|
||||
const newLocale = getLocale()
|
||||
|
||||
expect(newLocale).toBe('fr')
|
||||
expect(spy).toHaveBeenCalledOnce()
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import { defineMessage } from 'vue-intl'
|
||||
import localeMessages from '@/i18n?dir2json&ext=.json&1'
|
||||
import { match } from '@formatjs/intl-localematcher'
|
||||
|
||||
export const defaultLocale = 'en'
|
||||
export const fallbackLocale = 'en'
|
||||
|
||||
const USER_LOCALE_KEY = 'komga.userLocale'
|
||||
|
||||
|
|
@ -14,11 +15,11 @@ const localeName = defineMessage({
|
|||
|
||||
/**
|
||||
* Loads messages from a translation file by its locale code.
|
||||
* If the translation file does not exist, loads the `defaultLocale` instead.
|
||||
* If the translation file does not exist, loads the `fallbackLocale` instead.
|
||||
* @param locale the locale code, e.g. 'fr'
|
||||
*/
|
||||
export function loadLocale(locale: string): Record<string, string> {
|
||||
const localeToLoad = locale in availableLocales ? locale : defaultLocale
|
||||
const localeToLoad = locale in availableLocales ? locale : fallbackLocale
|
||||
return (localeMessages as unknown as Record<string, Record<string, string>>)[localeToLoad]!
|
||||
}
|
||||
|
||||
|
|
@ -41,12 +42,20 @@ function loadAvailableLocales(): Record<string, string> {
|
|||
export const availableLocales = loadAvailableLocales()
|
||||
|
||||
/**
|
||||
* Gets the saved locale from localStorage.
|
||||
* If the locale is not valid, defaults to 'en'.
|
||||
* Gets the saved locale from localStorage if defined and valid.
|
||||
* Else tries to get the best matching language from the brower's preferred languages.
|
||||
* If the locale is not valid, defaults to 'fallbackLocale'.
|
||||
*/
|
||||
export function getLocale(): string {
|
||||
const storageLocale = localStorage.getItem(USER_LOCALE_KEY) ?? defaultLocale
|
||||
return storageLocale in availableLocales ? storageLocale : defaultLocale
|
||||
const storageLocale = localStorage.getItem(USER_LOCALE_KEY)
|
||||
console.log('locale from storage:', storageLocale)
|
||||
if (storageLocale && storageLocale in availableLocales) return storageLocale
|
||||
|
||||
// get the browser's preferred languages and see if we can match it to an available locale
|
||||
console.log('preferred languages:', navigator.languages)
|
||||
const s = match(navigator.languages, Object.keys(availableLocales), fallbackLocale)
|
||||
console.log('match:', s)
|
||||
return s
|
||||
}
|
||||
|
||||
export const currentLocale = getLocale()
|
||||
|
|
|
|||
Loading…
Reference in a new issue