Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 84 additions & 24 deletions frontend/src/lang/index.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,94 @@
import { createI18n } from 'vue-i18n';
import zh from './modules/zh';
import zhHant from './modules/zh-Hant';
import en from './modules/en';
import ptBr from './modules/pt-br';
import ja from './modules/ja';
import ru from './modules/ru';
import ms from './modules/ms';
import ko from './modules/ko';
import tr from './modules/tr';
import esES from './modules/es-es';

type LocaleMessage = Record<string, unknown>;
type LocaleLoader = () => Promise<{ default: LocaleMessage }>;

const DEFAULT_LOCALE = 'en';
const STORAGE_KEY = 'lang';

const LOCALE_LOADERS: Record<string, LocaleLoader> = {
zh: () => import('./modules/zh'),
'zh-Hant': () => import('./modules/zh-Hant'),
en: () => import('./modules/en'),
'pt-BR': () => import('./modules/pt-br'),
ja: () => import('./modules/ja'),
ru: () => import('./modules/ru'),
ms: () => import('./modules/ms'),
ko: () => import('./modules/ko'),
tr: () => import('./modules/tr'),
'es-ES': () => import('./modules/es-es'),
};

const getStoredLocale = () => {
if (typeof window === 'undefined') return DEFAULT_LOCALE;
return localStorage.getItem(STORAGE_KEY) || DEFAULT_LOCALE;
};

const initialLocale = getStoredLocale();

const loadedLocales = new Set<string>();

export const loadLocaleMessages = async (locale: string) => {
const targetLocale = LOCALE_LOADERS[locale] ? locale : DEFAULT_LOCALE;
if (loadedLocales.has(targetLocale)) {
return targetLocale;
}
const loader = LOCALE_LOADERS[targetLocale];
if (!loader) {
return targetLocale;
}
const messagesModule = await loader();
const messages = messagesModule.default || {};
if (!i18n) {
return targetLocale;
}
i18n.global.setLocaleMessage(targetLocale, messages);
loadedLocales.add(targetLocale);
return targetLocale;
};

const getInitialMessages = async (): Promise<Record<string, LocaleMessage>> => {
const loader = LOCALE_LOADERS[initialLocale];
if (!loader) {
return { [initialLocale]: {} };
}
try {
const messagesModule = await loader();
const messages = messagesModule.default || {};
loadedLocales.add(initialLocale);
return { [initialLocale]: messages };
} catch {
return { [initialLocale]: {} };
}
};

const initialMessages = await getInitialMessages();

const i18n = createI18n({
legacy: false,
missingWarn: false,
locale: localStorage.getItem('lang') || 'en',
fallbackLocale: 'en',
fallbackWarn: false,
locale: initialLocale,
fallbackLocale: DEFAULT_LOCALE,
globalInjection: true,
messages: {
zh,
'zh-Hant': zhHant,
en,
'pt-BR': ptBr,
ja,
ru,
ms,
ko,
tr,
'es-ES': esES,
},
messages: initialMessages,
warnHtmlMessage: false,
});

export const ensureFallbackLocale = async () => {
const fallback = i18n.global.fallbackLocale.value || DEFAULT_LOCALE;
if (typeof fallback === 'string') {
await loadLocaleMessages(fallback);
}
};

export const setActiveLocale = async (locale: string) => {
const loaded = await loadLocaleMessages(locale);
i18n.global.locale.value = loaded;
if (typeof window !== 'undefined') {
localStorage.setItem(STORAGE_KEY, loaded);
}
return loaded;
};

export default i18n;
36 changes: 22 additions & 14 deletions frontend/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ for (const path in styleModule) {
}

import router from '@/routers/index';
import i18n from '@/lang/index';
import i18n, { ensureFallbackLocale, loadLocaleMessages } from '@/lang/index';
import pinia from '@/store/index';
import SvgIcon from './components/svg-icon/svg-icon.vue';
import Components from '@/components';
Expand All @@ -24,19 +24,27 @@ import * as Icons from '@element-plus/icons-vue';

import directives from '@/directives/index';

const app = createApp(App);
app.component('SvgIcon', SvgIcon);
app.use(ElementPlus);
app.use(Fit2CloudPlus, { locale: i18n.global.messages.value[localStorage.getItem('lang') || 'zh'] });
const bootstrap = async () => {
const currentLocale = i18n.global.locale.value;

Object.keys(Icons).forEach((key) => {
app.component(key, Icons[key as keyof typeof Icons]);
});
await Promise.all([loadLocaleMessages(currentLocale), ensureFallbackLocale()]);

app.use(router);
app.use(i18n);
app.use(pinia);
app.use(Components);
app.use(directives);
const app = createApp(App);
app.component('SvgIcon', SvgIcon);
app.use(ElementPlus);
app.use(Fit2CloudPlus, { locale: i18n.global.getLocaleMessage(currentLocale) });

app.mount('#app');
Object.keys(Icons).forEach((key) => {
app.component(key, Icons[key as keyof typeof Icons]);
});

app.use(router);
app.use(i18n);
app.use(pinia);
app.use(Components);
app.use(directives);

app.mount('#app');
};

bootstrap();
11 changes: 6 additions & 5 deletions frontend/src/store/modules/global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { defineStore } from 'pinia';
import piniaPersistConfig from '@/config/pinia-persist';
import { GlobalState, ThemeConfigProp } from '../interface';
import { DeviceType } from '@/enums/app';
import i18n from '@/lang';
import i18n, { setActiveLocale } from '@/lang';

