refactor: update locales handling

This commit is contained in:
axolotle 2024-07-29 14:35:22 +02:00
parent 144b276ff6
commit f3c1931ad0
5 changed files with 92 additions and 46 deletions

View file

@ -3,9 +3,10 @@
* @module api
*/
import { useSettings } from '@/composables/useSettings'
import store from '@/store'
import { openWebSocket, getResponseData, getError } from './handlers'
import type { Obj } from '@/types/commons'
import { getError, getResponseData, openWebSocket } from './handlers'
export type RequestMethod = 'GET' | 'POST' | 'PUT' | 'DELETE'
@ -90,7 +91,8 @@ export function objectToParams(
}
}
if (addLocale) {
urlParams.append('locale', store.getters.locale)
const { locale } = useSettings()
urlParams.append('locale', locale.value)
}
return urlParams
}
@ -132,6 +134,7 @@ export default {
asFormData = true,
}: APIQueryOptions = {},
): Promise<Obj | string> {
const { locale } = useSettings()
// `await` because Vuex actions returns promises by default.
const request: APIRequest | APIRequestAction = await store.dispatch(
'INIT_REQUEST',
@ -151,7 +154,7 @@ export default {
let options = this.options
if (method === 'GET') {
uri += `${uri.includes('?') ? '&' : '?'}locale=${store.getters.locale}`
uri += `${uri.includes('?') ? '&' : '?'}locale=${locale.value}`
} else {
options = {
...options,

View file

@ -0,0 +1,75 @@
import {
createGlobalState,
useLocalStorage,
watchImmediate,
} from '@vueuse/core'
import { ref } from 'vue'
import {
getDefaultLocales,
setI18nFallbackLocale,
setI18nLocale,
} from '@/i18n/helpers'
import type { SupportedLocales } from '@/i18n/supportedLocales'
import supportedLocales from '@/i18n/supportedLocales'
import type { RouteFromTo } from '@/types/commons'
export const useSettings = createGlobalState(() => {
const navigatorLocales = getDefaultLocales()
const localesLoaded = ref(false)
const locale = useLocalStorage<SupportedLocales>(
'locale',
navigatorLocales[0],
)
const fallbackLocale = useLocalStorage<SupportedLocales>(
'fallbackLocale',
navigatorLocales[1],
)
const cache = useLocalStorage('cache', true)
const transitions = useLocalStorage('transitions', true)
const dark = useLocalStorage('dark', false)
const experimental = useLocalStorage('experimental', false)
const spinner = ref('pacman')
const transitionName = ref<'slide-right' | 'slide-left' | undefined>()
watchImmediate([locale, fallbackLocale], async (next, prev) => {
if (next[0] !== prev[0]) await setI18nLocale(next[0])
if (next[1] !== prev[1]) await setI18nFallbackLocale(next[1])
localesLoaded.value = true
})
watchImmediate(dark, (dark) => {
document.documentElement.setAttribute(
'data-bs-theme',
dark ? 'dark' : 'light',
)
})
function updateTransitionName({ to, from }: RouteFromTo) {
// Use the breadcrumb array length as a direction indicator
const toDepth = (to.meta.breadcrumb || []).length
const fromDepth = (from.meta.breadcrumb || []).length
transitionName.value = toDepth < fromDepth ? 'slide-right' : 'slide-left'
}
return {
localesLoaded,
locale,
fallbackLocale,
cache,
transitions,
dark,
experimental,
spinner,
transitionName,
availableLocales: Object.entries(supportedLocales).map(
([locale, { name }]) => {
return { value: locale, text: name }
},
),
updateTransitionName,
}
})

View file

@ -6,10 +6,10 @@ import {
isObjectLiteral,
toEntries,
} from '@/helpers/commons'
import store from '@/store'
import type { ArrInnerType, Obj, Translation } from '@/types/commons'
import type { AdressModelValue, FileModelValue } from '@/types/form'
import { isAdressModelValue, isFileModelValue } from '@/types/form'
import { useSettings } from '@/composables/useSettings'
export const DEFAULT_STATUS_ICON = {
primary: null,
@ -34,11 +34,8 @@ export const DEFAULT_STATUS_ICON = {
export function formatI18nField(field?: Translation): string {
if (!field) return ''
if (typeof field === 'string') return field
const { locale, fallbackLocale } = store.state as {
locale: string
fallbackLocale: string
}
return field[locale] || field[fallbackLocale] || field.en || ''
const { locale, fallbackLocale } = useSettings()
return field[locale.value] || field[fallbackLocale.value] || field.en || ''
}
/**

View file

@ -1,5 +1,4 @@
import i18n from '@/i18n'
import store from '@/store'
import { nextTick } from 'vue'
import supportedLocales, {
@ -13,11 +12,7 @@ export let dateFnsLocale: any
* Returns the first two supported locales that can be found in the `localStorage` or
* in the user browser settings.
*/
function getDefaultLocales() {
const locale: SupportedLocales | null = store.getters.locale
const fallbackLocale: SupportedLocales | null = store.getters.fallbackLocale
if (locale && fallbackLocale) return [locale, fallbackLocale]
export function getDefaultLocales() {
const navigatorLocales = navigator.languages || [navigator.language]
const defaultLocales: SupportedLocales[] = []
@ -48,13 +43,7 @@ export async function setI18nLocale(locale: SupportedLocales) {
loadLocaleMessages('en')
}
if (i18n.mode === 'legacy') {
// @ts-ignore
i18n.global.locale = locale
} else {
i18n.global.locale.value = locale
}
i18n.global.locale.value = locale
document.querySelector('html')!.setAttribute('lang', locale)
// FIXME can't currently change document direction easily since bootstrap still doesn't handle rtl.
// document.dir = locale === 'ar' ? 'rtl' : 'ltr'
@ -64,19 +53,13 @@ export async function setI18nFallbackLocale(locale: SupportedLocales) {
if (!i18n.global.availableLocales.includes(locale)) {
await loadLocaleMessages(locale)
}
if (i18n.mode === 'legacy') {
// @ts-ignore
i18n.global.fallbackLocale = [locale, 'en']
} else {
i18n.global.fallbackLocale.value = [locale, 'en']
}
i18n.global.fallbackLocale.value = [locale, 'en']
}
/**
* Loads a translation file and adds its content to the i18n plugin `messages`.
*
* @return {Promise<string>} Promise that resolve the given locale string
* @return Promise that resolve the given locale string
*/
export async function loadLocaleMessages(locale: SupportedLocales) {
// load locale messages with dynamic import
@ -97,14 +80,3 @@ async function loadDateFnsLocale(locale: SupportedLocales) {
await import(`../../node_modules/date-fns/locale/${dateFnsLocaleName}.mjs`)
).default
}
/**
* Initialize all locales
*/
export async function initDefaultLocales() {
// Get defined locales from `localStorage` or `navigator`
const [locale, fallbackLocale] = getDefaultLocales()
await store.dispatch('UPDATE_LOCALE', locale)
await store.dispatch('UPDATE_FALLBACKLOCALE', fallbackLocale || 'en')
}

View file

@ -2,13 +2,14 @@ import { createApp, type Component } from 'vue'
import App from './App.vue'
import { createBootstrap } from 'bootstrap-vue-next'
import { VueShowdownPlugin } from 'vue-showdown'
import { watchOnce } from '@vueuse/core'
import { useSettings } from './composables/useSettings'
import store from './store'
import router from './router'
import i18n from './i18n'
import { registerGlobalErrorHandlers } from './api'
import { initDefaultLocales } from './i18n/helpers'
import '@/scss/main.scss'
@ -47,7 +48,5 @@ Object.values(globalComponentsModules).forEach(
registerGlobalErrorHandlers()
// Load default locales translations files and setup store data
initDefaultLocales().then(() => {
app.mount('#app')
})
// Load default locales translations files then mount the app
watchOnce(useSettings().localesLoaded, () => app.mount('#app'))