refactor: updated i18n implementation in the admin dashboard (#3395)
* feat: introduced new unplugin i18n and removed the old vite i18n package * refactor: updated vite config to support the new plugin * refactor: removed irrelevant logic from the i18n module
This commit is contained in:
committed by
GitHub
parent
17d6ae15a5
commit
6c63a8dc28
@@ -1,154 +1,16 @@
|
||||
import * as R from 'fp-ts/Record';
|
||||
import * as A from 'fp-ts/Array';
|
||||
import * as O from 'fp-ts/Option';
|
||||
import { pipe } from 'fp-ts/function';
|
||||
import { createI18n, I18n, I18nOptions } from 'vue-i18n';
|
||||
import { createI18n } from 'vue-i18n';
|
||||
import { HoppModule } from '.';
|
||||
|
||||
import languages from '../../languages.json';
|
||||
|
||||
import { throwError } from '../helpers/error';
|
||||
import { getLocalConfig, setLocalConfig } from '../helpers/localpersistence';
|
||||
|
||||
/*
|
||||
In context of this file, we have 2 main kinds of things.
|
||||
1. Locale -> A locale is termed as the i18n entries present in the /locales folder
|
||||
2. Language -> A language is an entry in the /languages.json folder
|
||||
|
||||
Each language entry should correspond to a locale entry.
|
||||
*/
|
||||
|
||||
/*
|
||||
* As we migrate out of Nuxt I18n into our own system for i18n management,
|
||||
* Some stuff has changed regarding how it works.
|
||||
*
|
||||
* The previous system works by using paths to represent locales to load.
|
||||
* Basically, /es/realtime will load the /realtime page but with 'es' language
|
||||
*
|
||||
* In the new system instead of relying on the lang code, we store the language
|
||||
* in the application local config store (localStorage). The URLs don't have
|
||||
* a locale path effect
|
||||
*/
|
||||
|
||||
// TODO: Syncing into settings ?
|
||||
|
||||
const LOCALES = import.meta.glob('../../locales/*.json');
|
||||
|
||||
setTimeout(() => {
|
||||
console.log(LOCALES);
|
||||
}, 1000);
|
||||
|
||||
type LanguagesDef = {
|
||||
code: string;
|
||||
file: string;
|
||||
iso: string;
|
||||
name: string;
|
||||
dir?: 'ltr' | 'rtl'; // Text Orientation (defaults to 'ltr')
|
||||
};
|
||||
|
||||
const FALLBACK_LANG_CODE = 'en';
|
||||
|
||||
// TypeScript cannot understand dir is restricted to "ltr" or "rtl" yet, hence assertion
|
||||
export const APP_LANGUAGES: LanguagesDef[] = languages as LanguagesDef[];
|
||||
|
||||
export const APP_LANG_CODES = languages.map(({ code }) => code);
|
||||
|
||||
export const FALLBACK_LANG = pipe(
|
||||
APP_LANGUAGES,
|
||||
A.findFirst((x) => x.code === FALLBACK_LANG_CODE),
|
||||
O.getOrElseW(() =>
|
||||
throwError(`Could not find the fallback language '${FALLBACK_LANG_CODE}'`)
|
||||
)
|
||||
);
|
||||
|
||||
// A reference to the i18n instance
|
||||
let i18nInstance: I18n<any, any, any> | null = null;
|
||||
|
||||
const resolveCurrentLocale = () =>
|
||||
pipe(
|
||||
// Resolve from locale and make sure it is in languages
|
||||
getLocalConfig('locale'),
|
||||
O.fromNullable,
|
||||
O.filter((locale) =>
|
||||
pipe(
|
||||
APP_LANGUAGES,
|
||||
A.some(({ code }) => code === locale)
|
||||
)
|
||||
),
|
||||
|
||||
// Else load from navigator.language
|
||||
O.alt(() =>
|
||||
pipe(
|
||||
APP_LANGUAGES,
|
||||
A.findFirst(({ code }) => navigator.language.startsWith(code)), // en-US should also match to en
|
||||
O.map(({ code }) => code)
|
||||
)
|
||||
),
|
||||
|
||||
// Else load fallback
|
||||
O.getOrElse(() => FALLBACK_LANG_CODE)
|
||||
);
|
||||
|
||||
/**
|
||||
* Changes the application language. This function returns a promise as
|
||||
* the locale files are lazy loaded on demand
|
||||
* @param locale The locale code of the language to load
|
||||
*/
|
||||
export const changeAppLanguage = async (locale: string) => {
|
||||
const localeData = (
|
||||
(await pipe(
|
||||
LOCALES,
|
||||
R.lookup(`../../locales/${locale}.json`),
|
||||
O.getOrElseW(() =>
|
||||
throwError(
|
||||
`Tried to change app language to non-existent locale '${locale}'`
|
||||
)
|
||||
)
|
||||
)()) as any
|
||||
).default;
|
||||
|
||||
if (!i18nInstance) {
|
||||
throw new Error('Tried to change language without active i18n instance');
|
||||
}
|
||||
|
||||
i18nInstance.global.setLocaleMessage(locale, localeData);
|
||||
|
||||
// TODO: Look into the type issues here
|
||||
i18nInstance.global.locale.value = locale;
|
||||
|
||||
setLocalConfig('locale', locale);
|
||||
};
|
||||
import messages from '@intlify/unplugin-vue-i18n/messages';
|
||||
|
||||
export default <HoppModule>{
|
||||
onVueAppInit(app) {
|
||||
const i18n = createI18n(<I18nOptions>{
|
||||
locale: 'en', // TODO: i18n system!
|
||||
const i18n = createI18n({
|
||||
locale: 'en',
|
||||
messages,
|
||||
fallbackLocale: 'en',
|
||||
legacy: false,
|
||||
allowComposition: true,
|
||||
});
|
||||
|
||||
app.use(i18n);
|
||||
|
||||
i18nInstance = i18n;
|
||||
|
||||
// TODO: Global loading state to hide the resolved lang loading
|
||||
const currentLocale = resolveCurrentLocale();
|
||||
changeAppLanguage(currentLocale);
|
||||
|
||||
setLocalConfig('locale', currentLocale);
|
||||
},
|
||||
onBeforeRouteChange(to, _, router) {
|
||||
// Convert old locale path format to new format
|
||||
const oldLocalePathLangCode = APP_LANG_CODES.find((langCode) =>
|
||||
to.path.startsWith(`/${langCode}/`)
|
||||
);
|
||||
|
||||
// Change language to the correct lang code
|
||||
if (oldLocalePathLangCode) {
|
||||
changeAppLanguage(oldLocalePathLangCode);
|
||||
|
||||
router.replace(to.path.substring(`/${oldLocalePathLangCode}`.length));
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user