I18n Translations (#2267)

* rebase

* i18n-1

* add error info i18n

* fix

* fix

* refactor: 删除error.json

* delete useI18n
This commit is contained in:
papapatrick
2024-08-05 18:42:21 +08:00
committed by GitHub
parent 025d405fe2
commit 10dcdb5491
107 changed files with 1128 additions and 416 deletions

View File

@@ -8,13 +8,13 @@ import QueryClientContext from '@/web/context/QueryClient';
import ChakraUIContext from '@/web/context/ChakraUI';
import I18nContextProvider from '@/web/context/I18n';
import { useInitApp } from '@/web/context/useInitApp';
import { useTranslation } from 'next-i18next';
import '@/web/styles/reset.scss';
import NextHead from '@/components/common/NextHead';
function App({ Component, pageProps }: AppProps) {
const { feConfigs, scripts, title } = useInitApp();
const { t } = useTranslation();
return (
<>
<NextHead
@@ -22,7 +22,7 @@ function App({ Component, pageProps }: AppProps) {
desc={
feConfigs?.systemDescription ||
process.env.SYSTEM_DESCRIPTION ||
`${title} 是一个大模型应用编排系统,提供开箱即用的数据处理、模型调用等能力,可以快速的构建知识库并通过 Flow 可视化进行工作流编排,实现复杂的知识库场景!`
`${title}${t('app:intro')}`
}
icon={feConfigs?.favicon || process.env.SYSTEM_FAVICON}
/>

View File

@@ -33,9 +33,10 @@ import MySelect from '@fastgpt/web/components/common/MySelect';
import MyModal from '@fastgpt/web/components/common/MyModal';
import { usePagination } from '@fastgpt/web/hooks/usePagination';
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
import { useI18n } from '@/web/context/I18n';
const BillTable = () => {
const { t } = useTranslation();
const { commonT } = useI18n();
const { toast } = useToast();
const [billType, setBillType] = useState<BillTypeEnum | ''>('');
const [billDetail, setBillDetail] = useState<BillSchemaType>();
@@ -134,7 +135,7 @@ const BillTable = () => {
<Td>
{item.createTime ? dayjs(item.createTime).format('YYYY/MM/DD HH:mm:ss') : '-'}
</Td>
<Td>{formatStorePrice2Read(item.price)}</Td>
<Td>{commonT('common:pay.yuan', { amount: formatStorePrice2Read(item.price) })}</Td>
<Td>{t(billStatusMap[item.status]?.label as any)}</Td>
<Td>
{item.status === 'NOTPAY' && (
@@ -181,7 +182,7 @@ export default BillTable;
function BillDetailModal({ bill, onClose }: { bill: BillSchemaType; onClose: () => void }) {
const { t } = useTranslation();
const { commonT } = useI18n();
return (
<MyModal
isOpen={true}
@@ -211,7 +212,7 @@ function BillDetailModal({ bill, onClose }: { bill: BillSchemaType; onClose: ()
)}
<Flex alignItems={'center'} pb={4}>
<FormLabel flex={'0 0 120px'}>{t('common:support.wallet.Amount')}:</FormLabel>
<Box>{formatStorePrice2Read(bill.price)}</Box>
<Box>{commonT('common:pay.yuan', { amount: formatStorePrice2Read(bill.price) })}</Box>
</Flex>
<Flex alignItems={'center'} pb={4}>
<FormLabel flex={'0 0 120px'}>{t('common:support.wallet.bill.Type')}:</FormLabel>

View File

@@ -435,7 +435,7 @@ const PlanUsage = () => {
<Box ml={2}>{formatTime2YMD(standardPlan?.expiredTime)}</Box>
</Flex>
<Box mt="2" color={'#485264'} fontSize="sm">
30使
{t('common:info.free_plan')}
</Box>
</>
) : (
@@ -470,9 +470,9 @@ const PlanUsage = () => {
>
<Flex>
<Flex flex={'1 0 0'} alignItems={'flex-end'}>
<Box fontSize={'md'}></Box>
<Box fontSize={'md'}>{t('common:info.resource')}</Box>
<Box fontSize={'xs'} color={'myGray.500'}>
()
{t('common:info.include')}
</Box>
</Flex>
<Link
@@ -484,7 +484,7 @@ const PlanUsage = () => {
cursor={'pointer'}
fontSize={'sm'}
>
{t('common:info.buy_extra')}
<MyIcon ml={1} name={'common/rightArrowLight'} w={'12px'} />
</Link>
</Flex>
@@ -567,7 +567,7 @@ const Other = () => {
});
reset(data);
toast({
title: '更新数据成功',
title: t('common:dataset.data.Update Success Tip'),
status: 'success'
});
},
@@ -637,7 +637,7 @@ const Other = () => {
>
<Image src="/imgs/workflow/laf.png" w={'18px'} alt="laf" />
<Box ml={2} flex={1}>
laf
{'laf' + t('common:navbar.Account')}
</Box>
<Box
w={'9px'}
@@ -664,7 +664,7 @@ const Other = () => {
>
<MyIcon name={'common/openai'} w={'18px'} color={'myGray.600'} />
<Box ml={2} flex={1}>
OpenAI/OneAPI
{'OpenAI / OneAPI' + t('common:navbar.Account')}
</Box>
<Box
w={'9px'}

View File

@@ -81,7 +81,9 @@ const InformTable = () => {
)}
</Box>
))}
{!isLoading && informs.length === 0 && <EmptyTip text={'暂无通知~'}></EmptyTip>}
{!isLoading && informs.length === 0 && (
<EmptyTip text={t('common:user.no_notice')}></EmptyTip>
)}
</Box>
{total > pageSize && (

View File

@@ -37,9 +37,7 @@ const OpenAIAccountModal = ({
>
<ModalBody>
<Box fontSize={'sm'} color={'myGray.500'}>
OpenAI/OneAPI
线使AI对话Key
Key 访GPT模型可以选择 FastAI
{t('common:info.open_api_notice')}
</Box>
<Flex alignItems={'center'} mt={5}>
<Box flex={'0 0 65px'}>API Key:</Box>
@@ -50,16 +48,16 @@ const OpenAIAccountModal = ({
<Input
flex={1}
{...register('baseUrl')}
placeholder={'请求地址,默认为 openai 官方。可填中转地址,未自动补全 "v1"'}
placeholder={t('common:info.open_api_placeholder')}
></Input>
</Flex>
</ModalBody>
<ModalFooter>
<Button mr={3} variant={'whiteBase'} onClick={onClose}>
{t('common:common.Cancel')}
</Button>
<Button isLoading={isLoading} onClick={handleSubmit((data) => onSubmit(data))}>
{t('common:common.Confirm')}
</Button>
</ModalFooter>
</MyModal>

View File

@@ -7,7 +7,6 @@ import { getErrText } from '@fastgpt/global/common/error/utils';
import { useTranslation } from 'next-i18next';
import MyModal from '@fastgpt/web/components/common/MyModal';
import { BillTypeEnum } from '@fastgpt/global/support/wallet/bill/constants';
import QRCodePayModal, { type QRPayProps } from '@/components/support/wallet/QRCodePayModal';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { EXTRA_PLAN_CARD_ROUTE } from '@/web/support/wallet/sub/constants';
@@ -27,7 +26,6 @@ const PayModal = ({
const [inputVal, setInputVal] = useState<number | undefined>(defaultValue);
const [loading, setLoading] = useState(false);
const [qrPayData, setQRPayData] = useState<QRPayProps>();
const handleClickPay = useCallback(async () => {
if (!inputVal || inputVal <= 0 || isNaN(+inputVal)) return;
setLoading(true);
@@ -79,7 +77,7 @@ const PayModal = ({
variant={item === inputVal ? 'solid' : 'outline'}
onClick={() => setInputVal(item)}
>
{item}
{t('common:pay.yuan', { amount: item })}
</Button>
))}
</Grid>
@@ -88,7 +86,7 @@ const PayModal = ({
value={inputVal}
type={'number'}
step={1}
placeholder={'其他金额,请取整数'}
placeholder={t('common:pay.other')}
onChange={(e) => {
setInputVal(Math.floor(+e.target.value));
}}
@@ -106,7 +104,7 @@ const PayModal = ({
isDisabled={!inputVal || inputVal === 0}
onClick={handleClickPay}
>
{t('common:pay.get_pay_QR')}
</Button>
</ModalFooter>

View File

@@ -105,9 +105,9 @@ const Promotion = () => {
<Table>
<Thead>
<Tr>
<Th></Th>
<Th></Th>
<Th>()</Th>
<Th>{t('common:user.Time')}</Th>
<Th>{t('common:user.type')}</Th>
<Th>{t('common:pay.amount')}</Th>
</Tr>
</Thead>
<Tbody fontSize={'sm'}>
@@ -124,7 +124,9 @@ const Promotion = () => {
</Table>
</TableContainer>
{!isLoading && promotionRecords.length === 0 && <EmptyTip text="无邀请记录~"></EmptyTip>}
{!isLoading && promotionRecords.length === 0 && (
<EmptyTip text={t('common:user.no_invite_records')}></EmptyTip>
)}
{total > pageSize && (
<Flex mt={4} justifyContent={'flex-end'}>
<Pagination />

View File

@@ -70,16 +70,16 @@ const UpdateNotificationModal = ({ onClose }: { onClose: () => void }) => {
flex={1}
bg={'myGray.50'}
{...register('account', { required: true })}
placeholder={t('common:support.user.Email Or Phone')}
placeholder={t('user:password.email_phone')}
></Input>
</Flex>
<Flex mt="6" alignItems="center" position={'relative'}>
<Box flex={'0 0 70px'}>{t('common:support.user.Verify Code')}</Box>
<Box flex={'0 0 70px'}>{t('user:password.verification_code')}</Box>
<Input
flex={1}
bg={'myGray.50'}
{...register('verifyCode', { required: true })}
placeholder={t('common:support.user.Verify Code')}
placeholder={t('user:password.code_required')}
></Input>
<Box
position={'absolute'}

View File

@@ -45,11 +45,11 @@ const UpdatePswModal = ({ onClose }: { onClose: () => void }) => {
>
<ModalBody>
<Flex alignItems={'center'}>
<Box flex={'0 0 70px'}>:</Box>
<Box flex={'0 0 70px'}>{t('common:user.old_password') + ':'}</Box>
<Input flex={1} type={'password'} {...register('oldPsw', { required: true })}></Input>
</Flex>
<Flex alignItems={'center'} mt={5}>
<Box flex={'0 0 70px'}>:</Box>
<Box flex={'0 0 70px'}>{t('common:user.new_password') + ':'}</Box>
<Input
flex={1}
type={'password'}
@@ -57,13 +57,13 @@ const UpdatePswModal = ({ onClose }: { onClose: () => void }) => {
required: true,
maxLength: {
value: 60,
message: '密码最少 4 位最多 60 位'
message: t('common:user.password_message')
}
})}
></Input>
</Flex>
<Flex alignItems={'center'} mt={5}>
<Box flex={'0 0 70px'}>:</Box>
<Box flex={'0 0 70px'}>{t('common:user.confirm_password') + ':'}</Box>
<Input
flex={1}
type={'password'}
@@ -71,7 +71,7 @@ const UpdatePswModal = ({ onClose }: { onClose: () => void }) => {
required: true,
maxLength: {
value: 60,
message: '密码最少 4 位最多 60 位'
message: t('common:user.password_message')
}
})}
></Input>
@@ -79,10 +79,10 @@ const UpdatePswModal = ({ onClose }: { onClose: () => void }) => {
</ModalBody>
<ModalFooter>
<Button mr={3} variant={'whiteBase'} onClick={onClose}>
{t('common:common.Cancel')}
</Button>
<Button isLoading={isLoading} onClick={handleSubmit((data) => onSubmit(data))}>
{t('common:common.Confirm')}
</Button>
</ModalFooter>
</MyModal>

View File

@@ -174,7 +174,7 @@ const UsageTable = () => {
<Td>{formatNumber(item.totalPoints) || 0}</Td>
<Td>
<Button size={'sm'} variant={'whitePrimary'} onClick={() => setUsageDetail(item)}>
{t('common:common.Detail')}
</Button>
</Td>
</Tr>
@@ -183,7 +183,9 @@ const UsageTable = () => {
</Table>
</TableContainer>
{!isLoading && usages.length === 0 && <EmptyTip text="无使用记录~"></EmptyTip>}
{!isLoading && usages.length === 0 && (
<EmptyTip text={t('common:user.no_usage_records')}></EmptyTip>
)}
<Loading loading={isLoading} fixed={false} />
{!!usageDetail && (

View File

@@ -77,10 +77,12 @@ const StandDetailModal = ({ onClose }: { onClose: () => void }) => {
{currentSubLevel &&
`(${t(standardSubLevelMap[currentSubLevel]?.label as any)})`}
</Td>
<Td>{datasetSize ? `${datasetSize}` : '-'}</Td>
<Td>
{datasetSize ? `${datasetSize + t('common:core.dataset.data.group')}` : '-'}
</Td>
<Td>
{totalPoints
? `${Math.round(totalPoints - surplusPoints)} / ${totalPoints} 积分`
? `${Math.round(totalPoints - surplusPoints)} / ${totalPoints} ${t('common:support.wallet.subscription.point')}`
: '-'}
</Td>
<Td>{formatTime2YMDHM(startTime)}</Td>

View File

@@ -104,7 +104,7 @@ const Account = ({ currentTab }: { currentTab: TabEnum }) => {
];
const { openConfirm, ConfirmModal } = useConfirm({
content: '确认退出登录?'
content: t('common:support.user.logout.confirm')
});
const router = useRouter();

View File

@@ -14,6 +14,7 @@ import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import { transformPreviewHistories } from '@/global/core/chat/utils';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { i18nT } from '@fastgpt/web/i18n/utils';
async function handler(
req: NextApiRequest,
res: NextApiResponse
@@ -61,7 +62,7 @@ async function handler(
return {
chatId,
appId,
title: chat?.title || '新对话',
title: chat?.title || i18nT('chat:new_chat'),
userAvatar: undefined,
variables: chat?.variables || {},
history: app.type === AppTypeEnum.plugin ? histories : transformPreviewHistories(histories),

View File

@@ -18,6 +18,7 @@ import { getAppLatestVersion } from '@fastgpt/service/core/app/controller';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { transformPreviewHistories } from '@/global/core/chat/utils';
import { i18nT } from '@fastgpt/web/i18n/utils';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
await connectToDatabase();
@@ -69,7 +70,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
data: {
chatId,
appId: app._id,
title: chat?.title || '新对话',
title: chat?.title || i18nT('chat:new_chat'),
//@ts-ignore
userAvatar: tmb?.userId?.avatar,
variables: chat?.variables || {},

View File

@@ -18,6 +18,7 @@ import { getAppLatestVersion } from '@fastgpt/service/core/app/controller';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { transformPreviewHistories } from '@/global/core/chat/utils';
import { i18nT } from '@fastgpt/web/i18n/utils';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
@@ -72,7 +73,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
data: {
chatId,
appId,
title: chat?.title || '新对话',
title: chat?.title || i18nT('chat:new_chat'),
userAvatar: team?.avatar,
variables: chat?.variables || {},
history: app.type === AppTypeEnum.plugin ? histories : transformPreviewHistories(histories),

View File

@@ -146,7 +146,7 @@ const InfoModal = ({ onClose }: { onClose: () => void }) => {
() => resumeInheritPer(appDetail._id),
// () => putAppById(appDetail._id, { inheritPermission: true }),
{
errorToast: '恢复失败',
errorToast: t('common:resume_failed'),
onSuccess: () => {
reloadApp();
}

View File

@@ -17,7 +17,6 @@ import CloseIcon from '@fastgpt/web/components/common/Icon/close';
import ChatBox from '@/components/core/chat/ChatContainer/ChatBox';
import { useSystem } from '@fastgpt/web/hooks/useSystem';
import { useQuery } from '@tanstack/react-query';
const PluginRunBox = dynamic(() => import('@/components/core/chat/ChatContainer/PluginRunBox'));
const DetailLogsModal = ({
@@ -32,7 +31,6 @@ const DetailLogsModal = ({
const { t } = useTranslation();
const { isPc } = useSystem();
const theme = useTheme();
const {
ChatBoxRef,
chatRecords,
@@ -101,7 +99,7 @@ const DetailLogsModal = ({
...(chatRecords.length > 0
? [
{ label: t('common:common.Output'), value: PluginRunBoxTabEnum.output },
{ label: '完整结果', value: PluginRunBoxTabEnum.detail }
{ label: t('common:common.all_result'), value: PluginRunBoxTabEnum.detail }
]
: [])
]}
@@ -133,7 +131,9 @@ const DetailLogsModal = ({
<>
<MyTag colorSchema="blue">
<MyIcon name={'history'} w={'14px'} />
<Box ml={1}>{`${chatRecords.length}条记录`}</Box>
<Box ml={1}>
{t('common:core.chat.History Amount', { amount: chatRecords.length })}
</Box>
</MyTag>
{!!chatModels && (
<MyTag ml={2} colorSchema={'green'}>

View File

@@ -122,7 +122,7 @@ const Logs = () => {
key={item._id}
_hover={{ bg: 'myWhite.600' }}
cursor={'pointer'}
title={'点击查看对话详情'}
title={t('common:core.view_chat_detail')}
onClick={() => setDetailLogsId(item.id)}
>
<Td>

View File

@@ -13,6 +13,7 @@ import dynamic from 'next/dynamic';
import { cloneDeep } from 'lodash';
import Flow from '../WorkflowComponents/Flow';
import { t } from 'i18next';
const Logs = dynamic(() => import('../Logs/index'));
const PublishChannel = dynamic(() => import('../Publish'));
@@ -22,8 +23,7 @@ const WorkflowEdit = () => {
const { openConfirm, ConfirmModal } = useConfirm({
showCancel: false,
content:
'检测到您的高级编排为旧版,系统将为您自动格式化成新版工作流。\n\n由于版本差异较大会导致一些工作流无法正常排布请重新手动连接工作流。如仍异常可尝试删除对应节点后重新添加。\n\n你可以直接点击调试进行工作流测试调试完毕后点击发布。直到你点击发布新工作流才会真正保存生效。\n\n在你发布新工作流前自动保存不会生效。'
content: t('common:info.old_version_attention')
});
const initData = useContextSelector(WorkflowContext, (v) => v.initData);

View File

@@ -123,8 +123,7 @@ const FeiShuEditModal = ({
</Flex>
<Flex alignItems={'center'} mt={4}>
<Flex flex={'0 0 90px'} alignItems={'center'}>
{/* TODO: i18n */}
{t('common:default_reply')}
</Flex>
<Input
placeholder={publishT('default_response') || 'link_name'}
@@ -136,8 +135,7 @@ const FeiShuEditModal = ({
</Flex>
<Flex alignItems={'center'} mt={4}>
<Flex flex={'0 0 90px'} alignItems={'center'}>
{/* TODO: i18n */}
{t('common:reply_now')}
</Flex>
<Input
placeholder={publishT('default_response') || 'link_name'}

View File

@@ -13,6 +13,7 @@ import dynamic from 'next/dynamic';
import { cloneDeep } from 'lodash';
import Flow from '../WorkflowComponents/Flow';
import { t } from 'i18next';
const Logs = dynamic(() => import('../Logs/index'));
const PublishChannel = dynamic(() => import('../Publish'));
@@ -22,8 +23,7 @@ const WorkflowEdit = () => {
const { openConfirm, ConfirmModal } = useConfirm({
showCancel: false,
content:
'检测到您的高级编排为旧版,系统将为您自动格式化成新版工作流。\n\n由于版本差异较大会导致一些工作流无法正常排布请重新手动连接工作流。如仍异常可尝试删除对应节点后重新添加。\n\n你可以直接点击调试进行工作流测试调试完毕后点击发布。直到你点击发布新工作流才会真正保存生效。\n\n在你发布新工作流前自动保存不会生效。'
content: t('common:info.old_version_attention')
});
const initData = useContextSelector(WorkflowContext, (v) => v.initData);

View File

@@ -77,7 +77,7 @@ const ChatTest = ({
...(chatRecords.length > 0
? [
{ label: t('common:common.Output'), value: PluginRunBoxTabEnum.output },
{ label: '完整结果', value: PluginRunBoxTabEnum.detail }
{ label: t('common:common.all_result'), value: PluginRunBoxTabEnum.detail }
]
: [])
]}

View File

@@ -5,7 +5,7 @@ import { useToast } from '@fastgpt/web/hooks/useToast';
import { useContextSelector } from 'use-context-selector';
import { WorkflowContext } from '../context';
import { useI18n } from '@/web/context/I18n';
import { useTranslation } from 'next-i18next';
type Props = {
onClose: () => void;
};
@@ -15,7 +15,7 @@ const ImportSettings = ({ onClose }: Props) => {
const { toast } = useToast();
const initData = useContextSelector(WorkflowContext, (v) => v.initData);
const [value, setValue] = useState('');
const { t } = useTranslation();
return (
<MyModal
isOpen
@@ -50,7 +50,7 @@ const ImportSettings = ({ onClose }: Props) => {
}
}}
>
{t('common:common.Confirm')}
</Button>
</ModalFooter>
</MyModal>

View File

@@ -263,7 +263,7 @@ const NodeTemplatesModal = ({ isOpen, onClose }: ModuleTemplateListProps) => {
onClick={() => router.push('/app/list')}
gap={1}
>
<Box></Box>
<Box>{t('common:create')}</Box>
<MyIcon name={'common/rightArrowLight'} w={'0.8rem'} />
</Flex>
)}
@@ -279,7 +279,7 @@ const NodeTemplatesModal = ({ isOpen, onClose }: ModuleTemplateListProps) => {
onClick={() => window.open(feConfigs.systemPluginCourseUrl)}
gap={1}
>
<Box></Box>
<Box>{t('common:plugin.contribute')}</Box>
<MyIcon name={'common/rightArrowLight'} w={'0.8rem'} />
</Flex>
)}

View File

@@ -47,7 +47,7 @@ const SelectAppModal = ({
return (
<MyModal
isOpen
title={`选择应用`}
title={t('common:core.module.Select app')}
iconSrc="/imgs/workflow/ai.svg"
onClose={onClose}
position={'relative'}

View File

@@ -261,7 +261,7 @@ export const useDebug = () => {
})}
</Box>
<Flex py={2} justifyContent={'flex-end'} px={6}>
<Button onClick={handleSubmit(onClickRun)}></Button>
<Button onClick={handleSubmit(onClickRun)}>{t('common:common.Run')}</Button>
</Flex>
</MyRightDrawer>
);

View File

@@ -25,6 +25,7 @@ import { connectionLineStyle, defaultEdgeOptions } from '../constants';
import { useContextSelector } from 'use-context-selector';
import { WorkflowContext } from '../context';
import { useWorkflow } from './hooks/useWorkflow';
import { t } from 'i18next';
const NodeSimple = dynamic(() => import('./nodes/NodeSimple'));
const nodeTypes: Record<FlowNodeTypeEnum, any> = {
@@ -171,7 +172,7 @@ const FlowController = React.memo(function FlowController() {
showInteractive={false}
showFitView={false}
>
<MyTooltip label={'页面居中'}>
<MyTooltip label={t('common:common.page_center')}>
<ControlButton className="custom-workflow-fix_view" onClick={() => fitView()}>
<MyIcon name={'core/modules/fixview'} w={'14px'} />
</ControlButton>

View File

@@ -64,7 +64,7 @@ const NodeCQNode = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
/>
</MyTooltip>
<Box flex={1} color={'myGray.600'} fontWeight={'medium'}>
{i + 1}
{t('common:classification') + (i + 1)}
</Box>
</Flex>
<Box position={'relative'}>

View File

@@ -203,7 +203,10 @@ function Reference({
/>
</Flex>
<ReferSelector
placeholder={t((inputChildren.referencePlaceholder as any) || '选择知识库引用')}
placeholder={t(
(inputChildren.referencePlaceholder as any) ||
t('common:core.module.Dataset quote.select')
)}
list={referenceList}
value={formatValue}
onSelect={onSelect}

View File

@@ -79,10 +79,10 @@ const NodeExtract = ({ data }: NodeProps<FlowNodeItemType>) => {
<Thead>
<Tr>
<Th bg={'myGray.50'} borderRadius={'none !important'}>
{t('common:item_name')}
</Th>
<Th bg={'myGray.50'}></Th>
<Th bg={'myGray.50'}></Th>
<Th bg={'myGray.50'}>{t('common:item_description')}</Th>
<Th bg={'myGray.50'}>{t('common:required')}</Th>
<Th bg={'myGray.50'} borderRadius={'none !important'}></Th>
</Tr>
</Thead>
@@ -197,7 +197,7 @@ const NodeExtract = ({ data }: NodeProps<FlowNodeItemType>) => {
const newOutput: FlowNodeOutputItemType = {
id: getNanoid(),
key: data.key,
label: `提取结果-${data.desc}`,
label: `${t('common:extraction_results')}-${data.desc}`,
valueType: WorkflowIOValueTypeEnum.string,
type: FlowNodeOutputTypeEnum.static
};
@@ -215,7 +215,7 @@ const NodeExtract = ({ data }: NodeProps<FlowNodeItemType>) => {
key: data.key,
value: {
...output,
label: `提取结果-${data.desc}`
label: `${t('common:extraction_results')}-${data.desc}`
}
});
} else {

View File

@@ -392,7 +392,7 @@ const ConditionSelect = ({
list={filterQuiredConditionList}
value={condition}
onchange={onSelect}
placeholder="选择条件"
placeholder={t('common:chose_condition')}
/>
);
};

View File

@@ -92,7 +92,7 @@ const NodeLaf = (props: NodeProps<FlowNodeItemType>) => {
onError(err) {
toast({
status: 'error',
title: getErrText(err, '获取Laf函数列表失败')
title: getErrText(err, t('common:get_laf_failed'))
});
}
}
@@ -298,7 +298,7 @@ const ConfigLaf = () => {
)}
</Center>
) : (
<Box>Laf环境</Box>
<Box>{t('common:no_laf_env')}</Box>
);
};

View File

@@ -443,7 +443,7 @@ const NodeIntro = React.memo(function NodeIntro({
// edit intro
const { onOpenModal: onOpenIntroModal, EditModal: EditIntroModal } = useEditTextarea({
title: t('common:core.module.Edit intro'),
tip: '调整该模块会对工具调用时机有影响。\n你可以通过精确的描述该模块功能引导模型进行工具调用。',
tip: t('common:info.node_info'),
canEmpty: false
});

View File

@@ -85,7 +85,7 @@ const EditFieldModal = ({
);
return (
<MyModal isOpen iconSrc="modal/edit" title={'工具字段参数配置'} onClose={onClose}>
<MyModal isOpen iconSrc="modal/edit" title={t('common:tool_field')} onClose={onClose}>
<ModalBody>
<Flex alignItems={'center'} mb={5}>
<Box flex={'0 0 80px'}>{t('common:common.Require Input')}</Box>
@@ -111,7 +111,7 @@ const EditFieldModal = ({
required: true,
pattern: {
value: /^[a-zA-Z]+[0-9]*$/,
message: '字段key必须是纯英文字母或数字并且不能以数字开头。'
message: t('common:info.felid_message')
}
})}
/>

View File

@@ -64,9 +64,9 @@ const RenderToolInput = ({
<Table bg={'white'}>
<Thead>
<Tr>
<Th></Th>
<Th></Th>
<Th></Th>
<Th>{t('common:item_name')}</Th>
<Th>{t('common:item_description')}</Th>
<Th>{t('common:required')}</Th>
{dynamicInput && <Th></Th>}
</Tr>
</Thead>

View File

@@ -387,7 +387,7 @@ const WorkflowContextProvider = ({
if (input) {
toast({
status: 'warning',
title: 'key 重复'
title: t('common:key_repetition')
});
} else {
updateObj.inputs.push(props.value);
@@ -408,7 +408,7 @@ const WorkflowContextProvider = ({
if (output) {
toast({
status: 'warning',
title: 'key 重复'
title: t('common:key_repetition')
});
updateObj.outputs = node.data.outputs;
} else {

View File

@@ -1,5 +1,5 @@
import { BoxProps, FlexProps } from '@chakra-ui/react';
import { i18nT } from '@fastgpt/web/i18n/utils';
export const cardStyles: BoxProps = {
borderRadius: 'lg',
// overflow: 'hidden',
@@ -20,11 +20,11 @@ export const workflowBoxStyles: FlexProps = {
export const publishStatusStyle = {
unPublish: {
colorSchema: 'adora' as any,
text: '未发布'
text: i18nT('common:core.app.have_publish')
},
published: {
colorSchema: 'green' as any,
text: '已发布'
text: i18nT('common:core.app.not_published')
}
};

View File

@@ -215,7 +215,7 @@ const ListItem = () => {
fontSize={'xs'}
color={'myGray.500'}
>
<Box className={'textEllipsis2'}>{app.intro || '还没写介绍~'}</Box>
<Box className={'textEllipsis2'}>{app.intro || t('common:common.no_intro')}</Box>
</Box>
<Flex
h={'24px'}
@@ -295,7 +295,7 @@ const ListItem = () => {
children: [
{
icon: 'edit',
label: '编辑信息',
label: t('common:dataset.Edit Info'),
onClick: () => {
if (app.type === AppTypeEnum.httpPlugin) {
setEditHttpPlugin({
@@ -383,14 +383,14 @@ const ListItem = () => {
})}
</Grid>
{myApps.length === 0 && <EmptyTip text={'还没有应用,快去创建一个吧!'} pt={'30vh'} />}
{myApps.length === 0 && <EmptyTip text={t('common:core.app.no_app')} pt={'30vh'} />}
<DelConfirmModal />
<ConfirmCopyModal />
{!!editedApp && (
<EditResourceModal
{...editedApp}
title="应用信息编辑"
title={t('common:core.app.edit_content')}
onClose={() => {
setEditedApp(undefined);
}}

View File

@@ -31,7 +31,7 @@ const CustomPluginRunBox = (props: PluginRunBoxProps) => {
<LightRowTabs<PluginRunBoxTabEnum>
list={[
{ label: t('common:common.Output'), value: PluginRunBoxTabEnum.output },
{ label: '完整结果', value: PluginRunBoxTabEnum.detail }
{ label: t('common:common.all_result'), value: PluginRunBoxTabEnum.detail }
]}
value={tab}
onChange={setTab}
@@ -52,7 +52,7 @@ const CustomPluginRunBox = (props: PluginRunBoxProps) => {
list={[
{ label: t('common:common.Input'), value: PluginRunBoxTabEnum.input },
{ label: t('common:common.Output'), value: PluginRunBoxTabEnum.output },
{ label: '完整结果', value: PluginRunBoxTabEnum.detail }
{ label: t('common:common.all_result'), value: PluginRunBoxTabEnum.detail }
]}
value={tab}
onChange={setTab}

View File

@@ -287,7 +287,7 @@ const FileSelector = ({
{isMaxSelected ? (
<>
<Box color={'myGray.500'} fontSize={'xs'}>
{t('file:reached_max_file_count')}
</Box>
</>
) : (

View File

@@ -243,7 +243,7 @@ const Test = ({ datasetId }: { datasetId: string }) => {
</Box>
</Flex>
<Box mt={3} fontSize={'sm'}>
CSV 100
{t('common:info.csv_message')}
<Box
as={'span'}
color={'primary.600'}
@@ -256,7 +256,7 @@ const Test = ({ datasetId }: { datasetId: string }) => {
});
}}
>
{t('common:info.csv_download')}
</Box>
</Box>
</Box>

View File

@@ -154,7 +154,9 @@ function List() {
label={
<Flex flexDirection={'column'} alignItems={'center'}>
<Box fontSize={'xs'} color={'myGray.500'}>
{dataset.type === DatasetTypeEnum.folder ? '打开文件夹' : '打开知识库'}
{dataset.type === DatasetTypeEnum.folder
? t('common.folder.Open folder')
: t('common.folder.open_dataset')}
</Box>
</Flex>
}

View File

@@ -7,7 +7,7 @@ import { useSendCode } from '@/web/support/user/hooks/useSendCode';
import type { ResLogin } from '@/global/support/api/userRes.d';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useTranslation } from 'next-i18next';
interface Props {
setPageType: Dispatch<`${LoginPageTypeEnum}`>;
loginSuccess: (e: ResLogin) => void;
@@ -22,6 +22,7 @@ interface RegisterType {
const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
const { toast } = useToast();
const { t } = useTranslation();
const { feConfigs } = useSystemStore();
const {
register,
@@ -58,12 +59,12 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
})
);
toast({
title: `密码已找回`,
title: t('user:password.retrieved'),
status: 'success'
});
} catch (error: any) {
toast({
title: error.message || '修改密码异常',
title: error.message || t('user:password.change_error'),
status: 'error'
});
}
@@ -75,7 +76,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
return (
<>
<Box fontWeight={'bold'} fontSize={'2xl'} textAlign={'center'}>
{feConfigs?.systemTitle}
{t('user:password.retrieved_account', { account: feConfigs?.systemTitle })}
</Box>
<Box
mt={'42px'}
@@ -88,13 +89,13 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
<FormControl isInvalid={!!errors.username}>
<Input
bg={'myGray.50'}
placeholder="邮箱/手机号"
placeholder={t('user:password.email_phone')}
{...register('username', {
required: '邮箱/手机号不能为空',
required: t('user:password.email_phone_void'),
pattern: {
value:
/(^1[3456789]\d{9}$)|(^[A-Za-z0-9]+([_\.][A-Za-z0-9]+)*@([A-Za-z0-9\-]+\.)+[A-Za-z]{2,6}$)/,
message: '邮箱/手机号格式错误'
message: t('user:password.email_phone_error')
}
})}
></Input>
@@ -110,9 +111,9 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
bg={'myGray.50'}
flex={1}
maxLength={8}
placeholder="验证码"
placeholder={t('user:password.verification_code')}
{...register('code', {
required: '验证码不能为空'
required: t('user:password.code_required')
})}
></Input>
<Box
@@ -137,16 +138,16 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
<Input
bg={'myGray.50'}
type={'password'}
placeholder="新密码(4~20位)"
placeholder={t('user:password.new_password')}
{...register('password', {
required: '密码不能为空',
required: t('user:password.password_required'),
minLength: {
value: 4,
message: '密码最少 4 位最多 20 位'
message: t('user:password.password_condition')
},
maxLength: {
value: 20,
message: '密码最少 4 位最多 20 位'
message: t('user:password.password_condition')
}
})}
></Input>
@@ -155,9 +156,10 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
<Input
bg={'myGray.50'}
type={'password'}
placeholder="确认密码"
placeholder={t('user:password.confirm')}
{...register('password2', {
validate: (val) => (getValues('password') === val ? true : '两次密码不一致')
validate: (val) =>
getValues('password') === val ? true : t('user:password.not_match')
})}
></Input>
</FormControl>
@@ -171,7 +173,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
isLoading={requesting}
onClick={handleSubmit(onclickFindPassword)}
>
{t('user:password.retrieve')}
</Button>
<Box
float={'right'}
@@ -183,7 +185,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
_hover={{ textDecoration: 'underline' }}
onClick={() => setPageType(LoginPageTypeEnum.passwordLogin)}
>
{t('user:password.to_login')}
</Box>
</Box>
</>

View File

@@ -43,12 +43,12 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => {
})
);
toast({
title: '登录成功',
title: t('user:login.success'),
status: 'success'
});
} catch (error: any) {
toast({
title: error.message || '登录异常',
title: error.message || t('user:login.error'),
status: 'error'
});
}
@@ -101,7 +101,7 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => {
required: true,
maxLength: {
value: 60,
message: '密码最多 60 位'
message: t('user:login.password_condition')
}
})}
></Input>

View File

@@ -24,7 +24,7 @@ const WechatForm = ({ setPageType, loginSuccess }: Props) => {
onError(err) {
toast({
status: 'warning',
title: getErrText(err, '获取二维码失败')
title: getErrText(err, t('common:get_QR_failed'))
});
}
});

View File

@@ -10,7 +10,6 @@ import { postCreateApp } from '@/web/core/app/api';
import { defaultAppTemplates } from '@/web/core/app/templates';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useTranslation } from 'next-i18next';
interface Props {
loginSuccess: (e: ResLogin) => void;
setPageType: Dispatch<`${LoginPageTypeEnum}`>;
@@ -26,6 +25,7 @@ interface RegisterType {
const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
const { toast } = useToast();
const { t } = useTranslation();
const { feConfigs } = useSystemStore();
const {
register,
@@ -63,7 +63,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
})
);
toast({
title: `注册成功`,
title: t('user:register.success'),
status: 'success'
});
// auto register template app
@@ -80,7 +80,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
}, 100);
} catch (error: any) {
toast({
title: error.message || '注册异常',
title: error.message || t('user:register.error'),
status: 'error'
});
}
@@ -92,7 +92,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
return (
<>
<Box fontWeight={'bold'} fontSize={'2xl'} textAlign={'center'}>
{feConfigs?.systemTitle}
{t('user:register.register_account', { account: feConfigs?.systemTitle })}
</Box>
<Box
mt={'42px'}
@@ -105,13 +105,13 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
<FormControl isInvalid={!!errors.username}>
<Input
bg={'myGray.50'}
placeholder="邮箱/手机号"
placeholder={t('user:password.email_phone')}
{...register('username', {
required: '邮箱/手机号不能为空',
required: t('user:password.email_phone_void'),
pattern: {
value:
/(^1[3456789]\d{9}$)|(^[A-Za-z0-9]+([_\.][A-Za-z0-9]+)*@([A-Za-z0-9\-]+\.)+[A-Za-z]{2,6}$)/,
message: '邮箱/手机号格式错误'
message: t('user:password.email_phone_error')
}
})}
></Input>
@@ -127,9 +127,9 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
bg={'myGray.50'}
flex={1}
maxLength={8}
placeholder="验证码"
placeholder={t('user:password.verification_code')}
{...register('code', {
required: '验证码不能为空'
required: t('user:password.code_required')
})}
></Input>
<Box
@@ -154,16 +154,16 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
<Input
bg={'myGray.50'}
type={'password'}
placeholder="密码(4~20位)"
placeholder={t('user:password.new_password')}
{...register('password', {
required: '密码不能为空',
required: t('user:password.password_required'),
minLength: {
value: 4,
message: '密码最少 4 位最多 20 位'
message: t('user:password.password_condition')
},
maxLength: {
value: 20,
message: '密码最少 4 位最多 20 位'
message: t('user:password.password_condition')
}
})}
></Input>
@@ -172,9 +172,10 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
<Input
bg={'myGray.50'}
type={'password'}
placeholder="确认密码"
placeholder={t('user:password.confirm')}
{...register('password2', {
validate: (val) => (getValues('password') === val ? true : '两次密码不一致')
validate: (val) =>
getValues('password') === val ? true : t('user:password.not_match')
})}
></Input>
</FormControl>
@@ -187,7 +188,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
isLoading={requesting}
onClick={handleSubmit(onclickRegister)}
>
{t('user:register.confirm')}
</Button>
<Box
float={'right'}
@@ -199,7 +200,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
_hover={{ textDecoration: 'underline' }}
onClick={() => setPageType(LoginPageTypeEnum.passwordLogin)}
>
{t('user:register.to_login')}
</Box>
</Box>
</>

View File

@@ -9,7 +9,7 @@ import { useToast } from '@fastgpt/web/hooks/useToast';
import Loading from '@fastgpt/web/components/common/MyLoading';
import { serviceSideProps } from '@/web/common/utils/i18n';
import { getErrText } from '@fastgpt/global/common/error/utils';
import { useTranslation } from 'next-i18next';
const FastLogin = ({
code,
token,
@@ -23,7 +23,7 @@ const FastLogin = ({
const { setUserInfo } = useUserStore();
const router = useRouter();
const { toast } = useToast();
const { t } = useTranslation();
const loginSuccess = useCallback(
(res: ResLogin) => {
setToken(res.token);
@@ -50,7 +50,7 @@ const FastLogin = ({
if (!res) {
toast({
status: 'warning',
title: '登录异常'
title: t('common:support.user.login.error')
});
return setTimeout(() => {
router.replace('/login');
@@ -60,7 +60,7 @@ const FastLogin = ({
} catch (error) {
toast({
status: 'warning',
title: getErrText(error, '登录异常')
title: getErrText(error, t('common:support.user.login.error'))
});
setTimeout(() => {
router.replace('/login');

View File

@@ -13,6 +13,7 @@ import { clearToken, setToken } from '@/web/support/user/auth';
import Script from 'next/script';
import Loading from '@fastgpt/web/components/common/MyLoading';
import { useMount } from 'ahooks';
import { t } from 'i18next';
const RegisterForm = dynamic(() => import('./components/RegisterForm'));
const ForgetPasswordForm = dynamic(() => import('./components/ForgetPasswordForm'));
@@ -115,7 +116,7 @@ const Login = () => {
textAlign={'center'}
onClick={onOpen}
>
{t('common:support.user.login.can_not_login')}
</Box>
)}
</Flex>

View File

@@ -57,7 +57,7 @@ const provider = () => {
if (!res) {
toast({
status: 'warning',
title: '登录异常'
title: t('common:support.user.login.error')
});
return setTimeout(() => {
router.replace('/login');
@@ -67,7 +67,7 @@ const provider = () => {
} catch (error) {
toast({
status: 'warning',
title: getErrText(error, '登录异常')
title: getErrText(error, t('common:support.user.login.error'))
});
setTimeout(() => {
router.replace('/login');
@@ -95,7 +95,7 @@ const provider = () => {
if (state !== loginStore?.state) {
toast({
status: 'warning',
title: '安全校验失败'
title: t('common:support.user.login.security_failed')
});
setTimeout(() => {
router.replace('/login');

View File

@@ -46,7 +46,7 @@ const ExtraPlan = () => {
if (datasetSizePayAmount === 0) {
return toast({
status: 'warning',
title: '购买数量不能为0'
title: t('common:support.wallet.amount_0')
});
}
setLoading(true);
@@ -91,7 +91,7 @@ const ExtraPlan = () => {
if (payAmount === 0) {
return toast({
status: 'warning',
title: '购买数量不能为0'
title: t('common:support.wallet.amount_0')
});
}
setLoading(true);
@@ -147,7 +147,7 @@ const ExtraPlan = () => {
{t('common:support.wallet.subscription.Extra dataset size')}
</Box>
<Box mt={3} fontSize={['28px', '32px']} fontWeight={'bold'}>
{extraDatasetPrice}/1000{' '}
{`${extraDatasetPrice}/1000` + t('common:core.dataset.data.group')}
<Box ml={1} as={'span'} fontSize={'md'} color={'myGray.600'} fontWeight={'normal'}>
/{t('common:common.month')}
</Box>
@@ -164,7 +164,7 @@ const ExtraPlan = () => {
<Box h={'120px'} w={'100%'}>
<Flex mt={4}>
<MyIcon mr={2} name={'support/bill/shoppingCart'} w={'16px'} color={'primary.600'} />
{t('common:support.wallet.buy_resource')}
</Flex>
<Flex mt={4} alignItems={'center'}>
<Box flex={['0 0 100px', '1 0 0']}>
@@ -252,7 +252,7 @@ const ExtraPlan = () => {
{t('common:support.wallet.subscription.Extra ai points')}
</Box>
<Box mt={3} fontSize={['28px', '32px']} fontWeight={'bold'}>
{extraPointsPrice}/1000{' '}
{`${extraDatasetPrice}/1000` + t('common:support.wallet.subscription.point')}
<Box ml={1} as={'span'} fontSize={'md'} color={'myGray.600'} fontWeight={'normal'}>
/{t('common:common.month')}
</Box>
@@ -269,7 +269,7 @@ const ExtraPlan = () => {
<Box h={'120px'} w={'100%'}>
<Flex mt={4}>
<MyIcon mr={2} name={'support/bill/shoppingCart'} w={'16px'} color={'primary.600'} />
{t('common:support.wallet.buy_resource')}
</Flex>
{/* <Flex mt={4} alignItems={'center'}>
<Box flex={['0 0 100px', '1 0 0']}>
@@ -325,7 +325,7 @@ const ExtraPlan = () => {
</NumberInputStepper>
</NumberInput>
<Box position={'absolute'} right={'20px'} color={'myGray.500'} fontSize={'xs'}>
000
{'000' + t('common:support.wallet.subscription.point')}
</Box>
</Flex>
</Flex>

View File

@@ -6,36 +6,36 @@ const FAQ = () => {
const { t } = useTranslation();
const faqs = [
{
title: '订阅套餐会自动续费么?',
desc: '当前套餐过期后,系统会自动根据“未来套餐”进行续费,系统会尝试从账户余额进行扣费,如果您需要自动续费,请在账户余额中预留额度。'
title: t('common:FAQ.auto_renew_q'),
desc: t('common:FAQ.auto_renew_a')
},
{
title: '能否切换订阅套餐?',
desc: '当前套餐价格大于新套餐时,无法立即切换,将会在当前套餐过期后以“续费”形式进行切换。\n当前套餐价格小于新套餐时系统会自动计算当前套餐剩余余额您可支付差价进行套餐切换。'
title: t('common:FAQ.change_package_q'),
desc: t('common:FAQ.change_package_a')
},
{
title: '什么是AI积分',
desc: '每次调用AI模型时都会消耗一定的AI积分。具体的计算标准可参考上方的“AI 积分计算标准”。\nToken计算采用GPT3.5相同公式1Token≈0.7中文字符≈0.9英文单词连续出现的字符可能被认为是1个Tokens。'
title: t('common:FAQ.ai_point_q'),
desc: t('common:FAQ.ai_point_a')
},
{
title: 'AI积分会过期么',
desc: '会过期。当前套餐过期后AI积分将会清空并更新为新套餐的AI积分。年度套餐的AI积分时长为1年而不是每个月。'
title: t('common:FAQ.ai_point_expire_q'),
desc: t('common:FAQ.ai_point_expire_a')
},
{
title: '知识库存储怎么计算?',
desc: '1条知识库存储等于1条知识库索引。一条知识库数据可以包含1条或多条知识库索引。增强训练中1条数据会生成5条索引。'
title: t('common:FAQ.dataset_compute_q'),
desc: t('common:FAQ.dataset_compute_a')
},
{
title: '知识库索引超出会删除么?',
desc: '不会。但知识库索引超出时,无法插入和更新知识库内容。'
title: t('common:FAQ.dataset_index_q'),
desc: t('common:FAQ.dataset_index_a')
},
{
title: '额外资源包可以叠加么?',
desc: '可以的。每次购买的资源包都是独立的在其有效期内将会叠加使用。AI积分会优先扣除最先过期的资源包。'
title: t('common:FAQ.package_overlay_q'),
desc: t('common:FAQ.package_overlay_a')
},
{
title: '免费版数据会清除么?',
desc: '免费版用户免费版且未购买额外套餐30天无使用记录后系统会自动清除账号下所有知识库内容。'
title: t('common:FAQ.free_user_clean_q'),
desc: t('common:FAQ.free_user_clean_a')
}
];

View File

@@ -1,7 +1,6 @@
import React from 'react';
import { Box, Flex, Grid, Link } from '@chakra-ui/react';
import { useTranslation } from 'next-i18next';
import { useSystemStore } from '@/web/common/system/useSystemStore';
const Points = () => {
@@ -19,7 +18,7 @@ const Points = () => {
{t('common:support.wallet.subscription.Ai points')}
</Box>
<Link href="https://tiktokenizer.vercel.app/" target="_blank">
线 Tokens
{t('common:support.wallet.subscription.token_compute')}
</Link>
<Grid gap={6} mt={['30px', '40px']} w={'100%'}>
<Box
@@ -39,13 +38,17 @@ const Points = () => {
fontSize={'md'}
fontWeight={'bold'}
>
AI语言模型
{t('common:support.wallet.subscription.ai_model')}
</Box>
<Box flex={4} textAlign={'center'}>
{llmModelList?.map((item, i) => (
<Flex key={item.model} py={4} bg={i % 2 !== 0 ? 'myGray.50' : ''}>
<Box flex={'1 0 0'}>{item.name}</Box>
<Box flex={'1 0 0'}>{item.charsPointsPrice} / 1000 Tokens</Box>
<Box flex={'1 0 0'}>
{item.charsPointsPrice +
t('common:support.wallet.subscription.point') +
' / 1000 Tokens'}
</Box>
</Flex>
))}
</Box>
@@ -60,17 +63,21 @@ const Points = () => {
>
<Box flex={1} borderRightWidth={'1px'} borderRightColor={'myGray.150'} py={4} px={6}>
<Box fontSize={'md'} fontWeight={'bold'}>
{t('common:core.ai.model.Vector Model')}
</Box>
<Box fontSize={'sm'} mt={1} color={'myGray.500'}>
&
{t('common:core.ai.model.doc_index_and_dialog')}
</Box>
</Box>
<Box flex={4} textAlign={'center'}>
{vectorModelList?.map((item, i) => (
<Flex key={item.model} py={4} bg={i % 2 !== 0 ? 'myGray.50' : ''}>
<Box flex={'1 0 0'}>{item.name}</Box>
<Box flex={'1 0 0'}>{item.charsPointsPrice} / 1000 Tokens</Box>
<Box flex={'1 0 0'}>
{item.charsPointsPrice +
t('common:support.wallet.subscription.point') +
' / 1000 Tokens'}
</Box>
</Flex>
))}
</Box>
@@ -85,14 +92,19 @@ const Points = () => {
>
<Box flex={1} borderRightWidth={'1px'} borderRightColor={'myGray.150'} py={4} px={6}>
<Box fontSize={'md'} fontWeight={'bold'}>
{t('common:core.app.TTS')}
</Box>
</Box>
<Box flex={4} textAlign={'center'}>
{audioSpeechModelList?.map((item, i) => (
<Flex key={item.model} py={4} bg={i % 2 !== 0 ? 'myGray.50' : ''}>
<Box flex={'1 0 0'}>{item.name}</Box>
<Box flex={'1 0 0'}>{item.charsPointsPrice} / 1000</Box>
<Box flex={'1 0 0'}>
{item.charsPointsPrice +
t('common:support.wallet.subscription.point') +
' / 1000' +
t('common:unit.character')}
</Box>
</Flex>
))}
</Box>
@@ -107,13 +119,18 @@ const Points = () => {
>
<Box flex={1} borderRightWidth={'1px'} borderRightColor={'myGray.150'} py={4} px={6}>
<Box fontSize={'md'} fontWeight={'bold'}>
{t('common:core.app.Whisper')}
</Box>
</Box>
<Box flex={4} textAlign={'center'} h={'100%'}>
<Flex py={4}>
<Box flex={'1 0 0'}>{whisperModel?.name}</Box>
<Box flex={'1 0 0'}>{whisperModel?.charsPointsPrice} / </Box>
<Box flex={'1 0 0'}>
{whisperModel?.charsPointsPrice +
t('common:support.wallet.subscription.point') +
' / 1000' +
t('common:unit.minute')}
</Box>
</Flex>
</Box>
</Box>

View File

@@ -309,6 +309,7 @@ const ConfirmPayModal = ({
onConfirmPay
}: ConfirmPayModalProps & { onClose: () => void; onConfirmPay: () => void }) => {
const { t } = useTranslation();
const [qrPayData, setQRPayData] = useState<QRPayProps>();
const formatPayPrice = Math.ceil(formatStorePrice2Read(payPrice));
@@ -340,16 +341,24 @@ const ConfirmPayModal = ({
>
<ModalBody py={5} px={9}>
<Flex>
<Box flex={'0 0 100px'}></Box>
<Box>{formatStorePrice2Read(totalPrice)}</Box>
<Box flex={'0 0 100px'}>{t('common:pay.new_package_price')}</Box>
<Box>{t('common:pay.yuan', { amount: formatStorePrice2Read(totalPrice) })}</Box>
</Flex>
<Flex mt={6}>
<Box flex={'0 0 100px'}></Box>
<Box>{Math.floor(formatStorePrice2Read(totalPrice - payPrice))}</Box>
<Box flex={'0 0 100px'}>{t('common:pay.old_package_price')}</Box>
<Box>
{t('common:pay.yuan', {
amount: Math.floor(formatStorePrice2Read(totalPrice - payPrice))
})}
</Box>
</Flex>
<Flex mt={6}>
<Box flex={'0 0 100px'}></Box>
<Box>{formatPayPrice}</Box>
<Box flex={'0 0 100px'}>{t('common:pay.balance_notice')}</Box>
<Box>
{t('common:pay.yuan', {
amount: formatPayPrice
})}
</Box>
</Flex>
</ModalBody>
<ModalFooter
@@ -359,13 +368,15 @@ const ConfirmPayModal = ({
justifyContent={'flex-start'}
px={0}
>
<Box>: </Box>
<Box>{t('common:pay.balance') + ': '}</Box>
<Box ml={2} flex={1}>
{formatTeamBalance}
{t('common:pay.yuan', {
amount: formatTeamBalance
})}
</Box>
{teamBalance >= payPrice ? (
<Button size={'sm'} onClick={onConfirmPay}>
{t('common:pay.confirm_pay')}
</Button>
) : (
<Button
@@ -375,7 +386,7 @@ const ConfirmPayModal = ({
handleClickPay(Math.ceil(formatStorePrice2Read(payPrice - teamBalance)));
}}
>
{t('common:pay.to_recharge')}
</Button>
)}
</ModalFooter>

View File

@@ -15,14 +15,14 @@ const Tools = () => {
const list = [
{
icon: 'core/dataset/datasetLight',
label: '我的知识库',
label: t('common:core.dataset.My Dataset'),
link: '/dataset/list'
},
...(feConfigs?.show_git
? [
{
icon: 'common/gitLight',
label: 'GitHub 地址',
label: t('common:core.app.tool_label.github'),
link: 'https://github.com/labring/FastGPT'
}
]
@@ -31,7 +31,7 @@ const Tools = () => {
? [
{
icon: 'common/courseLight',
label: '使用文档',
label: t('common:core.app.tool_label.doc'),
link: getDocPath('/docs/intro')
}
]
@@ -40,7 +40,7 @@ const Tools = () => {
? [
{
icon: 'support/bill/priceLight',
label: '计费说明',
label: t('common:core.app.tool_label.price'),
link: '/price'
}
]