Feat: Place the language configuration in web/.env for easy user configuration. (#13920)

### What problem does this PR solve?

Feat: Place the language configuration in web/.env for easy user
configuration.

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
balibabu
2026-04-03 16:50:18 +08:00
committed by GitHub
parent 354108922b
commit 5b43c7cf16
6 changed files with 51 additions and 55 deletions

View File

@@ -1,2 +1,3 @@
PORT=9222
DID_YOU_KNOW=none
DID_YOU_KNOW=none
VITE_DEFAULT_LANGUAGE_CODE=en # en', 'zh-Hans', 'zh-Hant', 'ru', 'id', 'ja', 'es', 'vi', 'pt-BR', 'de', 'fr', 'it', 'bg', 'ar', 'tr'

View File

@@ -1,6 +1,6 @@
import { Toaster as Sonner } from '@/components/ui/sonner';
import { Toaster } from '@/components/ui/toaster';
import i18n, { changeLanguageAsync } from '@/locales/config';
import { changeLanguageAsync } from '@/locales/config';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { configResponsive } from 'ahooks';
import dayjs from 'dayjs';
@@ -64,25 +64,6 @@ const queryClient = new QueryClient({
});
function Root({ children }: React.PropsWithChildren) {
const updateDocumentLocale = (lng: string) => {
document.documentElement.lang = lng;
document.documentElement.dir = 'ltr';
dayjs.locale(lng === 'zh' ? 'zh-cn' : lng);
};
useEffect(() => {
const handleLanguageChanged = (lng: string) => {
storage.setLanguage(lng);
updateDocumentLocale(lng);
};
updateDocumentLocale(storage.getLanguage() || i18n.language || 'en');
i18n.on('languageChanged', handleLanguageChanged);
return () => {
i18n.off('languageChanged', handleLanguageChanged);
};
}, []);
return (
<>
{children}

View File

@@ -26,7 +26,6 @@ import {
useRef,
useState,
} from 'react';
import { useTranslation } from 'react-i18next';
import { v4 as uuid } from 'uuid';
import { useTranslate } from './common-hooks';
import { useSetPaginationParams } from './route-hook';
@@ -51,15 +50,15 @@ export const useSetSelectedRecord = <T = IKnowledgeFile>() => {
};
export const useChangeLanguage = () => {
const { i18n } = useTranslation();
const { saveSetting } = useSaveSetting();
const changeLanguage = (lng: string) => {
// const targetLng = LanguageTranslationMap[lng as keyof typeof LanguageTranslationMap];
changeLanguageAsync(lng);
saveSetting({ language: lng });
};
const changeLanguage = useCallback(
(lng: string) => {
changeLanguageAsync(lng);
saveSetting({ language: lng });
},
[saveSetting],
);
return changeLanguage;
};

View File

@@ -4,9 +4,14 @@ import userService, {
getLoginChannels,
loginWithChannel,
} from '@/services/user-service';
import authorizationUtil, { redirectToLogin } from '@/utils/authorization-util';
import {
default as authorizationUtil,
redirectToLogin,
default as storage,
} from '@/utils/authorization-util';
import { useMutation, useQuery } from '@tanstack/react-query';
import { useTranslation } from 'react-i18next';
import { useSaveSetting } from './use-user-setting-request';
export interface ILoginRequestBody {
email: string;
@@ -48,6 +53,7 @@ export const useLoginWithChannel = () => {
};
export const useLogin = () => {
const { saveSetting } = useSaveSetting(true);
const {
data,
isPending: loading,
@@ -57,6 +63,10 @@ export const useLogin = () => {
mutationFn: async (params: { email: string; password: string }) => {
const { data: res = {}, response } = await userService.login(params);
if (res.code === 0) {
// The language is based on the .lng stored in the client's local storage.
// The language stored in the database is for agent template resources,
// since the agent template resources are stored on the server.
saveSetting({ language: storage.getLanguage() });
const { data } = res;
const authorization = response.headers.get(Authorization);
const token = data.access_token;

View File

@@ -11,11 +11,7 @@ import {
IUserInfo,
} from '@/interfaces/database/user-setting';
import { ISetLangfuseConfigRequestBody } from '@/interfaces/request/system';
import {
changeLanguageAsync,
DEFAULT_LANGUAGE_CODE,
supportedLanguages,
} from '@/locales/config';
import { DEFAULT_LANGUAGE_CODE, supportedLanguages } from '@/locales/config';
import { Routes } from '@/routes';
import userService, {
addTenantUser,
@@ -62,10 +58,6 @@ export const useFetchUserInfo = (): ResponseGetType<IUserInfo> => {
supportedLanguages.find((lang) => lang.code === data.data.language)
?.code ?? DEFAULT_LANGUAGE_CODE;
if (targetLng) {
await changeLanguageAsync(targetLng);
}
return Object.assign({}, data.data, {
language: targetLng,
});
@@ -132,7 +124,7 @@ export const useSelectParserList = (): Array<{
label: string;
}> => {
const { data: tenantInfo } = useFetchTenantInfo(true);
const { t, i18n } = useTranslation();
const { t } = useTranslation();
const defaultParsers = useMemo(
() => [
@@ -163,7 +155,7 @@ export const useSelectParserList = (): Array<{
{ value: 'email', label: t('knowledgeConfiguration.parserLabel.email') },
{ value: 'tag', label: t('knowledgeConfiguration.parserLabel.tag') },
],
[i18n.language, t],
[t],
);
const parserList = useMemo(() => {
@@ -183,7 +175,7 @@ export const useSelectParserList = (): Array<{
return parserList;
};
export const useSaveSetting = () => {
export const useSaveSetting = (silent = false) => {
const queryClient = useQueryClient();
const { t } = useTranslation();
const {
@@ -197,7 +189,9 @@ export const useSaveSetting = () => {
) => {
const { data } = await userService.setting(userInfo);
if (data.code === 0) {
message.success(t('message.modified'));
if (!silent) {
message.success(t('message.modified'));
}
queryClient.invalidateQueries({ queryKey: ['userInfo'] });
}
return data?.code;

View File

@@ -1,12 +1,16 @@
import { LanguageAbbreviation } from '@/constants/common';
import storage from '@/utils/authorization-util';
import dayjs from 'dayjs';
import i18n from 'i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import { upperFirst } from 'lodash';
import { initReactI18next } from 'react-i18next';
import { LanguageAbbreviation } from '@/constants/common';
import translation_en from './en';
//The language is based on the .ng file stored in the client's local storage.
// The language stored in the database is for agent template resources, as these resources reside on the server.
// When a user logs in from a different machine, the login page language is the language configured by VITE_DEFAULT_LANGUAGE_CODE.
const languageImports: Record<string, () => Promise<{ default: any }>> = {
[LanguageAbbreviation.En]: () => import('./en'),
[LanguageAbbreviation.Zh]: () => import('./zh'),
@@ -40,18 +44,27 @@ export const supportedLanguages = supportedLanguageCodes.map((code) => {
};
});
export const DEFAULT_LANGUAGE_CODE = LanguageAbbreviation.En;
export const DEFAULT_LANGUAGE_CODE =
import.meta.env.VITE_DEFAULT_LANGUAGE_CODE || LanguageAbbreviation.En;
const resources = {
[LanguageAbbreviation.En]: translation_en,
};
const updateDocumentLocale = (lng: string) => {
document.documentElement.lang = lng;
document.documentElement.dir = 'ltr';
dayjs.locale(lng === 'zh' ? 'zh-cn' : lng);
};
i18n
.use(initReactI18next)
.use(LanguageDetector)
.init({
detection: {
lookupLocalStorage: 'lng',
order: ['localStorage'],
caches: [],
},
supportedLngs: supportedLanguageCodes,
resources,
@@ -62,7 +75,6 @@ i18n
});
export const loadLanguageAsync = async (lng: string): Promise<void> => {
// const normalizedLng = normalizeLanguageCode(lng);
const normalizedLng = lng;
if (i18n.hasResourceBundle(normalizedLng, 'translation')) {
@@ -85,7 +97,6 @@ export const loadLanguageAsync = async (lng: string): Promise<void> => {
};
export const changeLanguageAsync = async (lng: string): Promise<void> => {
// const normalizedLng = normalizeLanguageCode(lng);
const normalizedLng = lng;
if (
@@ -94,16 +105,16 @@ export const changeLanguageAsync = async (lng: string): Promise<void> => {
) {
await loadLanguageAsync(normalizedLng);
}
storage.setLanguage(lng);
updateDocumentLocale(lng);
await i18n.changeLanguage(normalizedLng);
};
export const initLanguage = async (): Promise<void> => {
// const currentLng = normalizeLanguageCode(
// i18n.language || localStorage.getItem('lng') || LanguageAbbreviation.En,
// );
const currentLng =
i18n.language || localStorage.getItem('lng') || DEFAULT_LANGUAGE_CODE;
const currentLng = storage.getLanguage() || DEFAULT_LANGUAGE_CODE;
await changeLanguageAsync(currentLng);
};