mirror of
https://github.com/YunoHost/yunohost-admin.git
synced 2024-09-03 20:06:15 +02:00
refactor: update locales handling
This commit is contained in:
parent
144b276ff6
commit
f3c1931ad0
5 changed files with 92 additions and 46 deletions
|
@ -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,
|
||||
|
|
75
app/src/composables/useSettings.ts
Normal file
75
app/src/composables/useSettings.ts
Normal 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,
|
||||
}
|
||||
})
|
|
@ -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 || ''
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
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']
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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')
|
||||
}
|
||||
|
|
|
@ -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'))
|
||||
|
|
Loading…
Reference in a new issue