Perf: i18n change and captcha code. (#2625)

* perf: send captcha check

* perf: back router

* perf: i18n init

* perf: ui

* i18n

* perf: ui duration
This commit is contained in:
Archer
2024-09-05 23:01:12 +08:00
committed by GitHub
parent 478386c612
commit c614f8b9ca
43 changed files with 259 additions and 793 deletions

View File

@@ -5,11 +5,11 @@
module.exports = {
i18n: {
defaultLocale: 'zh',
defaultLocale: 'en',
locales: ['en', 'zh'],
localeDetection: false
},
localePath:
typeof window === 'undefined' ? require('path').resolve('../../packages/web/i18n') : '/i18n',
reloadOnPrerender: process.env.NODE_ENV === 'development'
}
};

View File

@@ -68,7 +68,6 @@
"devDependencies": {
"@svgr/webpack": "^6.5.1",
"@types/formidable": "^2.0.5",
"@types/js-cookie": "^3.0.3",
"@types/js-yaml": "^4.0.9",
"@types/jsonwebtoken": "^9.0.3",
"@types/lodash": "^4.14.191",

View File

@@ -7,9 +7,11 @@ import { useQuery } from '@tanstack/react-query';
import { useUserStore } from '@/web/support/user/useUserStore';
import { getUnreadCount } from '@/web/support/user/inform/api';
import dynamic from 'next/dynamic';
import { useI18nLng } from '@fastgpt/web/hooks/useI18n';
import Auth from './auth';
import { useSystem } from '@fastgpt/web/hooks/useSystem';
import { useMount } from 'ahooks';
const Navbar = dynamic(() => import('./navbar'));
const NavbarPhone = dynamic(() => import('./navbarPhone'));
const UpdateInviteModal = dynamic(() => import('@/components/support/user/team/UpdateInviteModal'));
@@ -46,6 +48,7 @@ const Layout = ({ children }: { children: JSX.Element }) => {
const { loading, feConfigs, isNotSufficientModal } = useSystemStore();
const { isPc } = useSystem();
const { userInfo } = useUserStore();
const { setUserDefaultLng } = useI18nLng();
const isChatPage = useMemo(
() => router.pathname === '/chat' && Object.values(router.query).join('').length !== 0,
@@ -61,6 +64,10 @@ const Layout = ({ children }: { children: JSX.Element }) => {
const isHideNavbar = !!pcUnShowLayoutRoute[router.pathname];
useMount(() => {
setUserDefaultLng();
});
return (
<>
<Box h={'100%'} bg={'myGray.100'}>

View File

@@ -37,7 +37,7 @@ const SendCodeAuthModal = ({
<Skeleton
minH="200px"
isLoaded={!loading}
fadeDuration={2}
fadeDuration={1}
display={'flex'}
justifyContent={'center'}
my={1}

View File

@@ -15,6 +15,7 @@ import NextHead from '@/components/common/NextHead';
function App({ Component, pageProps }: AppProps) {
const { feConfigs, scripts, title } = useInitApp();
const { t } = useTranslation();
return (
<>
<NextHead

View File

@@ -7,8 +7,10 @@ import { UserType } from '@fastgpt/global/support/user/type';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { useForm } from 'react-hook-form';
import { UserUpdateParams } from '@/types/user';
import { langMap, setLngStore } from '@/web/common/utils/i18n';
import { langMap } from '@/web/common/utils/i18n';
import { useRouter } from 'next/router';
import { useI18nLng } from '@fastgpt/web/hooks/useI18n';
import MySelect from '@fastgpt/web/components/common/MySelect';
import TimezoneSelect from '@fastgpt/web/components/common/MySelect/TimezoneSelect';
@@ -16,7 +18,7 @@ const Individuation = () => {
const { t, i18n } = useTranslation();
const { userInfo, updateUserInfo } = useUserStore();
const { toast } = useToast();
const router = useRouter();
const { onChangeLng } = useI18nLng();
const { reset } = useForm<UserUpdateParams>({
defaultValues: userInfo as UserType
@@ -55,14 +57,7 @@ const Individuation = () => {
}))}
onchange={(val: any) => {
const lang = val;
setLngStore(lang);
router.replace(
{
query: router.query
},
router.asPath,
{ locale: lang }
);
onChangeLng(lang);
}}
/>
</Box>

View File

@@ -40,14 +40,14 @@ const Account = ({ currentTab }: { currentTab: TabEnum }) => {
const tabList = [
{
icon: 'support/user/userLight',
label: t('common:user.Personal Information'),
label: t('user:personal_information'),
value: TabEnum.info
},
...(feConfigs?.isPlus
? [
{
icon: 'support/usage/usageRecordLight',
label: t('common:user.Usage Record'),
label: t('user:usage_record'),
value: TabEnum.usage
}
]
@@ -57,7 +57,7 @@ const Account = ({ currentTab }: { currentTab: TabEnum }) => {
? [
{
icon: 'support/bill/payRecordLight',
label: t('common:support.wallet.Bills'),
label: t('user:bill_and_invoices'),
value: TabEnum.bill
}
]
@@ -67,7 +67,7 @@ const Account = ({ currentTab }: { currentTab: TabEnum }) => {
? [
{
icon: 'support/account/promotionLight',
label: t('common:user.Promotion Record'),
label: t('user:promotion_records'),
value: TabEnum.promotion
}
]
@@ -83,14 +83,14 @@ const Account = ({ currentTab }: { currentTab: TabEnum }) => {
: []),
{
icon: 'support/user/individuation',
label: t('common:support.account.Individuation'),
label: t('user:personalization'),
value: TabEnum.individuation
},
...(feConfigs.isPlus
? [
{
icon: 'support/user/informLight',
label: t('common:user.Notice'),
label: t('user:notice'),
value: TabEnum.inform
}
]
@@ -98,7 +98,7 @@ const Account = ({ currentTab }: { currentTab: TabEnum }) => {
{
icon: 'support/account/loginoutLight',
label: t('common:user.Sign Out'),
label: t('user:sign_out'),
value: TabEnum.loginout
}
];

View File

@@ -125,9 +125,14 @@ const Header = () => {
try {
localStorage.removeItem(`${appDetail._id}-past`);
localStorage.removeItem(`${appDetail._id}-future`);
router.back();
router.push({
pathname: '/app/list',
query: {
parentId: appDetail.parentId
}
});
} catch (error) {}
}, [appDetail._id, router]);
}, [appDetail._id, appDetail.parentId, router]);
const Render = useMemo(() => {
return (

View File

@@ -125,9 +125,14 @@ const Header = () => {
try {
localStorage.removeItem(`${appDetail._id}-past`);
localStorage.removeItem(`${appDetail._id}-future`);
router.back();
router.push({
pathname: '/app/list',
query: {
parentId: appDetail.parentId
}
});
} catch (error) {}
}, [appDetail._id, router]);
}, [appDetail._id, appDetail.parentId, router]);
const Render = useMemo(() => {
return (

View File

@@ -272,7 +272,7 @@ function List() {
{owner && (
<HStack spacing={1}>
<Avatar src={owner.avatar} w={'0.875rem'} borderRadius={'50%'} />
<Box maxW={'150px'} className="textEllipsis">
<Box maxW={'150px'} className="textEllipsis" fontSize={'mini'}>
{owner.memberName}
</Box>
</HStack>
@@ -288,7 +288,9 @@ function List() {
{isPc && (
<HStack spacing={1} className="time">
<MyIcon name={'history'} w={'0.85rem'} color={'myGray.400'} />
<Box color={'myGray.500'}>{formatTimeToChatTime(dataset.updateTime)}</Box>
<Box color={'myGray.500'} fontSize={'mini'}>
{formatTimeToChatTime(dataset.updateTime)}
</Box>
</HStack>
)}
{dataset.permission.hasWritePer && (

View File

@@ -43,12 +43,12 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => {
})
);
toast({
title: t('user:login.success'),
title: t('login:login_success'),
status: 'success'
});
} catch (error: any) {
toast({
title: error.message || t('user:login.error'),
title: error.message || t('login:login_failed'),
status: 'error'
});
}
@@ -61,7 +61,7 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => {
const placeholder = (() => {
if (isCommunityVersion) {
return t('common:support.user.login.Root login');
return t('login:use_root_login');
}
return [t('common:support.user.login.Username')]
.concat(
@@ -102,36 +102,36 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => {
type={'password'}
placeholder={
isCommunityVersion
? t('common:support.user.login.Root password placeholder')
? t('login:root_password_placeholder')
: t('common:support.user.login.Password')
}
{...register('password', {
required: true,
maxLength: {
value: 60,
message: t('user:login.password_condition')
message: t('login:password_condition')
}
})}
></Input>
</FormControl>
{feConfigs?.docUrl && (
<Flex alignItems={'center'} mt={7} fontSize={'sm'}>
{t('common:support.user.login.Policy tip')}
<Flex alignItems={'center'} mt={7} fontSize={'mini'}>
{t('login:policy_tip')}
<Link
ml={1}
href={getDocPath('/docs/agreement/terms/')}
target={'_blank'}
color={'primary.500'}
>
{t('common:support.user.login.Terms')}
{t('login:terms')}
</Link>
<Box mx={1}>{t('common:support.user.login.And')}</Box>
<Box mx={1}>&</Box>
<Link
href={getDocPath('/docs/agreement/privacy/')}
target={'_blank'}
color={'primary.500'}
>
{t('common:support.user.login.Privacy')}
{t('login:privacy')}
</Link>
</Flex>
)}
@@ -145,7 +145,7 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => {
isLoading={requesting}
onClick={handleSubmit(onclickLogin)}
>
{t('common:Login')}
{t('login:Login')}
</Button>
<Flex align={'center'} justifyContent={'flex-end'} color={'primary.700'}>
@@ -156,7 +156,7 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => {
onClick={() => setPageType('forgetPassword')}
fontSize="sm"
>
{t('common:support.user.login.Forget Password')}
{t('login:forget_password')}
</Box>
)}
{feConfigs?.register_method && feConfigs.register_method.length > 0 && (
@@ -168,7 +168,7 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => {
onClick={() => setPageType('register')}
fontSize="sm"
>
{t('common:support.user.login.Register')}
{t('login:register')}
</Box>
</>
)}

View File

@@ -129,7 +129,7 @@ const Login = () => {
export async function getServerSideProps(context: any) {
return {
props: { ...(await serviceSideProps(context, ['app', 'user'])) }
props: { ...(await serviceSideProps(context, ['app', 'user', 'login'])) }
};
}

View File

@@ -1,7 +1,6 @@
import { I18nNsType } from '@fastgpt/web/types/i18next';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
export const LANG_KEY = 'NEXT_LOCALE_LANG';
export enum LangEnum {
'zh' = 'zh',
'en' = 'en'
@@ -20,24 +19,3 @@ export const langMap = {
export const serviceSideProps = (content: any, ns: I18nNsType = []) => {
return serverSideTranslations(content.locale, ['common', 'error', ...ns], null, content.locales);
};
export const getLng = (lng: string) => {
return lng.split('-')[0];
};
export const change2DefaultLng = (currentLng: string) => {
if (!navigator || !localStorage) return;
if (localStorage.getItem(LANG_KEY)) return;
const userLang = navigator.language;
if (userLang.includes(currentLng)) {
return;
}
// currentLng not in userLang
return getLng(userLang);
};
export const setLngStore = (lng: string) => {
if (!localStorage) return;
localStorage.setItem(LANG_KEY, lng);
};

View File

@@ -1,17 +1,14 @@
import { useEffect, useState } from 'react';
import { clientInitData } from '@/web/common/system/staticData';
import { useTranslation } from 'next-i18next';
import { useRouter } from 'next/router';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import type { FastGPTFeConfigsType } from '@fastgpt/global/common/system/types/index.d';
import { change2DefaultLng, LangEnum, setLngStore } from '@/web/common/utils/i18n';
import { useMemoizedFn, useMount } from 'ahooks';
import { TrackEventName } from '../common/system/constants';
export const useInitApp = () => {
const router = useRouter();
const { hiId } = router.query as { hiId?: string };
const { i18n } = useTranslation();
const { loadGitStar, setInitd, feConfigs } = useSystemStore();
const [scripts, setScripts] = useState<FastGPTFeConfigsType['scripts']>([]);
const [title, setTitle] = useState(process.env.SYSTEM_NAME || 'AI');
@@ -38,20 +35,8 @@ export const useInitApp = () => {
setInitd();
});
const initUserLanguage = useMemoizedFn(() => {
// get default language
const targetLng =
change2DefaultLng(i18n.language) ||
(['zh', 'zh-CN'].includes(navigator.language) ? 'zh' : 'en');
setLngStore(targetLng);
router.replace(router.asPath, undefined, { locale: targetLng });
});
useMount(() => {
initFetch();
initUserLanguage();
const errorTrack = (event: ErrorEvent) => {
window.umami?.track(TrackEventName.windowError, {