const GlobalStore = defineStore({
id: 'GlobalState',
Expand Down Expand Up @@ -48,7 +48,7 @@ const GlobalStore = defineStore({
isMasterProductPro: false,
isOffLine: false,

masterAlias: i18n.global.t('xpack.node.master'),
masterAlias: '',
currentNode: 'local',
currentNodeAddr: '',
}),
Expand Down Expand Up @@ -79,9 +79,10 @@ const GlobalStore = defineStore({
setCsrfToken(token: string) {
this.csrfToken = token;
},
updateLanguage(language: any) {
this.language = language;
localStorage.setItem('lang', language);
async updateLanguage(language: string) {
const activeLocale = await setActiveLocale(language);
this.language = activeLocale;
return activeLocale;
},
setThemeConfig(themeConfig: ThemeConfigProp) {
this.themeConfig = themeConfig;
Expand Down
53 changes: 21 additions & 32 deletions frontend/src/views/login/components/login-form.vue
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
<el-dropdown-item command="ko">한국어</el-dropdown-item>
<el-dropdown-item command="ru">Русский</el-dropdown-item>
<el-dropdown-item command="ms">Bahasa Melayu</el-dropdown-item>
<el-dropdown-item command="Tr">Turkish</el-dropdown-item>
<el-dropdown-item command="tr">Turkish</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
Expand Down Expand Up @@ -199,7 +199,6 @@ const themeConfig = computed(() => globalStore.themeConfig);
const globalStore = GlobalStore();
const menuStore = MenuStore();
const tabsStore = TabsStore();
const usei18n = useI18n();

const errAuthInfo = ref(false);
const errCaptcha = ref(false);
Expand Down Expand Up @@ -276,32 +275,24 @@ const loading = ref<boolean>(false);
const mfaShow = ref<boolean>(false);
const dropdownText = ref('中文(简体)');

function handleCommand(command: string) {
loginForm.language = command;
usei18n.locale.value = command;
globalStore.updateLanguage(command);
if (command === 'zh') {
dropdownText.value = '中文(简体)';
} else if (command === 'en') {
dropdownText.value = 'English';
} else if (command === 'pt-BR') {
dropdownText.value = 'Português (Brasil)';
} else if (command === 'zh-Hant') {
dropdownText.value = '中文(繁體)';
} else if (command === 'ko') {
dropdownText.value = '한국어';
} else if (command === 'ja') {
dropdownText.value = '日本語';
} else if (command === 'ru') {
dropdownText.value = 'Русский';
} else if (command === 'ms') {
dropdownText.value = 'Bahasa Melayu';
} else if (command === 'tr') {
dropdownText.value = 'Turkish';
} else if (command === 'es-ES') {
dropdownText.value = 'España - Español';
}
}
const languageLabelMap: Record<string, string> = {
zh: '中文(简体)',
en: 'English',
'pt-BR': 'Português (Brasil)',
'zh-Hant': '中文(繁體)',
ko: '한국어',
ja: '日本語',
ru: 'Русский',
ms: 'Bahasa Melayu',
tr: 'Turkish',
'es-ES': 'España - Español',
};

const handleCommand = async (command: string) => {
const activeLocale = await globalStore.updateLanguage(command);
loginForm.language = activeLocale;
dropdownText.value = languageLabelMap[activeLocale] || languageLabelMap.zh;
};

const agreeWithLogin = () => {
open.value = false;
Expand Down Expand Up @@ -421,18 +412,16 @@ const getSetting = async () => {
try {
const res = await getLoginSetting();
isDemo.value = res.data.isDemo;
loginForm.language = res.data.language;
handleCommand(loginForm.language);
const language = res.data.language || loginForm.language;
await handleCommand(language);
isIntl.value = res.data.isIntl;
isFxplay.value = res.data.isFxplay;
globalStore.isFxplay = isFxplay.value;
globalStore.isOffLine = res.data.isOffLine;

document.title = res.data.panelName;
i18n.locale.value = res.data.language;
i18n.warnHtmlMessage = false;
globalStore.setOpenMenuTabs(res.data.menuTabs === 'Enable');
globalStore.updateLanguage(res.data.language);
globalStore.setThemeConfig({ ...themeConfig.value, theme: res.data.theme, panelName: res.data.panelName });
} catch (error) {}
};
Expand Down
39 changes: 19 additions & 20 deletions frontend/src/views/setting/panel/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -508,26 +508,25 @@ const onSave = async (key: string, val: any) => {
key: key,
value: val + '',
};
await updateSetting(param)
.then(() => {
if (key === 'Language') {
i18n.global.locale.value = val;
globalStore.updateLanguage(val);
location.reload();
}
if (key === 'Theme') {
handleThemeChange(val);
}
if (key === 'MenuTabs') {
globalStore.setOpenMenuTabs(val === 'Enable');
}
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
search();
loading.value = false;
})
.catch(() => {
loading.value = false;
});
try {
await updateSetting(param);
if (key === 'Language') {
await globalStore.updateLanguage(val);
location.reload();
}
if (key === 'Theme') {
handleThemeChange(val);
}
if (key === 'MenuTabs') {
globalStore.setOpenMenuTabs(val === 'Enable');
}
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
search();
} catch (error) {
loading.value = false;
return;
}
loading.value = false;
};

onMounted(() => {
Expand Down
Loading