From 00fe9f42bc8e6e6c36048ae3b89259a1ca52c115 Mon Sep 17 00:00:00 2001 From: axolotle Date: Thu, 11 Jul 2024 15:10:18 +0200 Subject: [PATCH] ts: add i18n typing --- app/src/i18n/helpers.ts | 42 +++++++++++++++++--------------- app/src/i18n/supportedLocales.ts | 19 ++++++++++++++- app/src/store/settings.ts | 11 +++------ 3 files changed, 45 insertions(+), 27 deletions(-) diff --git a/app/src/i18n/helpers.ts b/app/src/i18n/helpers.ts index bc25555f..f23730c4 100644 --- a/app/src/i18n/helpers.ts +++ b/app/src/i18n/helpers.ts @@ -1,40 +1,42 @@ -import { nextTick } from 'vue' -import store from '@/store' import i18n from '@/i18n' -import supportedLocales from './supportedLocales' +import store from '@/store' +import { nextTick } from 'vue' -export let dateFnsLocale +import supportedLocales, { + isSupportedLocale, + type SupportedLocales, +} from '@/i18n/supportedLocales' + +export let dateFnsLocale: any /** * Returns the first two supported locales that can be found in the `localStorage` or * in the user browser settings. - * - * @return {string[]} */ function getDefaultLocales() { - const locale = store.getters.locale - const fallbackLocale = store.getters.fallbackLocale + const locale: SupportedLocales | null = store.getters.locale + const fallbackLocale: SupportedLocales | null = store.getters.fallbackLocale if (locale && fallbackLocale) return [locale, fallbackLocale] const navigatorLocales = navigator.languages || [navigator.language] - const defaultLocales = [] - const supported = Object.keys(supportedLocales) + const defaultLocales: SupportedLocales[] = [] + for (const locale of navigatorLocales) { - if (supported.includes(locale) && !defaultLocales.includes(locale)) { + if (isSupportedLocale(locale) && !defaultLocales.includes(locale)) { defaultLocales.push(locale) } else { const lang = locale.split('-')[0] - if (supported.includes(lang) && !defaultLocales.includes(lang)) { + if (isSupportedLocale(lang) && !defaultLocales.includes(lang)) { defaultLocales.push(lang) } } if (defaultLocales.length === 2) break } - return defaultLocales + return defaultLocales as [SupportedLocales, SupportedLocales] } -export async function setI18nLocale(locale) { +export async function setI18nLocale(locale: SupportedLocales) { if (!i18n.global.availableLocales.includes(locale)) { await loadLocaleMessages(locale) // also query/set the date-fns locale object for time translation @@ -47,22 +49,24 @@ export async function setI18nLocale(locale) { } if (i18n.mode === 'legacy') { + // @ts-ignore i18n.global.locale = locale } else { i18n.global.locale.value = locale } - document.querySelector('html').setAttribute('lang', 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' } -export async function setI18nFallbackLocale(locale) { +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'] @@ -74,7 +78,7 @@ export async function setI18nFallbackLocale(locale) { * * @return {Promise} Promise that resolve the given locale string */ -export async function loadLocaleMessages(locale) { +export async function loadLocaleMessages(locale: SupportedLocales) { // load locale messages with dynamic import const messages = await import(`./locales/${locale}.json`) @@ -87,8 +91,8 @@ export async function loadLocaleMessages(locale) { /** * Loads a date-fns locale object */ -async function loadDateFnsLocale(locale) { - const dateFnsLocaleName = supportedLocales[locale].dateFnsLocale || locale +async function loadDateFnsLocale(locale: SupportedLocales) { + const dateFnsLocaleName = supportedLocales[locale].dateFnsLocale ?? locale dateFnsLocale = ( await import(`../../node_modules/date-fns/locale/${dateFnsLocaleName}.mjs`) ).default diff --git a/app/src/i18n/supportedLocales.ts b/app/src/i18n/supportedLocales.ts index 5574fc7b..2da0bf11 100644 --- a/app/src/i18n/supportedLocales.ts +++ b/app/src/i18n/supportedLocales.ts @@ -4,7 +4,7 @@ // If a new locale or a new date-fns locale is added, add it to the supported // locales list in `app/vue.config.js` -export default { +const supportedLocales = { ar: { name: 'عربي', }, @@ -137,4 +137,21 @@ export default { name: '简化字', dateFnsLocale: 'zh-CN', }, +} as const + +type SL = typeof supportedLocales +export type SupportedLocales = keyof SL +export type SupportedDateFnsLocales = keyof { + [k in SupportedLocales as SL[k] extends { dateFnsLocale: string } + ? SL[k]['dateFnsLocale'] + : k]: never } + +export function isSupportedLocale(locale: string): locale is SupportedLocales { + return Object.keys(supportedLocales).includes(locale) +} + +export default supportedLocales as Record< + SupportedLocales, + { name: string; dateFnsLocale?: SupportedDateFnsLocales } +> diff --git a/app/src/store/settings.ts b/app/src/store/settings.ts index 67a5d1a9..8407b258 100644 --- a/app/src/store/settings.ts +++ b/app/src/store/settings.ts @@ -15,7 +15,6 @@ export default { dark: localStorage.getItem('dark') === 'true', experimental: localStorage.getItem('experimental') === 'true', spinner: 'pacman', - supportedLocales, }, mutations: { @@ -85,12 +84,10 @@ export default { experimental: (state) => state.experimental, spinner: (state) => state.spinner, - availableLocales: (state) => { - return Object.entries(state.supportedLocales).map( - ([locale, { name }]) => { - return { value: locale, text: name } - }, - ) + availableLocales: () => { + return Object.entries(supportedLocales).map(([locale, { name }]) => { + return { value: locale, text: name } + }) }, }, }