Commit 10dcdb54 by papapatrick Committed by GitHub

I18n Translations (#2267)

* rebase

* i18n-1

* add error info i18n

* fix

* fix

* refactor: 删除error.json

* delete useI18n
parent 025d405f
import { ErrType } from '../errorCode'; import { ErrType } from '../errorCode';
import { i18nT } from '../../../../web/i18n/utils';
/* dataset: 502000 */ /* dataset: 502000 */
export enum AppErrEnum { export enum AppErrEnum {
unExist = 'appUnExist', unExist = 'appUnExist',
...@@ -8,11 +8,11 @@ export enum AppErrEnum { ...@@ -8,11 +8,11 @@ export enum AppErrEnum {
const appErrList = [ const appErrList = [
{ {
statusText: AppErrEnum.unExist, statusText: AppErrEnum.unExist,
message: '应用不存在' message: i18nT('common:code_error.app_error.not_exist')
}, },
{ {
statusText: AppErrEnum.unAuthApp, statusText: AppErrEnum.unAuthApp,
message: '无权操作该应用' message: i18nT('common:code_error.app_error.un_auth_app')
} }
]; ];
export default appErrList.reduce((acc, cur, index) => { export default appErrList.reduce((acc, cur, index) => {
......
import { ErrType } from '../errorCode'; import { ErrType } from '../errorCode';
import { i18nT } from '../../../../web/i18n/utils';
/* dataset: 504000 */ /* dataset: 504000 */
export enum ChatErrEnum { export enum ChatErrEnum {
unAuthChat = 'unAuthChat' unAuthChat = 'unAuthChat'
...@@ -7,7 +7,7 @@ export enum ChatErrEnum { ...@@ -7,7 +7,7 @@ export enum ChatErrEnum {
const errList = [ const errList = [
{ {
statusText: ChatErrEnum.unAuthChat, statusText: ChatErrEnum.unAuthChat,
message: '无权操作该对话记录' message: i18nT('common:code_error.chat_error.un_auth')
} }
]; ];
export default errList.reduce((acc, cur, index) => { export default errList.reduce((acc, cur, index) => {
......
import { ErrType } from '../errorCode'; import { ErrType } from '../errorCode';
import { i18nT } from '../../../../web/i18n/utils';
/* dataset: 506000 */ /* dataset: 506000 */
export enum OpenApiErrEnum { export enum OpenApiErrEnum {
unExist = 'openapiUnExist', unExist = 'openapiUnExist',
unAuth = 'openapiUnAuth', unAuth = 'openapiUnAuth',
exceedLimit = 'openapiExceedLimit' exceedLimit = 'openapiExceedLimit'
} }
const errList = [ const errList = [
{ {
statusText: OpenApiErrEnum.unExist, statusText: OpenApiErrEnum.unExist,
message: 'Api Key 不存在' message: i18nT('common:code_error.openapi_error.api_key_not_exist')
}, },
{ {
statusText: OpenApiErrEnum.unAuth, statusText: OpenApiErrEnum.unAuth,
message: '无权操作该 Api Key' message: i18nT('common:code_error.openapi_error.un_auth')
}, },
{ {
statusText: OpenApiErrEnum.exceedLimit, statusText: OpenApiErrEnum.exceedLimit,
message: '最多 10 组 API 密钥' message: i18nT('common:code_error.openapi_error.exceed_limit')
} }
]; ];
export default errList.reduce((acc, cur, index) => { export default errList.reduce((acc, cur, index) => {
return { return {
...acc, ...acc,
......
import { ErrType } from '../errorCode'; import { ErrType } from '../errorCode';
import { i18nT } from '../../../../web/i18n/utils';
/* dataset: 505000 */ /* dataset: 505000 */
export enum OutLinkErrEnum { export enum OutLinkErrEnum {
unExist = 'outlinkUnExist', unExist = 'outlinkUnExist',
unAuthLink = 'unAuthLink', unAuthLink = 'unAuthLink',
linkUnInvalid = 'linkUnInvalid', linkUnInvalid = 'linkUnInvalid',
unAuthUser = 'unAuthUser' unAuthUser = 'unAuthUser'
} }
const errList = [ const errList = [
{ {
statusText: OutLinkErrEnum.unExist, statusText: OutLinkErrEnum.unExist,
message: '分享链接不存在' message: i18nT('common:code_error.outlink_error.link_not_exist')
}, },
{ {
statusText: OutLinkErrEnum.unAuthLink, statusText: OutLinkErrEnum.unAuthLink,
message: '分享链接无效' message: i18nT('common:code_error.outlink_error.invalid_link')
}, },
{ {
code: 501, code: 501,
statusText: OutLinkErrEnum.linkUnInvalid, statusText: OutLinkErrEnum.linkUnInvalid,
message: '分享链接无效' message: i18nT('common:code_error.outlink_error.invalid_link') // 使用相同的错误消息
}, },
{ {
statusText: OutLinkErrEnum.unAuthUser, statusText: OutLinkErrEnum.unAuthUser,
message: '身份校验失败' message: i18nT('common:code_error.outlink_error.un_auth_user')
} }
]; ];
export default errList.reduce((acc, cur, index) => { export default errList.reduce((acc, cur, index) => {
return { return {
...acc, ...acc,
......
import { ErrType } from '../errorCode'; import { ErrType } from '../errorCode';
import { i18nT } from '../../../../web/i18n/utils';
/* dataset: 508000 */ /* dataset: 508000 */
export enum PluginErrEnum { export enum PluginErrEnum {
unExist = 'pluginUnExist', unExist = 'pluginUnExist',
unAuth = 'pluginUnAuth' unAuth = 'pluginUnAuth'
} }
const errList = [ const errList = [
{ {
statusText: PluginErrEnum.unExist, statusText: PluginErrEnum.unExist,
message: '插件不存在' message: i18nT('common:code_error.plugin_error.not_exist')
}, },
{ {
statusText: PluginErrEnum.unAuth, statusText: PluginErrEnum.unAuth,
message: '无权操作该插件' message: i18nT('common:code_error.plugin_error.un_auth')
} }
]; ];
export default errList.reduce((acc, cur, index) => { export default errList.reduce((acc, cur, index) => {
return { return {
...acc, ...acc,
......
import { ErrType } from '../errorCode'; import { ErrType } from '../errorCode';
import { i18nT } from '../../../../web/i18n/utils';
/* dataset: 509000 */ /* dataset: 509000 */
export enum SystemErrEnum { export enum SystemErrEnum {
communityVersionNumLimit = 'communityVersionNumLimit' communityVersionNumLimit = 'communityVersionNumLimit'
} }
const systemErr = [ const systemErr = [
{ {
statusText: SystemErrEnum.communityVersionNumLimit, statusText: SystemErrEnum.communityVersionNumLimit,
message: '超出开源版数量限制,请升级商业版: https://fastgpt.in' message: i18nT('common:code_error.system_error.community_version_num_limit')
} }
]; ];
export default systemErr.reduce((acc, cur, index) => { export default systemErr.reduce((acc, cur, index) => {
return { return {
...acc, ...acc,
......
import { ErrType } from '../errorCode'; import { ErrType } from '../errorCode';
import { i18nT } from '../../../../web/i18n/utils';
/* team: 500000 */ /* team: 500000 */
export enum TeamErrEnum { export enum TeamErrEnum {
teamOverSize = 'teamOverSize', teamOverSize = 'teamOverSize',
...@@ -12,17 +12,43 @@ export enum TeamErrEnum { ...@@ -12,17 +12,43 @@ export enum TeamErrEnum {
websiteSyncNotEnough = 'websiteSyncNotEnough', websiteSyncNotEnough = 'websiteSyncNotEnough',
reRankNotEnough = 'reRankNotEnough' reRankNotEnough = 'reRankNotEnough'
} }
const teamErr = [ const teamErr = [
{ statusText: TeamErrEnum.teamOverSize, message: 'error.team.overSize' }, {
{ statusText: TeamErrEnum.unAuthTeam, message: '无权操作该团队' }, statusText: TeamErrEnum.teamOverSize,
{ statusText: TeamErrEnum.aiPointsNotEnough, message: '' }, message: i18nT('common:code_error.team_error.over_size')
{ statusText: TeamErrEnum.datasetSizeNotEnough, message: '知识库容量不足,请先扩容~' }, },
{ statusText: TeamErrEnum.datasetAmountNotEnough, message: '知识库数量已达上限~' }, { statusText: TeamErrEnum.unAuthTeam, message: i18nT('common:code_error.team_error.un_auth') },
{ statusText: TeamErrEnum.appAmountNotEnough, message: '应用数量已达上限~' }, {
{ statusText: TeamErrEnum.pluginAmountNotEnough, message: '插件数量已达上限~' }, statusText: TeamErrEnum.aiPointsNotEnough,
{ statusText: TeamErrEnum.websiteSyncNotEnough, message: '无权使用Web站点同步~' }, message: i18nT('common:code_error.team_error.ai_points_not_enough')
{ statusText: TeamErrEnum.reRankNotEnough, message: '无权使用检索重排~' } }, // 需要定义或留空
{
statusText: TeamErrEnum.datasetSizeNotEnough,
message: i18nT('common:code_error.team_error.dataset_size_not_enough')
},
{
statusText: TeamErrEnum.datasetAmountNotEnough,
message: i18nT('common:code_error.team_error.dataset_amount_not_enough')
},
{
statusText: TeamErrEnum.appAmountNotEnough,
message: i18nT('common:code_error.team_error.app_amount_not_enough')
},
{
statusText: TeamErrEnum.pluginAmountNotEnough,
message: i18nT('common:code_error.team_error.plugin_amount_not_enough')
},
{
statusText: TeamErrEnum.websiteSyncNotEnough,
message: i18nT('common:code_error.team_error.website_sync_not_enough')
},
{
statusText: TeamErrEnum.reRankNotEnough,
message: i18nT('common:code_error.team_error.re_rank_not_enough')
}
]; ];
export default teamErr.reduce((acc, cur, index) => { export default teamErr.reduce((acc, cur, index) => {
return { return {
...acc, ...acc,
......
import { ErrType } from '../errorCode'; import { ErrType } from '../errorCode';
import { i18nT } from '../../../../web/i18n/utils';
/* team: 503000 */ /* team: 503000 */
export enum UserErrEnum { export enum UserErrEnum {
unAuthUser = 'unAuthUser', unAuthUser = 'unAuthUser',
...@@ -8,10 +8,22 @@ export enum UserErrEnum { ...@@ -8,10 +8,22 @@ export enum UserErrEnum {
balanceNotEnough = 'balanceNotEnough' balanceNotEnough = 'balanceNotEnough'
} }
const errList = [ const errList = [
{ statusText: UserErrEnum.unAuthUser, message: '找不到该用户' }, {
{ statusText: UserErrEnum.binVisitor, message: '您的身份校验未通过' }, statusText: UserErrEnum.unAuthUser,
{ statusText: UserErrEnum.binVisitor, message: '您当前身份为游客,无权操作' }, message: i18nT('common:code_error.user_error.un_auth_user')
{ statusText: UserErrEnum.balanceNotEnough, message: '账号余额不足~' } },
{
statusText: UserErrEnum.binVisitor,
message: i18nT('common:code_error.user_error.bin_visitor')
}, // 身份校验未通过
{
statusText: UserErrEnum.binVisitor,
message: i18nT('common:code_error.user_error.bin_visitor_guest')
}, // 游客身份
{
statusText: UserErrEnum.balanceNotEnough,
message: i18nT('common:code_error.user_error.balance_not_enough')
}
]; ];
export default errList.reduce((acc, cur, index) => { export default errList.reduce((acc, cur, index) => {
return { return {
......
...@@ -8,24 +8,25 @@ import teamErr from './code/team'; ...@@ -8,24 +8,25 @@ import teamErr from './code/team';
import userErr from './code/user'; import userErr from './code/user';
import commonErr from './code/common'; import commonErr from './code/common';
import SystemErrEnum from './code/system'; import SystemErrEnum from './code/system';
import { i18nT } from '../../../web/i18n/utils';
export const ERROR_CODE: { [key: number]: string } = { export const ERROR_CODE: { [key: number]: string } = {
400: '请求失败', 400: i18nT('common:code_error.error_code.400'),
401: '无权访问', 401: i18nT('common:code_error.error_code.401'),
403: '紧张访问', 403: i18nT('common:code_error.error_code.403'),
404: '请求不存在', 404: i18nT('common:code_error.error_code.404'),
405: '请求方法错误', 405: i18nT('common:code_error.error_code.405'),
406: '请求的格式错误', 406: i18nT('common:code_error.error_code.406'),
410: '资源已删除', 410: i18nT('common:code_error.error_code.410'),
422: '验证错误', 422: i18nT('common:code_error.error_code.422'),
500: '服务器发生错误', 500: i18nT('common:code_error.error_code.500'),
502: '网关错误', 502: i18nT('common:code_error.error_code.502'),
503: '服务器暂时过载或维护', 503: i18nT('common:code_error.error_code.503'),
504: '网关超时' 504: i18nT('common:code_error.error_code.504')
}; };
export const TOKEN_ERROR_CODE: Record<number, string> = { export const TOKEN_ERROR_CODE: Record<number, string> = {
403: '登录状态无效,请重新登录' 403: i18nT('common:code_error.token_error_code.403')
}; };
export const proxyError: Record<string, boolean> = { export const proxyError: Record<string, boolean> = {
...@@ -63,32 +64,31 @@ export const ERROR_RESPONSE: Record< ...@@ -63,32 +64,31 @@ export const ERROR_RESPONSE: Record<
[ERROR_ENUM.unAuthorization]: { [ERROR_ENUM.unAuthorization]: {
code: 403, code: 403,
statusText: ERROR_ENUM.unAuthorization, statusText: ERROR_ENUM.unAuthorization,
message: '凭证错误', message: i18nT('common:code_error.error_message.403'),
data: null data: null
}, },
[ERROR_ENUM.insufficientQuota]: { [ERROR_ENUM.insufficientQuota]: {
code: 510, code: 510,
statusText: ERROR_ENUM.insufficientQuota, statusText: ERROR_ENUM.insufficientQuota,
message: '账号余额不足', message: i18nT('common:code_error.error_message.510'),
data: null data: null
}, },
[ERROR_ENUM.unAuthModel]: { [ERROR_ENUM.unAuthModel]: {
code: 511, code: 511,
statusText: ERROR_ENUM.unAuthModel, statusText: ERROR_ENUM.unAuthModel,
message: '无权操作该模型', message: i18nT('common:code_error.error_message.511'),
data: null data: null
}, },
[ERROR_ENUM.unAuthFile]: { [ERROR_ENUM.unAuthFile]: {
code: 513, code: 513,
statusText: ERROR_ENUM.unAuthFile, statusText: ERROR_ENUM.unAuthFile,
message: '无权阅读该文件', message: i18nT('common:code_error.error_message.513'),
data: null data: null
}, },
[ERROR_ENUM.unAuthApiKey]: { [ERROR_ENUM.unAuthApiKey]: {
code: 514, code: 514,
statusText: ERROR_ENUM.unAuthApiKey, statusText: ERROR_ENUM.unAuthApiKey,
message: 'Api Key 不合法', message: i18nT('common:code_error.error_message.514'),
data: null data: null
}, },
...appErr, ...appErr,
......
import { Box, Button, Image } from '@chakra-ui/react'; import { Box, Button, Image } from '@chakra-ui/react';
import { useTranslation } from 'next-i18next';
export default function ComfirmVar({ export default function ComfirmVar({
newVariables, newVariables,
onCancel, onCancel,
...@@ -9,6 +9,7 @@ export default function ComfirmVar({ ...@@ -9,6 +9,7 @@ export default function ComfirmVar({
onCancel: () => void; onCancel: () => void;
onConfirm: () => void; onConfirm: () => void;
}) { }) {
const { t } = useTranslation();
return ( return (
<> <>
<Box <Box
...@@ -50,7 +51,7 @@ export default function ComfirmVar({ ...@@ -50,7 +51,7 @@ export default function ComfirmVar({
> >
<Image alt={''} src={'/imgs/workflow/variable.png'} objectFit={'contain'} w={'20px'} /> <Image alt={''} src={'/imgs/workflow/variable.png'} objectFit={'contain'} w={'20px'} />
</Box> </Box>
<Box>引用了未定义的变量,是否自动添加?</Box> <Box>{t('common:undefined_var')}</Box>
</Box> </Box>
<Box <Box
ml={16} ml={16}
...@@ -83,10 +84,10 @@ export default function ComfirmVar({ ...@@ -83,10 +84,10 @@ export default function ComfirmVar({
<Box> <Box>
<Box display={'flex'} justifyContent={'flex-end'} mt={4} mr={4}> <Box display={'flex'} justifyContent={'flex-end'} mt={4} mr={4}>
<Button size={'sm'} variant={'ghost'} onClick={onCancel}> <Button size={'sm'} variant={'ghost'} onClick={onCancel}>
取消 {t('common:common.Cancel')}
</Button> </Button>
<Button size={'sm'} variant={'primary'} ml={4} onClick={onConfirm}> <Button size={'sm'} variant={'primary'} ml={4} onClick={onConfirm}>
确定 {t('common:common.Confirm')}
</Button> </Button>
</Box> </Box>
</Box> </Box>
......
...@@ -2,7 +2,7 @@ import { useRef, useState, useCallback, useMemo, useEffect } from 'react'; ...@@ -2,7 +2,7 @@ import { useRef, useState, useCallback, useMemo, useEffect } from 'react';
import { IconButton, Flex, Box, Input } from '@chakra-ui/react'; import { IconButton, Flex, Box, Input } from '@chakra-ui/react';
import { ArrowBackIcon, ArrowForwardIcon } from '@chakra-ui/icons'; import { ArrowBackIcon, ArrowForwardIcon } from '@chakra-ui/icons';
import { useMutation } from '@tanstack/react-query'; import { useMutation } from '@tanstack/react-query';
import { useTranslation } from 'react-i18next';
import { throttle } from 'lodash'; import { throttle } from 'lodash';
import { useToast } from './useToast'; import { useToast } from './useToast';
import { getErrText } from '@fastgpt/global/common/error/utils'; import { getErrText } from '@fastgpt/global/common/error/utils';
...@@ -34,6 +34,7 @@ export function usePagination<T = any>({ ...@@ -34,6 +34,7 @@ export function usePagination<T = any>({
elementRef?: React.RefObject<HTMLDivElement>; elementRef?: React.RefObject<HTMLDivElement>;
}) { }) {
const { toast } = useToast(); const { toast } = useToast();
const { t } = useTranslation();
const [pageNum, setPageNum] = useState(1); const [pageNum, setPageNum] = useState(1);
const pageNumRef = useRef(pageNum); const pageNumRef = useRef(pageNum);
pageNumRef.current = pageNum; pageNumRef.current = pageNum;
...@@ -63,7 +64,7 @@ export function usePagination<T = any>({ ...@@ -63,7 +64,7 @@ export function usePagination<T = any>({
onChange && onChange(num); onChange && onChange(num);
} catch (error: any) { } catch (error: any) {
toast({ toast({
title: getErrText(error, '获取数据异常'), title: getErrText(error, t('common:core.chat.error.data_error')),
status: 'error' status: 'error'
}); });
console.log(error); console.log(error);
...@@ -138,9 +139,9 @@ export function usePagination<T = any>({ ...@@ -138,9 +139,9 @@ export function usePagination<T = any>({
const ScrollData = useCallback( const ScrollData = useCallback(
({ children, ...props }: { children: React.ReactNode }) => { ({ children, ...props }: { children: React.ReactNode }) => {
const loadText = useMemo(() => { const loadText = useMemo(() => {
if (isLoading) return '请求中……'; if (isLoading) return t('common:common.is_requesting');
if (total <= data.length) return '已加载全部'; if (total <= data.length) return t('common:common.request_end');
return '点击加载更多'; return t('common:common.request_more');
}, []); }, []);
return ( return (
...@@ -151,9 +152,9 @@ export function usePagination<T = any>({ ...@@ -151,9 +152,9 @@ export function usePagination<T = any>({
fontSize={'xs'} fontSize={'xs'}
color={'blackAlpha.500'} color={'blackAlpha.500'}
textAlign={'center'} textAlign={'center'}
cursor={loadText === '点击加载更多' ? 'pointer' : 'default'} cursor={loadText === t('common:common.request_more') ? 'pointer' : 'default'}
onClick={() => { onClick={() => {
if (loadText !== '点击加载更多') return; if (loadText !== t('common:common.request_more')) return;
mutate(pageNum + 1); mutate(pageNum + 1);
}} }}
> >
......
...@@ -85,7 +85,7 @@ export function useScrollPagination< ...@@ -85,7 +85,7 @@ export function useScrollPagination<
} }
} catch (error: any) { } catch (error: any) {
toast({ toast({
title: getErrText(error, '获取数据异常'), title: getErrText(error, t('common:core.chat.error.data_error')),
status: 'error' status: 'error'
}); });
console.log(error); console.log(error);
......
...@@ -17,9 +17,17 @@ ...@@ -17,9 +17,17 @@
"confirm_delete_folder_tip": "Are you sure to delete this folder? All the following applications and corresponding chat records will be deleted, please confirm!", "confirm_delete_folder_tip": "Are you sure to delete this folder? All the following applications and corresponding chat records will be deleted, please confirm!",
"copy_one_app": "Copy", "copy_one_app": "Copy",
"create_copy_success": "Create copy success", "create_copy_success": "Create copy success",
"cron": {
"every_day": "Executed every day",
"every_month": "Executed monthly",
"every_week": "Executed every week",
"interval": "interval execution"
},
"current_settings": "Current settings", "current_settings": "Current settings",
"day": "day",
"edit_app": "Edit app", "edit_app": "Edit app",
"edit_info": "Edit info", "edit_info": "Edit info",
"execute_time": "execution time",
"export_config_successful": "Config copied, please check for important data", "export_config_successful": "Config copied, please check for important data",
"export_configs": "Export Configs", "export_configs": "Export Configs",
"feedback_count": "User Feedback", "feedback_count": "User Feedback",
...@@ -27,6 +35,15 @@ ...@@ -27,6 +35,15 @@
"go_to_run": "Run", "go_to_run": "Run",
"import_configs": "Import Configs", "import_configs": "Import Configs",
"import_configs_failed": "Failed to import configs, please ensure configs are valid!", "import_configs_failed": "Failed to import configs, please ensure configs are valid!",
"interval": {
"12_hours": "every 12 hours",
"2_hours": "every 2 hours",
"3_hours": "every 3 hours",
"4_hours": "every 4 hours",
"6_hours": "every 6 hours",
"per_hour": "per hour"
},
"intro": "It is a large model application orchestration system that provides out-of-the-box data processing, model calling and other capabilities. It can quickly build a knowledge base and perform workflow orchestration through Flow visualization to realize complex knowledge base scenarios!",
"logs_empty": "No logs yet~", "logs_empty": "No logs yet~",
"logs_message_total": "Total Messages", "logs_message_total": "Total Messages",
"logs_title": "Title", "logs_title": "Title",
...@@ -40,6 +57,9 @@ ...@@ -40,6 +57,9 @@
"modules": { "modules": {
"Title is required": "Module name cannot be empty" "Title is required": "Module name cannot be empty"
}, },
"month": {
"unit": "Number"
},
"move_app": "Move app", "move_app": "Move app",
"paste_config": "Paste Config", "paste_config": "Paste Config",
"plugin_cost_per_times": "{{cost}}/per time", "plugin_cost_per_times": "{{cost}}/per time",
...@@ -53,6 +73,7 @@ ...@@ -53,6 +73,7 @@
"template": { "template": {
"simple_robot": "Simple Robot" "simple_robot": "Simple Robot"
}, },
"time_zone": "Time zone",
"tool_input_param_tip": "Configure related information before the plugin runs properly", "tool_input_param_tip": "Configure related information before the plugin runs properly",
"transition_to_workflow": "Transition to workflow", "transition_to_workflow": "Transition to workflow",
"transition_to_workflow_create_new_placeholder": "Create a new application instead of modifying the current one", "transition_to_workflow_create_new_placeholder": "Create a new application instead of modifying the current one",
...@@ -74,6 +95,15 @@ ...@@ -74,6 +95,15 @@
"version": { "version": {
"Revert success": "Revert success" "Revert success": "Revert success"
}, },
"week": {
"Friday": "Friday",
"Monday": "Monday",
"Saturday": "Saturday",
"Sunday": "Sunday",
"Thursday": "Thursday",
"Tuesday": "Tuesday",
"Wednesday": "Wednesday"
},
"workflow": { "workflow": {
"Input guide": "Input guide", "Input guide": "Input guide",
"template": { "template": {
......
{ {
"Delete_all": "Delete all", "Delete_all": "Delete all",
"chat_input_guide_lexicon_is_empty": "The lexicon has not been configured",
"config_input_guide": "Configure input boot",
"config_input_guide_lexicon": "Config",
"config_input_guide_lexicon_title": "Config lexicon",
"csv_input_lexicon_tip": "Only CSV can be imported in batches. Click to download the template",
"custom_input_guide_url": "Custom lexicon url",
"delete_all_input_guide_confirm": "Confirm to delete all input guide lexicons", "delete_all_input_guide_confirm": "Confirm to delete all input guide lexicons",
"input_guide": "Input guide", "chat_history": "chat record",
"input_guide_lexicon": "Lexicon", "chat_input_guide_lexicon_is_empty": "No vocabulary has been configured yet",
"input_guide_tip": "You can configure some preset questions. When the user enters a question, the relevant question is retrieved from these preset questions for prompt.", "citations": "{{num}} citations",
"insert_input_guide,_some_data_already_exists": "Duplicate data, automatically filtered, insert: {{len}} data", "click_contextual_preview": "Click to see contextual preview",
"new_input_guide_lexicon": "New lexicon" "config_input_guide": "Configure input boot",
"config_input_guide_lexicon": "Configure thesaurus",
"config_input_guide_lexicon_title": "Configure thesaurus",
"content_empty": "Content is empty",
"contextual": "context",
"contextual_preview": "Contextual preview",
"csv_input_lexicon_tip": "Only supports CSV batch import, click to download the template",
"custom_input_guide_url": "Custom thesaurus address",
"empty_directory": "There is nothing left to choose from in this directory~",
"in_progress": "in progress",
"input_guide": "Enter boot",
"input_guide_lexicon": "vocabulary",
"input_guide_tip": "Some preset questions can be configured. \nWhen the user enters a question, relevant questions will be obtained from these preset questions for prompts.",
"insert_input_guide,_some_data_already_exists": "There is duplicate data, which has been automatically filtered. A total of {{len}} pieces of data have been inserted.",
"is_chatting": "Chatting...please wait for the end",
"items": "strip",
"module_runtime_and": "module run time and",
"multiple_AI_conversations": "Multiple AI conversations",
"new_chat": "new conversation",
"new_input_guide_lexicon": "New vocabulary",
"plugins_output": "Plugin output",
"question_tip": "From left to right, the response order of each module",
"rearrangement": "Search results rearranged",
"stream_output": "stream output",
"view_citations": "View citations",
"web_site_sync": "Web site synchronization"
} }
{ {
"click_to_view_raw_source": "View source", "click_to_view_raw_source": "View source",
"release_the_mouse_to_upload_the_file": "Release the mouse to upload the file",
"upload_error_description": "Only supports uploading multiple files or one folder at a time",
"file_name": "File Name", "file_name": "File Name",
"file_size": "File Size", "file_size": "File Size",
"reached_max_file_count": "Maximum number of files reached",
"release_the_mouse_to_upload_the_file": "Release the mouse to upload the file",
"select_and_drag_file_tip": "Click or drag files here to upload", "select_and_drag_file_tip": "Click or drag files here to upload",
"select_file_amount_limit": "You can select up to {{max}} files", "select_file_amount_limit": "You can select up to {{max}} files",
"some_file_count_exceeds_limit": "Exceeds {{maxCount}} files, automatically truncated", "some_file_count_exceeds_limit": "Exceeds {{maxCount}} files, automatically truncated",
...@@ -11,5 +11,6 @@ ...@@ -11,5 +11,6 @@
"support_file_type": "Supports {{fileType}} type files", "support_file_type": "Supports {{fileType}} type files",
"support_max_count": "Supports up to {{maxCount}} files.", "support_max_count": "Supports up to {{maxCount}} files.",
"support_max_size": "Maximum size per file: {{maxSize}}.", "support_max_size": "Maximum size per file: {{maxSize}}.",
"upload_error_description": "Only supports uploading multiple files or one folder at a time",
"upload_failed": "Upload failed" "upload_failed": "Upload failed"
} }
{ {
"bind_inform_account_error": "Abnormal binding notification account", "bind_inform_account_error": "Abnormal binding notification account",
"bind_inform_account_success": "Binding notification account successful", "bind_inform_account_success": "Binding notification account successful",
"delete": {
"admin_failed": "Failed to delete administrator",
"admin_success": "Administrator deleted successfully"
},
"has_chosen": "chosen",
"login": {
"error": "Login exception",
"failed": "Login failed",
"login_account": "Login to {{account}} account",
"login_error": "wrong user name or password",
"password_condition": "Password maximum 60 characters",
"success": "login successful",
"to_register": "If you don’t have an account, please register."
},
"name": "name",
"notification": {
"Bind Notification Pipe Hint": "Bind the email address or mobile phone number for receiving notifications to ensure that you receive important system notifications in a timely manner."
},
"operations": "operate",
"password": {
"change_error": "Exception when changing password",
"code_required": "verification code must be filled",
"code_send_error": "Verification code sending exception",
"code_sended": "Verification code sent",
"confirm": "Confirm Password",
"email_phone": "Email/Mobile phone number",
"email_phone_error": "Email/mobile phone number format error",
"email_phone_void": "Email/mobile phone number cannot be empty",
"get_code": "get verification code",
"get_code_again": "Reacquire after s",
"new_password": "New password (4~20 digits)",
"not_match": "Two passwords are inconsistent",
"password_condition": "Password must be at least 4 characters and at most 20 characters",
"password_required": "password can not be blank",
"retrieve": "Retrieve password",
"retrieved": "Password has been retrieved",
"retrieved_account": "Retrieve {{account}} account",
"to_login": "Go to login",
"verification_code": "Verification code"
},
"permission": { "permission": {
"Manage": "administrator", "Manage": "administrator",
"Manage tip": "Team administrator, with full permissions", "Manage tip": "Team administrator, with full permissions",
"Read": "Read only", "Read": "Read only",
"Read desc": "Members can only read related resources and cannot create new resources.", "Read desc": "Members can only read related resources and cannot create new resources.",
"Write": "Write", "Write": "Write",
"Write tip": "In addition to readable resources, you can also create new resources" "Write tip": "In addition to readable resources, you can also create new resources",
"only_collaborators": "Collaborator access only",
"team_read": "Team accessible",
"team_write": "Team editable"
},
"permissions": "Permissions",
"register": {
"confirm": "Confirm registration",
"error": "Registration exception",
"failed": "registration failed",
"register_account": "Register {{account}} account",
"success": "registration success",
"to_login": "Already have an account? Log in"
},
"search_user": "Search username",
"synchronization": {
"button": "Sync now",
"placeholder": "Please enter sync label",
"title": "Fill in the tag synchronization link and click the sync button to synchronize"
}, },
"team": { "team": {
"Add manager": "Add manager" "Add manager": "Add manager",
"add_collaborator": "Add collaborators",
"manage_collaborators": "Manage collaborators",
"no_collaborators": "No collaborators yet"
} }
} }
...@@ -6,7 +6,9 @@ ...@@ -6,7 +6,9 @@
"Reset template confirm": "Are you sure to restore the code template? All input and output to template values will be reset, please be careful to save the current code." "Reset template confirm": "Are you sure to restore the code template? All input and output to template values will be reset, please be careful to save the current code."
}, },
"confirm_delete_field_tip": "Confirm to delete the field?", "confirm_delete_field_tip": "Confirm to delete the field?",
"create_link_error": "Create link exception",
"custom_input": "Custom input", "custom_input": "Custom input",
"delete_api": "Are you sure you want to delete this API key? \nAfter deletion, the key will become invalid immediately and the corresponding conversation log will not be deleted. Please confirm!",
"edit_input": "Edit input", "edit_input": "Edit input",
"field_description": "Field description", "field_description": "Field description",
"field_description_placeholder": "Describes the functionality of this input field, which affects the quality of model generation if the parameter is called for a tool", "field_description_placeholder": "Describes the functionality of this input field, which affects the quality of model generation if the parameter is called for a tool",
...@@ -28,5 +30,6 @@ ...@@ -28,5 +30,6 @@
"Error": "Error" "Error": "Error"
}, },
"tool_input": "Tool", "tool_input": "Tool",
"update_link_error": "Update link exception",
"variable_picker_tips": "enter node name or variable name to search" "variable_picker_tips": "enter node name or variable name to search"
} }
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
"has new version": "有新版本" "has new version": "有新版本"
} }
}, },
"intro": "是一个大模型应用编排系统,提供开箱即用的数据处理、模型调用等能力,可以快速的构建知识库并通过 Flow 可视化进行工作流编排,实现复杂的知识库场景!",
"app_detail": "应用详情", "app_detail": "应用详情",
"chat_debug": "调试预览", "chat_debug": "调试预览",
"chat_logs": "对话日志", "chat_logs": "对话日志",
...@@ -79,5 +80,34 @@ ...@@ -79,5 +80,34 @@
"template": { "template": {
"communication": "通信" "communication": "通信"
} }
} },
"interval": {
"per_hour": "每小时",
"2_hours": "每2小时",
"3_hours": "每3小时",
"4_hours": "每4小时",
"6_hours": "每6小时",
"12_hours": "每12小时"
},
"week": {
"Monday": "星期一",
"Tuesday": "星期二",
"Wednesday": "星期三",
"Thursday": "星期四",
"Friday": "星期五",
"Saturday": "星期六",
"Sunday": "星期日"
},
"month": {
"unit": "号"
},
"cron": {
"every_day": "每天执行",
"every_month": "每月执行",
"every_week": "每周执行",
"interval": "间隔执行"
},
"day": "日",
"execute_time": "执行时间",
"time_zone": "时区"
} }
...@@ -11,5 +11,24 @@ ...@@ -11,5 +11,24 @@
"input_guide_lexicon": "词库", "input_guide_lexicon": "词库",
"input_guide_tip": "可以配置一些预设的问题。在用户输入问题时,会从这些预设问题中获取相关问题进行提示。", "input_guide_tip": "可以配置一些预设的问题。在用户输入问题时,会从这些预设问题中获取相关问题进行提示。",
"insert_input_guide,_some_data_already_exists": "有重复数据,已自动过滤,共插入 {{len}} 条数据", "insert_input_guide,_some_data_already_exists": "有重复数据,已自动过滤,共插入 {{len}} 条数据",
"new_input_guide_lexicon": "新词库" "new_input_guide_lexicon": "新词库",
"is_chatting": "正在聊天中...请等待结束",
"content_empty": "内容为空",
"contextual": "{{num}}条上下文",
"contextual_preview": "上下文预览 {{num}} 条",
"items": "条",
"view_citations": "查看引用",
"citations": "{{num}}条引用",
"click_contextual_preview": "点击查看上下文预览",
"multiple_AI_conversations": "多组 AI 对话",
"module_runtime_and": "模块运行时间和",
"empty_directory": "这个目录已经没东西可选了~",
"chat_history": "聊天记录",
"stream_output": "流输出",
"plugins_output": "插件输出",
"in_progress": "进行中",
"question_tip": "从上到下,为各个模块的响应顺序",
"rearrangement": "检索结果重排",
"web_site_sync": "Web站点同步",
"new_chat": "新对话"
} }
...@@ -11,5 +11,6 @@ ...@@ -11,5 +11,6 @@
"support_file_type": "支持 {{fileType}} 类型文件", "support_file_type": "支持 {{fileType}} 类型文件",
"support_max_count": "最多支持 {{maxCount}} 个文件", "support_max_count": "最多支持 {{maxCount}} 个文件",
"support_max_size": "单个文件最大 {{maxSize}}", "support_max_size": "单个文件最大 {{maxSize}}",
"upload_failed": "上传异常" "upload_failed": "上传异常",
"reached_max_file_count": "已达到最大文件数量"
} }
...@@ -10,9 +10,67 @@ ...@@ -10,9 +10,67 @@
"Read": "仅读", "Read": "仅读",
"Read desc": "成员仅可阅读相关资源,无法新建资源", "Read desc": "成员仅可阅读相关资源,无法新建资源",
"Write": "可写", "Write": "可写",
"Write tip": "除了可读资源外,还可以新建新的资源" "Write tip": "除了可读资源外,还可以新建新的资源",
"only_collaborators": "仅协作者访问",
"team_read": "团队可访问",
"team_write": "团队可编辑"
}, },
"team": { "team": {
"Add manager": "添加管理员" "Add manager": "添加管理员",
"add_collaborator": "添加协作者",
"manage_collaborators": "管理协作者",
"no_collaborators": "暂无协作者"
},
"search_user": "搜索用户名",
"has_chosen": "已选择",
"name": "名称",
"permissions": "权限",
"operations": "操作",
"delete": {
"admin_success": "删除管理员成功",
"admin_failed": "删除管理员失败"
},
"synchronization": {
"title": "填写标签同步链接,点击同步按钮即可同步",
"placeholder": "请输入同步标签",
"button": "立即同步"
},
"password": {
"retrieve": "找回密码",
"retrieved": "密码已找回",
"change_error": "修改密码异常",
"retrieved_account": "找回 {{account}} 账号",
"email_phone": "邮箱/手机号",
"email_phone_void": "邮箱/手机号不能为空",
"email_phone_error": "邮箱/手机号格式错误",
"code_required": "验证码不能为空",
"new_password": "新密码(4~20位)",
"password_required": "密码不能为空",
"password_condition": "密码最少 4 位最多 20 位",
"verification_code": "验证码",
"confirm": "确认密码",
"not_match": "两次密码不一致",
"to_login": "去登陆",
"get_code": "获取验证码",
"get_code_again": "s后重新获取",
"code_sended": "验证码已发送",
"code_send_error": "验证码发送异常"
},
"register": {
"success": "注册成功",
"failed": "注册失败",
"error": "注册异常",
"register_account": "注册 {{account}} 账号",
"confirm": "确认注册",
"to_login": "已有账号,去登录"
},
"login": {
"success": "登录成功",
"failed": "登录失败",
"error": "登录异常",
"login_account": "登录 {{account}} 账号",
"login_error": "用户名或密码错误",
"to_register": "没有账号,去注册",
"password_condition": "密码最多 60 位"
} }
} }
...@@ -28,5 +28,8 @@ ...@@ -28,5 +28,8 @@
"Error": "错误信息" "Error": "错误信息"
}, },
"tool_input": "工具参数", "tool_input": "工具参数",
"variable_picker_tips": "可输入节点名或变量名搜索" "variable_picker_tips": "可输入节点名或变量名搜索",
"delete_api": "确认删除该API密钥?删除后该密钥立即失效,对应的对话日志不会删除,请确认!",
"create_link_error": "创建链接异常",
"update_link_error": "更新链接异常"
} }
...@@ -7,7 +7,6 @@ import publish from '../i18n/zh/publish.json'; ...@@ -7,7 +7,6 @@ import publish from '../i18n/zh/publish.json';
import workflow from '../i18n/zh/workflow.json'; import workflow from '../i18n/zh/workflow.json';
import user from '../i18n/zh/user.json'; import user from '../i18n/zh/user.json';
import chat from '../i18n/zh/chat.json'; import chat from '../i18n/zh/chat.json';
export interface I18nNamespaces { export interface I18nNamespaces {
common: typeof common; common: typeof common;
dataset: typeof dataset; dataset: typeof dataset;
......
...@@ -22,7 +22,7 @@ const CommunityModal = ({ onClose }: { onClose: () => void }) => { ...@@ -22,7 +22,7 @@ const CommunityModal = ({ onClose }: { onClose: () => void }) => {
<ModalFooter> <ModalFooter>
<Button variant={'whiteBase'} onClick={onClose}> <Button variant={'whiteBase'} onClick={onClose}>
关闭 {t('common:common.Close')}
</Button> </Button>
</ModalFooter> </ModalFooter>
</MyModal> </MyModal>
......
...@@ -3,7 +3,7 @@ import MyModal from '@fastgpt/web/components/common/MyModal'; ...@@ -3,7 +3,7 @@ import MyModal from '@fastgpt/web/components/common/MyModal';
import { Box, Button, Flex, Grid, useTheme } from '@chakra-ui/react'; import { Box, Button, Flex, Grid, useTheme } from '@chakra-ui/react';
import { PromptTemplateItem } from '@fastgpt/global/core/ai/type.d'; import { PromptTemplateItem } from '@fastgpt/global/core/ai/type.d';
import { ModalBody, ModalFooter } from '@chakra-ui/react'; import { ModalBody, ModalFooter } from '@chakra-ui/react';
import { useTranslation } from 'next-i18next';
const PromptTemplate = ({ const PromptTemplate = ({
title, title,
templates, templates,
...@@ -17,7 +17,7 @@ const PromptTemplate = ({ ...@@ -17,7 +17,7 @@ const PromptTemplate = ({
}) => { }) => {
const theme = useTheme(); const theme = useTheme();
const [selectTemplateTitle, setSelectTemplateTitle] = useState<PromptTemplateItem>(); const [selectTemplateTitle, setSelectTemplateTitle] = useState<PromptTemplateItem>();
const { t } = useTranslation();
return ( return (
<MyModal isOpen title={title} onClose={onClose} iconSrc="/imgs/modal/prompt.svg"> <MyModal isOpen title={title} onClose={onClose} iconSrc="/imgs/modal/prompt.svg">
<ModalBody h="100%" w={'600px'} maxW={'90vw'} overflowY={'auto'}> <ModalBody h="100%" w={'600px'} maxW={'90vw'} overflowY={'auto'}>
...@@ -55,7 +55,7 @@ const PromptTemplate = ({ ...@@ -55,7 +55,7 @@ const PromptTemplate = ({
onClose(); onClose();
}} }}
> >
确认选择 {t('common:confirm_choice')}
</Button> </Button>
</ModalFooter> </ModalFooter>
</MyModal> </MyModal>
......
...@@ -14,7 +14,6 @@ import CollaboratorContextProvider, { ...@@ -14,7 +14,6 @@ import CollaboratorContextProvider, {
} from '../../support/permission/MemberManager/context'; } from '../../support/permission/MemberManager/context';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip'; import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import { useSystemStore } from '@/web/common/system/useSystemStore'; import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useI18n } from '@/web/context/I18n';
import ResumeInherit from '@/components/support/permission/ResumeInheritText'; import ResumeInherit from '@/components/support/permission/ResumeInheritText';
const FolderSlideCard = ({ const FolderSlideCard = ({
...@@ -55,7 +54,6 @@ const FolderSlideCard = ({ ...@@ -55,7 +54,6 @@ const FolderSlideCard = ({
}) => { }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { feConfigs } = useSystemStore(); const { feConfigs } = useSystemStore();
const { commonT } = useI18n();
const { toast } = useToast(); const { toast } = useToast();
const { ConfirmModal, openConfirm } = useConfirm({ const { ConfirmModal, openConfirm } = useConfirm({
...@@ -78,7 +76,7 @@ const FolderSlideCard = ({ ...@@ -78,7 +76,7 @@ const FolderSlideCard = ({
/> />
</HStack> </HStack>
<Box mt={3} fontSize={'sm'} color={'myGray.500'} cursor={'pointer'} onClick={onEdit}> <Box mt={3} fontSize={'sm'} color={'myGray.500'} cursor={'pointer'} onClick={onEdit}>
{intro || '暂无介绍'} {intro || t('common:not_yet_introduced')}
</Box> </Box>
</Box> </Box>
......
...@@ -135,7 +135,9 @@ const AIChatSettingsModal = ({ ...@@ -135,7 +135,9 @@ const AIChatSettingsModal = ({
<QuestionTip ml={1} label={t('common:core.module.template.AI support tool tip')} /> <QuestionTip ml={1} label={t('common:core.module.template.AI support tool tip')} />
</Box> </Box>
<Box flex={1} ml={'10px'}> <Box flex={1} ml={'10px'}>
{selectedModel?.toolChoice || selectedModel?.functionCall ? '支持' : '不支持'} {selectedModel?.toolChoice || selectedModel?.functionCall
? t('common:common.support')
: t('common:common.not_support')}
</Box> </Box>
</Flex> </Flex>
<Flex mt={8}> <Flex mt={8}>
......
...@@ -20,11 +20,10 @@ import type { MultipleSelectProps } from '@fastgpt/web/components/common/MySelec ...@@ -20,11 +20,10 @@ import type { MultipleSelectProps } from '@fastgpt/web/components/common/MySelec
import { cronParser2Fields } from '@fastgpt/global/common/string/time'; import { cronParser2Fields } from '@fastgpt/global/common/string/time';
import TimezoneSelect from '@fastgpt/web/components/common/MySelect/TimezoneSelect'; import TimezoneSelect from '@fastgpt/web/components/common/MySelect/TimezoneSelect';
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel'; import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
const MultipleRowSelect = dynamic( const MultipleRowSelect = dynamic(
() => import('@fastgpt/web/components/common/MySelect/MultipleRowSelect') () => import('@fastgpt/web/components/common/MySelect/MultipleRowSelect')
); );
import { i18nT } from '@fastgpt/web/i18n/utils';
// options type: // options type:
enum CronJobTypeEnum { enum CronJobTypeEnum {
month = 'month', month = 'month',
...@@ -40,17 +39,32 @@ const get24HoursOptions = () => { ...@@ -40,17 +39,32 @@ const get24HoursOptions = () => {
value: i value: i
})); }));
}; };
const getRoute = (i: number) => {
switch (i) {
case 0:
return 'app:week.Sunday';
case 1:
return 'app:week.Monday';
case 2:
return 'app:week.Tuesday';
case 3:
return 'app:week.Wednesday';
case 4:
return 'app:week.Thursday';
case 5:
return 'app:week.Friday';
case 6:
return 'app:week.Saturday';
default:
return 'app:week.Sunday';
}
};
const getWeekOptions = () => { const getWeekOptions = () => {
return Array.from({ length: 7 }, (_, i) => { return Array.from({ length: 7 }, (_, i) => {
if (i === 0) {
return {
label: '星期日',
value: i,
children: get24HoursOptions()
};
}
return { return {
label: `星期${i}`, label: i18nT(getRoute(i)),
value: i, value: i,
children: get24HoursOptions() children: get24HoursOptions()
}; };
...@@ -58,7 +72,7 @@ const getWeekOptions = () => { ...@@ -58,7 +72,7 @@ const getWeekOptions = () => {
}; };
const getMonthOptions = () => { const getMonthOptions = () => {
return Array.from({ length: 28 }, (_, i) => ({ return Array.from({ length: 28 }, (_, i) => ({
label: `${i + 1}号`, label: `${i + 1}` + i18nT('app:month.unit'),
value: i, value: i,
children: get24HoursOptions() children: get24HoursOptions()
})); }));
...@@ -67,27 +81,27 @@ const getInterValOptions = () => { ...@@ -67,27 +81,27 @@ const getInterValOptions = () => {
// 每n小时 // 每n小时
return [ return [
{ {
label: `每小时`, label: i18nT('app:interval.per_hour'),
value: 1 value: 1
}, },
{ {
label: `每2小时`, label: i18nT('app:interval.2_hours'),
value: 2 value: 2
}, },
{ {
label: `每3小时`, label: i18nT('app:interval.3_hours'),
value: 3 value: 3
}, },
{ {
label: `每4小时`, label: i18nT('app:interval.4_hours'),
value: 4 value: 4
}, },
{ {
label: `每6小时`, label: i18nT('app:interval.6_hours'),
value: 6 value: 6
}, },
{ {
label: `每12小时`, label: i18nT('app:interval.12_hours'),
value: 12 value: 12
} }
]; ];
...@@ -113,22 +127,22 @@ const ScheduledTriggerConfig = ({ ...@@ -113,22 +127,22 @@ const ScheduledTriggerConfig = ({
const cronSelectList = useRef<MultipleSelectProps['list']>([ const cronSelectList = useRef<MultipleSelectProps['list']>([
{ {
label: '每天执行', label: t('app:cron.every_day'),
value: CronJobTypeEnum.day, value: CronJobTypeEnum.day,
children: get24HoursOptions() children: get24HoursOptions()
}, },
{ {
label: '每周执行', label: t('app:cron.every_week'),
value: CronJobTypeEnum.week, value: CronJobTypeEnum.week,
children: getWeekOptions() children: getWeekOptions()
}, },
{ {
label: '每月执行', label: t('app:cron.every_month'),
value: CronJobTypeEnum.month, value: CronJobTypeEnum.month,
children: getMonthOptions() children: getMonthOptions()
}, },
{ {
label: '间隔执行', label: t('app:cron.interval'),
value: CronJobTypeEnum.interval, value: CronJobTypeEnum.interval,
children: getInterValOptions() children: getInterValOptions()
} }
...@@ -224,7 +238,7 @@ const ScheduledTriggerConfig = ({ ...@@ -224,7 +238,7 @@ const ScheduledTriggerConfig = ({
} }
if (cronField[0] === 'week') { if (cronField[0] === 'week') {
return t('core.app.schedule.Every week', { return t('core.app.schedule.Every week', {
day: cronField[1] === 0 ? '日' : cronField[1], day: cronField[1] === 0 ? t('app:day') : cronField[1],
hour: cronField[2] hour: cronField[2]
}); });
} }
...@@ -279,10 +293,7 @@ const ScheduledTriggerConfig = ({ ...@@ -279,10 +293,7 @@ const ScheduledTriggerConfig = ({
> >
<ModalBody> <ModalBody>
<Flex justifyContent={'space-between'} alignItems={'center'}> <Flex justifyContent={'space-between'} alignItems={'center'}>
<FormLabel flex={'0 0 80px'}> <FormLabel flex={'0 0 80px'}>{t('common:core.app.schedule.Open schedule')}</FormLabel>
{' '}
{t('common:core.app.schedule.Open schedule')}
</FormLabel>
<Switch <Switch
isChecked={isOpenSchedule} isChecked={isOpenSchedule}
onChange={(e) => { onChange={(e) => {
...@@ -297,7 +308,7 @@ const ScheduledTriggerConfig = ({ ...@@ -297,7 +308,7 @@ const ScheduledTriggerConfig = ({
{isOpenSchedule && ( {isOpenSchedule && (
<> <>
<Flex alignItems={'center'} mt={5}> <Flex alignItems={'center'} mt={5}>
<FormLabel flex={'0 0 80px'}>执行时间</FormLabel> <FormLabel flex={'0 0 80px'}>{t('app:execute_time')}</FormLabel>
<Box flex={'1 0 0'}> <Box flex={'1 0 0'}>
<MultipleRowSelect <MultipleRowSelect
label={formatLabel} label={formatLabel}
...@@ -310,7 +321,7 @@ const ScheduledTriggerConfig = ({ ...@@ -310,7 +321,7 @@ const ScheduledTriggerConfig = ({
</Box> </Box>
</Flex> </Flex>
<Flex alignItems={'center'} mt={5}> <Flex alignItems={'center'} mt={5}>
<FormLabel flex={'0 0 80px'}>时区</FormLabel> <FormLabel flex={'0 0 80px'}>{t('app:time_zone')}</FormLabel>
<Box flex={'1 0 0'}> <Box flex={'1 0 0'}>
<TimezoneSelect <TimezoneSelect
value={timezone} value={timezone}
......
...@@ -6,12 +6,13 @@ import { ChatBoxContext } from '../Provider'; ...@@ -6,12 +6,13 @@ import { ChatBoxContext } from '../Provider';
import { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type'; import { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant'; import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { useTranslation } from 'next-i18next';
const isLLMNode = (item: ChatHistoryItemResType) => const isLLMNode = (item: ChatHistoryItemResType) =>
item.moduleType === FlowNodeTypeEnum.chatNode || item.moduleType === FlowNodeTypeEnum.tools; item.moduleType === FlowNodeTypeEnum.chatNode || item.moduleType === FlowNodeTypeEnum.tools;
const ContextModal = ({ onClose, dataId }: { onClose: () => void; dataId: string }) => { const ContextModal = ({ onClose, dataId }: { onClose: () => void; dataId: string }) => {
const { getHistoryResponseData } = useContextSelector(ChatBoxContext, (v) => v); const { getHistoryResponseData } = useContextSelector(ChatBoxContext, (v) => v);
const { t } = useTranslation();
const { loading: isLoading, data: contextModalData } = useRequest2( const { loading: isLoading, data: contextModalData } = useRequest2(
() => () =>
getHistoryResponseData({ dataId }).then((res) => { getHistoryResponseData({ dataId }).then((res) => {
...@@ -34,7 +35,7 @@ const ContextModal = ({ onClose, dataId }: { onClose: () => void; dataId: string ...@@ -34,7 +35,7 @@ const ContextModal = ({ onClose, dataId }: { onClose: () => void; dataId: string
onClose={onClose} onClose={onClose}
isLoading={isLoading} isLoading={isLoading}
iconSrc="/imgs/modal/chatHistory.svg" iconSrc="/imgs/modal/chatHistory.svg"
title={`上下文预览(${contextModalData?.length || 0}条)`} title={t('chat:contextual_preview', { num: contextModalData?.length || 0 })}
h={['90vh', '80vh']} h={['90vh', '80vh']}
minW={['90vw', '600px']} minW={['90vw', '600px']}
isCentered isCentered
......
...@@ -175,28 +175,28 @@ const ResponseTags = ({ ...@@ -175,28 +175,28 @@ const ResponseTags = ({
{showDetail && ( {showDetail && (
<Flex alignItems={'center'} mt={3} flexWrap={'wrap'} gap={2}> <Flex alignItems={'center'} mt={3} flexWrap={'wrap'} gap={2}>
{quoteList.length > 0 && ( {quoteList.length > 0 && (
<MyTooltip label="查看引用"> <MyTooltip label={t('chat:view_citations')}>
<MyTag <MyTag
colorSchema="blue" colorSchema="blue"
type="borderSolid" type="borderSolid"
cursor={'pointer'} cursor={'pointer'}
onClick={() => setQuoteModalData({ rawSearch: quoteList })} onClick={() => setQuoteModalData({ rawSearch: quoteList })}
> >
{quoteList.length}条引用 {t('chat:citations', { num: quoteList.length })}
</MyTag> </MyTag>
</MyTooltip> </MyTooltip>
)} )}
{llmModuleAccount === 1 && ( {llmModuleAccount === 1 && (
<> <>
{historyPreviewLength > 0 && ( {historyPreviewLength > 0 && (
<MyTooltip label={'点击查看上下文预览'}> <MyTooltip label={t('chat:click_contextual_preview')}>
<MyTag <MyTag
colorSchema="green" colorSchema="green"
cursor={'pointer'} cursor={'pointer'}
type="borderSolid" type="borderSolid"
onClick={onOpenContextModal} onClick={onOpenContextModal}
> >
{historyPreviewLength}条上下文 {t('chat:contextual', { num: historyPreviewLength })}
</MyTag> </MyTag>
</MyTooltip> </MyTooltip>
)} )}
...@@ -204,12 +204,12 @@ const ResponseTags = ({ ...@@ -204,12 +204,12 @@ const ResponseTags = ({
)} )}
{llmModuleAccount > 1 && ( {llmModuleAccount > 1 && (
<MyTag type="borderSolid" colorSchema="blue"> <MyTag type="borderSolid" colorSchema="blue">
多组 AI 对话 {t('chat:multiple_AI_conversations')}
</MyTag> </MyTag>
)} )}
{isPc && runningTime > 0 && ( {isPc && runningTime > 0 && (
<MyTooltip label={'模块运行时间和'}> <MyTooltip label={t('chat:module_runtime_and')}>
<MyTag colorSchema="purple" type="borderSolid" cursor={'default'}> <MyTag colorSchema="purple" type="borderSolid" cursor={'default'}>
{runningTime}s {runningTime}s
</MyTag> </MyTag>
......
...@@ -90,7 +90,7 @@ const SelectMarkCollection = ({ ...@@ -90,7 +90,7 @@ const SelectMarkCollection = ({
})() })()
)} )}
</Grid> </Grid>
{datasets.length === 0 && <EmptyTip text={'这个目录已经没东西可选了~'}></EmptyTip>} {datasets.length === 0 && <EmptyTip text={t('chat:empty_directory')}></EmptyTip>}
</ModalBody> </ModalBody>
</DatasetSelectModal> </DatasetSelectModal>
)} )}
......
...@@ -4,8 +4,9 @@ import { useCallback } from 'react'; ...@@ -4,8 +4,9 @@ import { useCallback } from 'react';
import { htmlTemplate } from '@/web/core/chat/constants'; import { htmlTemplate } from '@/web/core/chat/constants';
import { fileDownload } from '@/web/common/file/utils'; import { fileDownload } from '@/web/common/file/utils';
import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants'; import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants';
import { useTranslation } from 'next-i18next';
export const useChatBox = () => { export const useChatBox = () => {
const { t } = useTranslation();
const onExportChat = useCallback( const onExportChat = useCallback(
({ type, history }: { type: ExportChatType; history: ChatItemType[] }) => { ({ type, history }: { type: ExportChatType; history: ChatItemType[] }) => {
const getHistoryHtml = () => { const getHistoryHtml = () => {
...@@ -74,7 +75,7 @@ ${JSON.stringify(item.tools, null, 2)} ...@@ -74,7 +75,7 @@ ${JSON.stringify(item.tools, null, 2)}
fileDownload({ fileDownload({
text: html, text: html,
type: 'text/html', type: 'text/html',
filename: '聊天记录.html' filename: `${t('chat:chat_history')}.html`
}); });
}, },
pdf: () => { pdf: () => {
...@@ -84,7 +85,7 @@ ${JSON.stringify(item.tools, null, 2)} ...@@ -84,7 +85,7 @@ ${JSON.stringify(item.tools, null, 2)}
// @ts-ignore // @ts-ignore
html2pdf(html, { html2pdf(html, {
margin: 0, margin: 0,
filename: `聊天记录.pdf` filename: `${t('chat:chat_history')}.pdf`
}); });
} }
}; };
......
...@@ -372,7 +372,7 @@ const ChatBox = ( ...@@ -372,7 +372,7 @@ const ChatBox = (
if (!onStartChat) return; if (!onStartChat) return;
if (isChatting) { if (isChatting) {
toast({ toast({
title: '正在聊天中...请等待结束', title: t('chat:is_chatting'),
status: 'warning' status: 'warning'
}); });
return; return;
...@@ -384,7 +384,7 @@ const ChatBox = ( ...@@ -384,7 +384,7 @@ const ChatBox = (
if (!text && files.length === 0) { if (!text && files.length === 0) {
toast({ toast({
title: '内容为空', title: t('chat:content_empty'),
status: 'warning' status: 'warning'
}); });
return; return;
......
...@@ -5,10 +5,10 @@ import { PluginRunContext } from '../context'; ...@@ -5,10 +5,10 @@ import { PluginRunContext } from '../context';
import Markdown from '@/components/Markdown'; import Markdown from '@/components/Markdown';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant'; import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import AIResponseBox from '../../../components/AIResponseBox'; import AIResponseBox from '../../../components/AIResponseBox';
import { useTranslation } from 'next-i18next';
const RenderOutput = () => { const RenderOutput = () => {
const { histories, isChatting } = useContextSelector(PluginRunContext, (v) => v); const { histories, isChatting } = useContextSelector(PluginRunContext, (v) => v);
const { t } = useTranslation();
const pluginOutputs = useMemo(() => { const pluginOutputs = useMemo(() => {
const pluginOutputs = histories?.[1]?.responseData?.find( const pluginOutputs = histories?.[1]?.responseData?.find(
(item) => item.moduleType === FlowNodeTypeEnum.pluginOutput (item) => item.moduleType === FlowNodeTypeEnum.pluginOutput
...@@ -22,7 +22,7 @@ const RenderOutput = () => { ...@@ -22,7 +22,7 @@ const RenderOutput = () => {
<Box border={'base'} rounded={'md'} bg={'myGray.25'}> <Box border={'base'} rounded={'md'} bg={'myGray.25'}>
<Box p={4} color={'myGray.900'}> <Box p={4} color={'myGray.900'}>
<Box color={'myGray.900'} fontWeight={'bold'}> <Box color={'myGray.900'} fontWeight={'bold'}>
流输出 {t('chat:stream_output')}
</Box> </Box>
{histories.length > 0 && histories[1]?.value.length > 0 ? ( {histories.length > 0 && histories[1]?.value.length > 0 ? (
<Box mt={2}> <Box mt={2}>
...@@ -46,7 +46,7 @@ const RenderOutput = () => { ...@@ -46,7 +46,7 @@ const RenderOutput = () => {
</Box> </Box>
<Box border={'base'} mt={4} rounded={'md'} bg={'myGray.25'}> <Box border={'base'} mt={4} rounded={'md'} bg={'myGray.25'}>
<Box p={4} color={'myGray.900'} fontWeight={'bold'}> <Box p={4} color={'myGray.900'} fontWeight={'bold'}>
<Box>插件输出</Box> <Box>{t('chat:plugins_output')}</Box>
{histories.length > 0 && histories[1].responseData ? ( {histories.length > 0 && histories[1].responseData ? (
<Markdown source={`~~~json\n${pluginOutputs}`} /> <Markdown source={`~~~json\n${pluginOutputs}`} />
) : null} ) : null}
......
...@@ -3,14 +3,14 @@ import React from 'react'; ...@@ -3,14 +3,14 @@ import React from 'react';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
import { PluginRunContext } from '../context'; import { PluginRunContext } from '../context';
import { Box } from '@chakra-ui/react'; import { Box } from '@chakra-ui/react';
import { useTranslation } from 'next-i18next';
const RenderResponseDetail = () => { const RenderResponseDetail = () => {
const { histories, isChatting } = useContextSelector(PluginRunContext, (v) => v); const { histories, isChatting } = useContextSelector(PluginRunContext, (v) => v);
const { t } = useTranslation();
const responseData = histories?.[1]?.responseData || []; const responseData = histories?.[1]?.responseData || [];
return isChatting ? ( return isChatting ? (
<>{'进行中'}</> <>{t('chat:in_progress')}</>
) : ( ) : (
<Box flex={'1 0 0'} h={'100%'} overflow={'auto'}> <Box flex={'1 0 0'} h={'100%'} overflow={'auto'}>
<ResponseBox useMobile={true} response={responseData} showDetail={true} /> <ResponseBox useMobile={true} response={responseData} showDetail={true} />
......
...@@ -11,7 +11,7 @@ import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/c ...@@ -11,7 +11,7 @@ import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/c
import { generatingMessageProps } from '../type'; import { generatingMessageProps } from '../type';
import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants'; import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants';
import { getPluginRunContent } from '@fastgpt/global/core/app/plugin/utils'; import { getPluginRunContent } from '@fastgpt/global/core/app/plugin/utils';
import { useTranslation } from 'next-i18next';
type PluginRunContextType = PluginRunBoxProps & { type PluginRunContextType = PluginRunBoxProps & {
isChatting: boolean; isChatting: boolean;
onSubmit: (e: FieldValues) => Promise<any>; onSubmit: (e: FieldValues) => Promise<any>;
...@@ -44,7 +44,7 @@ const PluginRunContextProvider = ({ ...@@ -44,7 +44,7 @@ const PluginRunContextProvider = ({
const { toast } = useToast(); const { toast } = useToast();
const chatController = useRef(new AbortController()); const chatController = useRef(new AbortController());
const { t } = useTranslation();
/* Abort chat completions, questionGuide */ /* Abort chat completions, questionGuide */
const abortRequest = useCallback(() => { const abortRequest = useCallback(() => {
chatController.current?.abort('stop'); chatController.current?.abort('stop');
...@@ -148,7 +148,7 @@ const PluginRunContextProvider = ({ ...@@ -148,7 +148,7 @@ const PluginRunContextProvider = ({
if (!onStartChat) return; if (!onStartChat) return;
if (isChatting) { if (isChatting) {
toast({ toast({
title: '正在聊天中...请等待结束', title: t('chat:is_chatting'),
status: 'warning' status: 'warning'
}); });
return; return;
......
...@@ -119,7 +119,7 @@ const WholeResponseModal = ({ ...@@ -119,7 +119,7 @@ const WholeResponseModal = ({
title={ title={
<Flex alignItems={'center'}> <Flex alignItems={'center'}>
{t('common:core.chat.response.Complete Response')} {t('common:core.chat.response.Complete Response')}
<QuestionTip ml={2} label={'从上到下,为各个模块的响应顺序'}></QuestionTip> <QuestionTip ml={2} label={t('chat:question_tip')}></QuestionTip>
</Flex> </Flex>
} }
> >
......
...@@ -62,7 +62,7 @@ const ApiKeyTable = ({ tips, appId }: { tips: string; appId?: string }) => { ...@@ -62,7 +62,7 @@ const ApiKeyTable = ({ tips, appId }: { tips: string; appId?: string }) => {
const [apiKey, setApiKey] = useState(''); const [apiKey, setApiKey] = useState('');
const { ConfirmModal, openConfirm } = useConfirm({ const { ConfirmModal, openConfirm } = useConfirm({
type: 'delete', type: 'delete',
content: '确认删除该API密钥?删除后该密钥立即失效,对应的对话日志不会删除,请确认!' content: t('workflow:delete_api')
}); });
const { mutate: onclickRemove, isLoading: isDeleting } = useMutation({ const { mutate: onclickRemove, isLoading: isDeleting } = useMutation({
...@@ -318,7 +318,7 @@ function EditKeyModal({ ...@@ -318,7 +318,7 @@ function EditKeyModal({
const { mutate: onclickCreate, isLoading: creating } = useRequest({ const { mutate: onclickCreate, isLoading: creating } = useRequest({
mutationFn: async (e: EditProps) => createAOpenApiKey(e), mutationFn: async (e: EditProps) => createAOpenApiKey(e),
errorToast: '创建链接异常', errorToast: t('workflow:create_link_error'),
onSuccess: onCreate onSuccess: onCreate
}); });
const { mutate: onclickUpdate, isLoading: updating } = useRequest({ const { mutate: onclickUpdate, isLoading: updating } = useRequest({
...@@ -326,7 +326,7 @@ function EditKeyModal({ ...@@ -326,7 +326,7 @@ function EditKeyModal({
//@ts-ignore //@ts-ignore
return putOpenApiKey(e); return putOpenApiKey(e);
}, },
errorToast: '更新链接异常', errorToast: t('workflow:update_link_error'),
onSuccess: onEdit onSuccess: onEdit
}); });
......
...@@ -73,7 +73,7 @@ const LafAccountModal = ({ ...@@ -73,7 +73,7 @@ const LafAccountModal = ({
onError: (err) => { onError: (err) => {
onResetForm(); onResetForm();
toast({ toast({
title: getErrText(err, '获取应用列表失败'), title: getErrText(err, t('common:get_app_failed')),
status: 'error' status: 'error'
}); });
} }
...@@ -132,7 +132,7 @@ const LafAccountModal = ({ ...@@ -132,7 +132,7 @@ const LafAccountModal = ({
}} }}
isLoading={isPatLoading} isLoading={isPatLoading}
> >
验证 {t('common:verification')}
</Button> </Button>
</> </>
) : ( ) : (
...@@ -145,7 +145,7 @@ const LafAccountModal = ({ ...@@ -145,7 +145,7 @@ const LafAccountModal = ({
}); });
}} }}
> >
已验证,点击取消绑定 {t('common:has_verification')}
</Button> </Button>
)} )}
</Flex> </Flex>
......
...@@ -5,7 +5,7 @@ import type { PermissionValueType } from '@fastgpt/global/support/permission/typ ...@@ -5,7 +5,7 @@ import type { PermissionValueType } from '@fastgpt/global/support/permission/typ
import { ReadPermissionVal, WritePermissionVal } from '@fastgpt/global/support/permission/constant'; import { ReadPermissionVal, WritePermissionVal } from '@fastgpt/global/support/permission/constant';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm'; import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import { useI18n } from '@/web/context/I18n'; import { useTranslation } from 'react-i18next';
export enum defaultPermissionEnum { export enum defaultPermissionEnum {
private = 'private', private = 'private',
...@@ -34,12 +34,11 @@ const DefaultPermissionList = ({ ...@@ -34,12 +34,11 @@ const DefaultPermissionList = ({
...styles ...styles
}: Props) => { }: Props) => {
const { ConfirmModal, openConfirm } = useConfirm({}); const { ConfirmModal, openConfirm } = useConfirm({});
const { commonT } = useI18n(); const { t } = useTranslation();
const defaultPermissionSelectList = [ const defaultPermissionSelectList = [
{ label: '仅协作者访问', value: defaultPer }, { label: t('user:permission.only_collaborators'), value: defaultPer },
{ label: '团队可访问', value: readPer }, { label: t('user:permission.team_read'), value: readPer },
{ label: '团队可编辑', value: writePer } { label: t('user:permission.team_write'), value: writePer }
]; ];
const { runAsync: onRequestChange, loading } = useRequest2((v: PermissionValueType) => const { runAsync: onRequestChange, loading } = useRequest2((v: PermissionValueType) =>
...@@ -58,7 +57,7 @@ const DefaultPermissionList = ({ ...@@ -58,7 +57,7 @@ const DefaultPermissionList = ({
openConfirm( openConfirm(
() => onRequestChange(per), () => onRequestChange(per),
undefined, undefined,
commonT('permission.Remove InheritPermission Confirm') t('common:permission.Remove InheritPermission Confirm')
)(); )();
} else { } else {
return onRequestChange(per); return onRequestChange(per);
......
...@@ -79,7 +79,13 @@ function AddMemberModal({ onClose }: AddModalPropsType) { ...@@ -79,7 +79,13 @@ function AddMemberModal({ onClose }: AddModalPropsType) {
}); });
return ( return (
<MyModal isOpen onClose={onClose} iconSrc="modal/AddClb" title="添加协作者" minW="800px"> <MyModal
isOpen
onClose={onClose}
iconSrc="modal/AddClb"
title={t('user:team.add_collaborator')}
minW="800px"
>
<ModalBody> <ModalBody>
<MyBox <MyBox
isLoading={loadingMembers} isLoading={loadingMembers}
...@@ -103,7 +109,7 @@ function AddMemberModal({ onClose }: AddModalPropsType) { ...@@ -103,7 +109,7 @@ function AddMemberModal({ onClose }: AddModalPropsType) {
<MyIcon name="common/searchLight" w="16px" color={'myGray.500'} /> <MyIcon name="common/searchLight" w="16px" color={'myGray.500'} />
</InputLeftElement> </InputLeftElement>
<Input <Input
placeholder="搜索用户名" placeholder={t('user:search_user')}
bgColor="myGray.50" bgColor="myGray.50"
onChange={(e) => setSearchText(e.target.value)} onChange={(e) => setSearchText(e.target.value)}
/> />
...@@ -156,7 +162,9 @@ function AddMemberModal({ onClose }: AddModalPropsType) { ...@@ -156,7 +162,9 @@ function AddMemberModal({ onClose }: AddModalPropsType) {
</Flex> </Flex>
</Flex> </Flex>
<Flex p="4" flexDirection="column"> <Flex p="4" flexDirection="column">
<Box>已选: {selectedMemberIdList.length}</Box> <Box>
{t('user:has_chosen') + ': '}+ {selectedMemberIdList.length}
</Box>
<Flex flexDirection="column" mt="2"> <Flex flexDirection="column" mt="2">
{selectedMemberIdList.map((tmbId) => { {selectedMemberIdList.map((tmbId) => {
const member = filterMembers.find((v) => v.tmbId === tmbId); const member = filterMembers.find((v) => v.tmbId === tmbId);
...@@ -211,7 +219,7 @@ function AddMemberModal({ onClose }: AddModalPropsType) { ...@@ -211,7 +219,7 @@ function AddMemberModal({ onClose }: AddModalPropsType) {
onChange={(v) => setSelectedPermission(v)} onChange={(v) => setSelectedPermission(v)}
/> />
<Button isLoading={isUpdating} ml="4" h={'32px'} onClick={onConfirm}> <Button isLoading={isUpdating} ml="4" h={'32px'} onClick={onConfirm}>
确认 {t('common:common.Confirm')}
</Button> </Button>
</ModalFooter> </ModalFooter>
</MyModal> </MyModal>
......
...@@ -23,12 +23,13 @@ import { PermissionValueType } from '@fastgpt/global/support/permission/type'; ...@@ -23,12 +23,13 @@ import { PermissionValueType } from '@fastgpt/global/support/permission/type';
import { useUserStore } from '@/web/support/user/useUserStore'; import { useUserStore } from '@/web/support/user/useUserStore';
import EmptyTip from '@fastgpt/web/components/common/EmptyTip'; import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
import Loading from '@fastgpt/web/components/common/MyLoading'; import Loading from '@fastgpt/web/components/common/MyLoading';
import { useTranslation } from 'next-i18next';
export type ManageModalProps = { export type ManageModalProps = {
onClose: () => void; onClose: () => void;
}; };
function ManageModal({ onClose }: ManageModalProps) { function ManageModal({ onClose }: ManageModalProps) {
const { t } = useTranslation();
const { userInfo } = useUserStore(); const { userInfo } = useUserStore();
const { permission, collaboratorList, onUpdateCollaborators, onDelOneCollaborator } = const { permission, collaboratorList, onUpdateCollaborators, onDelOneCollaborator } =
useContextSelector(CollaboratorContext, (v) => v); useContextSelector(CollaboratorContext, (v) => v);
...@@ -44,23 +45,29 @@ function ManageModal({ onClose }: ManageModalProps) { ...@@ -44,23 +45,29 @@ function ManageModal({ onClose }: ManageModalProps) {
permission: per permission: per
}); });
}, },
successToast: '更新成功', successToast: t('common.Update Success'),
errorToast: 'Error' errorToast: 'Error'
}); });
const loading = isDeleting || isUpdating; const loading = isDeleting || isUpdating;
return ( return (
<MyModal isOpen onClose={onClose} minW="600px" title="管理协作者" iconSrc="common/settingLight"> <MyModal
isOpen
onClose={onClose}
minW="600px"
title={t('user:team.manage_collaborators')}
iconSrc="common/settingLight"
>
<ModalBody> <ModalBody>
<TableContainer borderRadius="md" minH="400px"> <TableContainer borderRadius="md" minH="400px">
<Table> <Table>
<Thead bg="myGray.100"> <Thead bg="myGray.100">
<Tr> <Tr>
<Th border="none">名称</Th> <Th border="none">{t('user:name')}</Th>
<Th border="none">权限</Th> <Th border="none">{t('user:permissions')}</Th>
<Th border="none" w={'40px'}> <Th border="none" w={'40px'}>
操作 {t('user:operations')}
</Th> </Th>
</Tr> </Tr>
</Thead> </Thead>
...@@ -109,7 +116,7 @@ function ManageModal({ onClose }: ManageModalProps) { ...@@ -109,7 +116,7 @@ function ManageModal({ onClose }: ManageModalProps) {
})} })}
</Tbody> </Tbody>
</Table> </Table>
{collaboratorList?.length === 0 && <EmptyTip text={'暂无协作者'} />} {collaboratorList?.length === 0 && <EmptyTip text={t('user:team.no_collaborators')} />}
</TableContainer> </TableContainer>
{loading && <Loading fixed={false} />} {loading && <Loading fixed={false} />}
</ModalBody> </ModalBody>
......
...@@ -47,8 +47,8 @@ function AddManagerModal({ onClose, onSuccess }: { onClose: () => void; onSucces ...@@ -47,8 +47,8 @@ function AddManagerModal({ onClose, onSuccess }: { onClose: () => void; onSucces
refetchMembers(); refetchMembers();
onSuccess(); onSuccess();
}, },
successToast: '成功', successToast: t('common:common.Success'),
errorToast: '失败' errorToast: t('common:common.failed')
}); });
const filterMembers = useMemo(() => { const filterMembers = useMemo(() => {
...@@ -83,7 +83,7 @@ function AddManagerModal({ onClose, onSuccess }: { onClose: () => void; onSucces ...@@ -83,7 +83,7 @@ function AddManagerModal({ onClose, onSuccess }: { onClose: () => void; onSucces
<MyIcon name="common/searchLight" w="16px" color={'myGray.500'} /> <MyIcon name="common/searchLight" w="16px" color={'myGray.500'} />
</InputLeftElement> </InputLeftElement>
<Input <Input
placeholder="搜索用户名" placeholder={t('user:search_user')}
fontSize="sm" fontSize="sm"
bg={'myGray.50'} bg={'myGray.50'}
onChange={(e) => { onChange={(e) => {
...@@ -120,7 +120,7 @@ function AddManagerModal({ onClose, onSuccess }: { onClose: () => void; onSucces ...@@ -120,7 +120,7 @@ function AddManagerModal({ onClose, onSuccess }: { onClose: () => void; onSucces
</Flex> </Flex>
</Flex> </Flex>
<Flex borderLeft="1px" borderColor="myGray.200" flexDirection="column" p="4"> <Flex borderLeft="1px" borderColor="myGray.200" flexDirection="column" p="4">
<Box mt={3}>已选: {selected.length} 个</Box> <Box mt={3}>{t('common:chosen') + ': ' + selected.length} </Box>
<Box mt={5}> <Box mt={5}>
{selected.map((member) => { {selected.map((member) => {
return ( return (
......
...@@ -31,8 +31,8 @@ function PermissionManage() { ...@@ -31,8 +31,8 @@ function PermissionManage() {
mutationFn: async (memberId: string) => { mutationFn: async (memberId: string) => {
return delMemberPermission(memberId); return delMemberPermission(memberId);
}, },
successToast: '删除管理员成功', successToast: t('user:delete.admin_success'),
errorToast: '删除管理员异常', errorToast: t('user:delete.admin_failed'),
onSuccess: () => { onSuccess: () => {
refetchMembers(); refetchMembers();
} }
...@@ -75,7 +75,7 @@ function PermissionManage() { ...@@ -75,7 +75,7 @@ function PermissionManage() {
onOpenAddManager(); onOpenAddManager();
}} }}
> >
添加管理员 {t('user:team.Add manager')}
</Button> </Button>
)} )}
</Flex> </Flex>
......
...@@ -95,7 +95,7 @@ const TeamTagsAsync = ({ onClose }: { onClose: () => void }) => { ...@@ -95,7 +95,7 @@ const TeamTagsAsync = ({ onClose }: { onClose: () => void }) => {
<Box> <Box>
<Box>{teamInfo?.teamName}</Box> <Box>{teamInfo?.teamName}</Box>
<Box color={'myGray.500'} fontSize={'xs'} fontWeight={'normal'}> <Box color={'myGray.500'} fontSize={'xs'} fontWeight={'normal'}>
{'填写标签同步链接,点击同步按钮即可同步'} {t('user:synchronization.title')}
</Box> </Box>
</Box> </Box>
} }
...@@ -110,7 +110,7 @@ const TeamTagsAsync = ({ onClose }: { onClose: () => void }) => { ...@@ -110,7 +110,7 @@ const TeamTagsAsync = ({ onClose }: { onClose: () => void }) => {
ml={4} ml={4}
autoFocus autoFocus
bg={'myWhite.600'} bg={'myWhite.600'}
placeholder="请输入同步标签" placeholder={t('user:synchronization.placeholder')}
{...register('teamDomain', { {...register('teamDomain', {
required: true required: true
})} })}
...@@ -181,7 +181,7 @@ const TeamTagsAsync = ({ onClose }: { onClose: () => void }) => { ...@@ -181,7 +181,7 @@ const TeamTagsAsync = ({ onClose }: { onClose: () => void }) => {
leftIcon={<RepeatIcon />} leftIcon={<RepeatIcon />}
onClick={handleSubmit((data) => onclickTagAsync(data))} onClick={handleSubmit((data) => onclickTagAsync(data))}
> >
立即同步 {t('user:synchronization.button')}
</Button> </Button>
</Flex> </Flex>
</ModalBody> </ModalBody>
......
...@@ -73,7 +73,7 @@ const QRCodePayModal = ({ ...@@ -73,7 +73,7 @@ const QRCodePayModal = ({
return ( return (
<MyModal isOpen title={t('common:user.Pay')} iconSrc="/imgs/modal/pay.svg"> <MyModal isOpen title={t('common:user.Pay')} iconSrc="/imgs/modal/pay.svg">
<ModalBody textAlign={'center'}> <ModalBody textAlign={'center'}>
<Box mb={3}>请微信扫码支付: {readPrice}元,请勿关闭页面</Box> <Box mb={3}>{t('common:pay.wechat', { price: readPrice })}</Box>
<Box id={'payQRCode'} display={'inline-block'} h={'128px'}></Box> <Box id={'payQRCode'} display={'inline-block'} h={'128px'}></Box>
</ModalBody> </ModalBody>
<ModalFooter /> <ModalFooter />
......
...@@ -112,13 +112,13 @@ const StandardPlanContentList = ({ ...@@ -112,13 +112,13 @@ const StandardPlanContentList = ({
{!!planContent.permissionReRank && ( {!!planContent.permissionReRank && (
<Flex alignItems={'center'}> <Flex alignItems={'center'}>
<MyIcon name={'price/right'} w={'16px'} mr={3} /> <MyIcon name={'price/right'} w={'16px'} mr={3} />
<Box color={'myGray.600'}>检索结果重排</Box> <Box color={'myGray.600'}>{t('chat:rearrangement')}</Box>
</Flex> </Flex>
)} )}
{!!planContent.permissionWebsiteSync && ( {!!planContent.permissionWebsiteSync && (
<Flex alignItems={'center'}> <Flex alignItems={'center'}>
<MyIcon name={'price/right'} w={'16px'} mr={3} /> <MyIcon name={'price/right'} w={'16px'} mr={3} />
<Box color={'myGray.600'}>Web站点同步</Box> <Box color={'myGray.600'}>{t('chat:web_site_sync')}</Box>
</Flex> </Flex>
)} )}
</Grid> </Grid>
......
import { AppTypeEnum } from '@fastgpt/global/core/app/constants'; import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { InitChatResponse } from './api'; import { InitChatResponse } from './api';
import { i18nT } from '@fastgpt/web/i18n/utils';
export const defaultChatData: InitChatResponse = { export const defaultChatData: InitChatResponse = {
chatId: '', chatId: '',
appId: '', appId: '',
...@@ -12,7 +12,7 @@ export const defaultChatData: InitChatResponse = { ...@@ -12,7 +12,7 @@ export const defaultChatData: InitChatResponse = {
type: AppTypeEnum.simple, type: AppTypeEnum.simple,
pluginInputs: [] pluginInputs: []
}, },
title: '新对话', title: i18nT('chat:new_chat'),
variables: {}, variables: {},
history: [] history: []
}; };
...@@ -8,13 +8,13 @@ import QueryClientContext from '@/web/context/QueryClient'; ...@@ -8,13 +8,13 @@ import QueryClientContext from '@/web/context/QueryClient';
import ChakraUIContext from '@/web/context/ChakraUI'; import ChakraUIContext from '@/web/context/ChakraUI';
import I18nContextProvider from '@/web/context/I18n'; import I18nContextProvider from '@/web/context/I18n';
import { useInitApp } from '@/web/context/useInitApp'; import { useInitApp } from '@/web/context/useInitApp';
import { useTranslation } from 'next-i18next';
import '@/web/styles/reset.scss'; import '@/web/styles/reset.scss';
import NextHead from '@/components/common/NextHead'; import NextHead from '@/components/common/NextHead';
function App({ Component, pageProps }: AppProps) { function App({ Component, pageProps }: AppProps) {
const { feConfigs, scripts, title } = useInitApp(); const { feConfigs, scripts, title } = useInitApp();
const { t } = useTranslation();
return ( return (
<> <>
<NextHead <NextHead
...@@ -22,7 +22,7 @@ function App({ Component, pageProps }: AppProps) { ...@@ -22,7 +22,7 @@ function App({ Component, pageProps }: AppProps) {
desc={ desc={
feConfigs?.systemDescription || feConfigs?.systemDescription ||
process.env.SYSTEM_DESCRIPTION || process.env.SYSTEM_DESCRIPTION ||
`${title} 是一个大模型应用编排系统,提供开箱即用的数据处理、模型调用等能力,可以快速的构建知识库并通过 Flow 可视化进行工作流编排,实现复杂的知识库场景!` `${title}${t('app:intro')}`
} }
icon={feConfigs?.favicon || process.env.SYSTEM_FAVICON} icon={feConfigs?.favicon || process.env.SYSTEM_FAVICON}
/> />
......
...@@ -33,9 +33,10 @@ import MySelect from '@fastgpt/web/components/common/MySelect'; ...@@ -33,9 +33,10 @@ import MySelect from '@fastgpt/web/components/common/MySelect';
import MyModal from '@fastgpt/web/components/common/MyModal'; import MyModal from '@fastgpt/web/components/common/MyModal';
import { usePagination } from '@fastgpt/web/hooks/usePagination'; import { usePagination } from '@fastgpt/web/hooks/usePagination';
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel'; import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
import { useI18n } from '@/web/context/I18n';
const BillTable = () => { const BillTable = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { commonT } = useI18n();
const { toast } = useToast(); const { toast } = useToast();
const [billType, setBillType] = useState<BillTypeEnum | ''>(''); const [billType, setBillType] = useState<BillTypeEnum | ''>('');
const [billDetail, setBillDetail] = useState<BillSchemaType>(); const [billDetail, setBillDetail] = useState<BillSchemaType>();
...@@ -134,7 +135,7 @@ const BillTable = () => { ...@@ -134,7 +135,7 @@ const BillTable = () => {
<Td> <Td>
{item.createTime ? dayjs(item.createTime).format('YYYY/MM/DD HH:mm:ss') : '-'} {item.createTime ? dayjs(item.createTime).format('YYYY/MM/DD HH:mm:ss') : '-'}
</Td> </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>{t(billStatusMap[item.status]?.label as any)}</Td>
<Td> <Td>
{item.status === 'NOTPAY' && ( {item.status === 'NOTPAY' && (
...@@ -181,7 +182,7 @@ export default BillTable; ...@@ -181,7 +182,7 @@ export default BillTable;
function BillDetailModal({ bill, onClose }: { bill: BillSchemaType; onClose: () => void }) { function BillDetailModal({ bill, onClose }: { bill: BillSchemaType; onClose: () => void }) {
const { t } = useTranslation(); const { t } = useTranslation();
const { commonT } = useI18n();
return ( return (
<MyModal <MyModal
isOpen={true} isOpen={true}
...@@ -211,7 +212,7 @@ function BillDetailModal({ bill, onClose }: { bill: BillSchemaType; onClose: () ...@@ -211,7 +212,7 @@ function BillDetailModal({ bill, onClose }: { bill: BillSchemaType; onClose: ()
)} )}
<Flex alignItems={'center'} pb={4}> <Flex alignItems={'center'} pb={4}>
<FormLabel flex={'0 0 120px'}>{t('common:support.wallet.Amount')}:</FormLabel> <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>
<Flex alignItems={'center'} pb={4}> <Flex alignItems={'center'} pb={4}>
<FormLabel flex={'0 0 120px'}>{t('common:support.wallet.bill.Type')}:</FormLabel> <FormLabel flex={'0 0 120px'}>{t('common:support.wallet.bill.Type')}:</FormLabel>
......
...@@ -435,7 +435,7 @@ const PlanUsage = () => { ...@@ -435,7 +435,7 @@ const PlanUsage = () => {
<Box ml={2}>{formatTime2YMD(standardPlan?.expiredTime)}</Box> <Box ml={2}>{formatTime2YMD(standardPlan?.expiredTime)}</Box>
</Flex> </Flex>
<Box mt="2" color={'#485264'} fontSize="sm"> <Box mt="2" color={'#485264'} fontSize="sm">
免费版用户30天无任何使用记录时,系统会自动清理账号知识库。 {t('common:info.free_plan')}
</Box> </Box>
</> </>
) : ( ) : (
...@@ -470,9 +470,9 @@ const PlanUsage = () => { ...@@ -470,9 +470,9 @@ const PlanUsage = () => {
> >
<Flex> <Flex>
<Flex flex={'1 0 0'} alignItems={'flex-end'}> <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'}> <Box fontSize={'xs'} color={'myGray.500'}>
(包含标准套餐与额外资源包) {t('common:info.include')}
</Box> </Box>
</Flex> </Flex>
<Link <Link
...@@ -484,7 +484,7 @@ const PlanUsage = () => { ...@@ -484,7 +484,7 @@ const PlanUsage = () => {
cursor={'pointer'} cursor={'pointer'}
fontSize={'sm'} fontSize={'sm'}
> >
购买额外套餐 {t('common:info.buy_extra')}
<MyIcon ml={1} name={'common/rightArrowLight'} w={'12px'} /> <MyIcon ml={1} name={'common/rightArrowLight'} w={'12px'} />
</Link> </Link>
</Flex> </Flex>
...@@ -567,7 +567,7 @@ const Other = () => { ...@@ -567,7 +567,7 @@ const Other = () => {
}); });
reset(data); reset(data);
toast({ toast({
title: '更新数据成功', title: t('common:dataset.data.Update Success Tip'),
status: 'success' status: 'success'
}); });
}, },
...@@ -637,7 +637,7 @@ const Other = () => { ...@@ -637,7 +637,7 @@ const Other = () => {
> >
<Image src="/imgs/workflow/laf.png" w={'18px'} alt="laf" /> <Image src="/imgs/workflow/laf.png" w={'18px'} alt="laf" />
<Box ml={2} flex={1}> <Box ml={2} flex={1}>
laf 账号 {'laf' + t('common:navbar.Account')}
</Box> </Box>
<Box <Box
w={'9px'} w={'9px'}
...@@ -664,7 +664,7 @@ const Other = () => { ...@@ -664,7 +664,7 @@ const Other = () => {
> >
<MyIcon name={'common/openai'} w={'18px'} color={'myGray.600'} /> <MyIcon name={'common/openai'} w={'18px'} color={'myGray.600'} />
<Box ml={2} flex={1}> <Box ml={2} flex={1}>
OpenAI/OneAPI 账号 {'OpenAI / OneAPI' + t('common:navbar.Account')}
</Box> </Box>
<Box <Box
w={'9px'} w={'9px'}
......
...@@ -81,7 +81,9 @@ const InformTable = () => { ...@@ -81,7 +81,9 @@ const InformTable = () => {
)} )}
</Box> </Box>
))} ))}
{!isLoading && informs.length === 0 && <EmptyTip text={'暂无通知~'}></EmptyTip>} {!isLoading && informs.length === 0 && (
<EmptyTip text={t('common:user.no_notice')}></EmptyTip>
)}
</Box> </Box>
{total > pageSize && ( {total > pageSize && (
......
...@@ -37,9 +37,7 @@ const OpenAIAccountModal = ({ ...@@ -37,9 +37,7 @@ const OpenAIAccountModal = ({
> >
<ModalBody> <ModalBody>
<Box fontSize={'sm'} color={'myGray.500'}> <Box fontSize={'sm'} color={'myGray.500'}>
可以填写 OpenAI/OneAPI {t('common:info.open_api_notice')}
的相关秘钥。如果你填写了该内容,在线上平台使用【AI对话】、【问题分类】和【内容提取】将会走你填写的Key,不会计费。请注意你的
Key 是否有访问对应模型的权限。GPT模型可以选择 FastAI。
</Box> </Box>
<Flex alignItems={'center'} mt={5}> <Flex alignItems={'center'} mt={5}>
<Box flex={'0 0 65px'}>API Key:</Box> <Box flex={'0 0 65px'}>API Key:</Box>
...@@ -50,16 +48,16 @@ const OpenAIAccountModal = ({ ...@@ -50,16 +48,16 @@ const OpenAIAccountModal = ({
<Input <Input
flex={1} flex={1}
{...register('baseUrl')} {...register('baseUrl')}
placeholder={'请求地址,默认为 openai 官方。可填中转地址,未自动补全 "v1"'} placeholder={t('common:info.open_api_placeholder')}
></Input> ></Input>
</Flex> </Flex>
</ModalBody> </ModalBody>
<ModalFooter> <ModalFooter>
<Button mr={3} variant={'whiteBase'} onClick={onClose}> <Button mr={3} variant={'whiteBase'} onClick={onClose}>
取消 {t('common:common.Cancel')}
</Button> </Button>
<Button isLoading={isLoading} onClick={handleSubmit((data) => onSubmit(data))}> <Button isLoading={isLoading} onClick={handleSubmit((data) => onSubmit(data))}>
确认 {t('common:common.Confirm')}
</Button> </Button>
</ModalFooter> </ModalFooter>
</MyModal> </MyModal>
......
...@@ -7,7 +7,6 @@ import { getErrText } from '@fastgpt/global/common/error/utils'; ...@@ -7,7 +7,6 @@ import { getErrText } from '@fastgpt/global/common/error/utils';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import MyModal from '@fastgpt/web/components/common/MyModal'; import MyModal from '@fastgpt/web/components/common/MyModal';
import { BillTypeEnum } from '@fastgpt/global/support/wallet/bill/constants'; import { BillTypeEnum } from '@fastgpt/global/support/wallet/bill/constants';
import QRCodePayModal, { type QRPayProps } from '@/components/support/wallet/QRCodePayModal'; import QRCodePayModal, { type QRPayProps } from '@/components/support/wallet/QRCodePayModal';
import { useSystemStore } from '@/web/common/system/useSystemStore'; import { useSystemStore } from '@/web/common/system/useSystemStore';
import { EXTRA_PLAN_CARD_ROUTE } from '@/web/support/wallet/sub/constants'; import { EXTRA_PLAN_CARD_ROUTE } from '@/web/support/wallet/sub/constants';
...@@ -27,7 +26,6 @@ const PayModal = ({ ...@@ -27,7 +26,6 @@ const PayModal = ({
const [inputVal, setInputVal] = useState<number | undefined>(defaultValue); const [inputVal, setInputVal] = useState<number | undefined>(defaultValue);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [qrPayData, setQRPayData] = useState<QRPayProps>(); const [qrPayData, setQRPayData] = useState<QRPayProps>();
const handleClickPay = useCallback(async () => { const handleClickPay = useCallback(async () => {
if (!inputVal || inputVal <= 0 || isNaN(+inputVal)) return; if (!inputVal || inputVal <= 0 || isNaN(+inputVal)) return;
setLoading(true); setLoading(true);
...@@ -79,7 +77,7 @@ const PayModal = ({ ...@@ -79,7 +77,7 @@ const PayModal = ({
variant={item === inputVal ? 'solid' : 'outline'} variant={item === inputVal ? 'solid' : 'outline'}
onClick={() => setInputVal(item)} onClick={() => setInputVal(item)}
> >
{item}元 {t('common:pay.yuan', { amount: item })}
</Button> </Button>
))} ))}
</Grid> </Grid>
...@@ -88,7 +86,7 @@ const PayModal = ({ ...@@ -88,7 +86,7 @@ const PayModal = ({
value={inputVal} value={inputVal}
type={'number'} type={'number'}
step={1} step={1}
placeholder={'其他金额,请取整数'} placeholder={t('common:pay.other')}
onChange={(e) => { onChange={(e) => {
setInputVal(Math.floor(+e.target.value)); setInputVal(Math.floor(+e.target.value));
}} }}
...@@ -106,7 +104,7 @@ const PayModal = ({ ...@@ -106,7 +104,7 @@ const PayModal = ({
isDisabled={!inputVal || inputVal === 0} isDisabled={!inputVal || inputVal === 0}
onClick={handleClickPay} onClick={handleClickPay}
> >
获取充值二维码 {t('common:pay.get_pay_QR')}
</Button> </Button>
</ModalFooter> </ModalFooter>
......
...@@ -105,9 +105,9 @@ const Promotion = () => { ...@@ -105,9 +105,9 @@ const Promotion = () => {
<Table> <Table>
<Thead> <Thead>
<Tr> <Tr>
<Th>时间</Th> <Th>{t('common:user.Time')}</Th>
<Th>类型</Th> <Th>{t('common:user.type')}</Th>
<Th>金额(¥)</Th> <Th>{t('common:pay.amount')}</Th>
</Tr> </Tr>
</Thead> </Thead>
<Tbody fontSize={'sm'}> <Tbody fontSize={'sm'}>
...@@ -124,7 +124,9 @@ const Promotion = () => { ...@@ -124,7 +124,9 @@ const Promotion = () => {
</Table> </Table>
</TableContainer> </TableContainer>
{!isLoading && promotionRecords.length === 0 && <EmptyTip text="无邀请记录~"></EmptyTip>} {!isLoading && promotionRecords.length === 0 && (
<EmptyTip text={t('common:user.no_invite_records')}></EmptyTip>
)}
{total > pageSize && ( {total > pageSize && (
<Flex mt={4} justifyContent={'flex-end'}> <Flex mt={4} justifyContent={'flex-end'}>
<Pagination /> <Pagination />
......
...@@ -70,16 +70,16 @@ const UpdateNotificationModal = ({ onClose }: { onClose: () => void }) => { ...@@ -70,16 +70,16 @@ const UpdateNotificationModal = ({ onClose }: { onClose: () => void }) => {
flex={1} flex={1}
bg={'myGray.50'} bg={'myGray.50'}
{...register('account', { required: true })} {...register('account', { required: true })}
placeholder={t('common:support.user.Email Or Phone')} placeholder={t('user:password.email_phone')}
></Input> ></Input>
</Flex> </Flex>
<Flex mt="6" alignItems="center" position={'relative'}> <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 <Input
flex={1} flex={1}
bg={'myGray.50'} bg={'myGray.50'}
{...register('verifyCode', { required: true })} {...register('verifyCode', { required: true })}
placeholder={t('common:support.user.Verify Code')} placeholder={t('user:password.code_required')}
></Input> ></Input>
<Box <Box
position={'absolute'} position={'absolute'}
......
...@@ -45,11 +45,11 @@ const UpdatePswModal = ({ onClose }: { onClose: () => void }) => { ...@@ -45,11 +45,11 @@ const UpdatePswModal = ({ onClose }: { onClose: () => void }) => {
> >
<ModalBody> <ModalBody>
<Flex alignItems={'center'}> <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> <Input flex={1} type={'password'} {...register('oldPsw', { required: true })}></Input>
</Flex> </Flex>
<Flex alignItems={'center'} mt={5}> <Flex alignItems={'center'} mt={5}>
<Box flex={'0 0 70px'}>新密码:</Box> <Box flex={'0 0 70px'}>{t('common:user.new_password') + ':'}</Box>
<Input <Input
flex={1} flex={1}
type={'password'} type={'password'}
...@@ -57,13 +57,13 @@ const UpdatePswModal = ({ onClose }: { onClose: () => void }) => { ...@@ -57,13 +57,13 @@ const UpdatePswModal = ({ onClose }: { onClose: () => void }) => {
required: true, required: true,
maxLength: { maxLength: {
value: 60, value: 60,
message: '密码最少 4 位最多 60 位' message: t('common:user.password_message')
} }
})} })}
></Input> ></Input>
</Flex> </Flex>
<Flex alignItems={'center'} mt={5}> <Flex alignItems={'center'} mt={5}>
<Box flex={'0 0 70px'}>确认密码:</Box> <Box flex={'0 0 70px'}>{t('common:user.confirm_password') + ':'}</Box>
<Input <Input
flex={1} flex={1}
type={'password'} type={'password'}
...@@ -71,7 +71,7 @@ const UpdatePswModal = ({ onClose }: { onClose: () => void }) => { ...@@ -71,7 +71,7 @@ const UpdatePswModal = ({ onClose }: { onClose: () => void }) => {
required: true, required: true,
maxLength: { maxLength: {
value: 60, value: 60,
message: '密码最少 4 位最多 60 位' message: t('common:user.password_message')
} }
})} })}
></Input> ></Input>
...@@ -79,10 +79,10 @@ const UpdatePswModal = ({ onClose }: { onClose: () => void }) => { ...@@ -79,10 +79,10 @@ const UpdatePswModal = ({ onClose }: { onClose: () => void }) => {
</ModalBody> </ModalBody>
<ModalFooter> <ModalFooter>
<Button mr={3} variant={'whiteBase'} onClick={onClose}> <Button mr={3} variant={'whiteBase'} onClick={onClose}>
取消 {t('common:common.Cancel')}
</Button> </Button>
<Button isLoading={isLoading} onClick={handleSubmit((data) => onSubmit(data))}> <Button isLoading={isLoading} onClick={handleSubmit((data) => onSubmit(data))}>
确认 {t('common:common.Confirm')}
</Button> </Button>
</ModalFooter> </ModalFooter>
</MyModal> </MyModal>
......
...@@ -174,7 +174,7 @@ const UsageTable = () => { ...@@ -174,7 +174,7 @@ const UsageTable = () => {
<Td>{formatNumber(item.totalPoints) || 0}</Td> <Td>{formatNumber(item.totalPoints) || 0}</Td>
<Td> <Td>
<Button size={'sm'} variant={'whitePrimary'} onClick={() => setUsageDetail(item)}> <Button size={'sm'} variant={'whitePrimary'} onClick={() => setUsageDetail(item)}>
详情 {t('common:common.Detail')}
</Button> </Button>
</Td> </Td>
</Tr> </Tr>
...@@ -183,7 +183,9 @@ const UsageTable = () => { ...@@ -183,7 +183,9 @@ const UsageTable = () => {
</Table> </Table>
</TableContainer> </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} /> <Loading loading={isLoading} fixed={false} />
{!!usageDetail && ( {!!usageDetail && (
......
...@@ -77,10 +77,12 @@ const StandDetailModal = ({ onClose }: { onClose: () => void }) => { ...@@ -77,10 +77,12 @@ const StandDetailModal = ({ onClose }: { onClose: () => void }) => {
{currentSubLevel && {currentSubLevel &&
`(${t(standardSubLevelMap[currentSubLevel]?.label as any)})`} `(${t(standardSubLevelMap[currentSubLevel]?.label as any)})`}
</Td> </Td>
<Td>{datasetSize ? `${datasetSize}组` : '-'}</Td> <Td>
{datasetSize ? `${datasetSize + t('common:core.dataset.data.group')}` : '-'}
</Td>
<Td> <Td>
{totalPoints {totalPoints
? `${Math.round(totalPoints - surplusPoints)} / ${totalPoints} 积分` ? `${Math.round(totalPoints - surplusPoints)} / ${totalPoints} ${t('common:support.wallet.subscription.point')}`
: '-'} : '-'}
</Td> </Td>
<Td>{formatTime2YMDHM(startTime)}</Td> <Td>{formatTime2YMDHM(startTime)}</Td>
......
...@@ -104,7 +104,7 @@ const Account = ({ currentTab }: { currentTab: TabEnum }) => { ...@@ -104,7 +104,7 @@ const Account = ({ currentTab }: { currentTab: TabEnum }) => {
]; ];
const { openConfirm, ConfirmModal } = useConfirm({ const { openConfirm, ConfirmModal } = useConfirm({
content: '确认退出登录?' content: t('common:support.user.logout.confirm')
}); });
const router = useRouter(); const router = useRouter();
......
...@@ -14,6 +14,7 @@ import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant'; ...@@ -14,6 +14,7 @@ import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant'; import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import { transformPreviewHistories } from '@/global/core/chat/utils'; import { transformPreviewHistories } from '@/global/core/chat/utils';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants'; import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { i18nT } from '@fastgpt/web/i18n/utils';
async function handler( async function handler(
req: NextApiRequest, req: NextApiRequest,
res: NextApiResponse res: NextApiResponse
...@@ -61,7 +62,7 @@ async function handler( ...@@ -61,7 +62,7 @@ async function handler(
return { return {
chatId, chatId,
appId, appId,
title: chat?.title || '新对话', title: chat?.title || i18nT('chat:new_chat'),
userAvatar: undefined, userAvatar: undefined,
variables: chat?.variables || {}, variables: chat?.variables || {},
history: app.type === AppTypeEnum.plugin ? histories : transformPreviewHistories(histories), history: app.type === AppTypeEnum.plugin ? histories : transformPreviewHistories(histories),
......
...@@ -18,6 +18,7 @@ import { getAppLatestVersion } from '@fastgpt/service/core/app/controller'; ...@@ -18,6 +18,7 @@ import { getAppLatestVersion } from '@fastgpt/service/core/app/controller';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant'; import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants'; import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { transformPreviewHistories } from '@/global/core/chat/utils'; import { transformPreviewHistories } from '@/global/core/chat/utils';
import { i18nT } from '@fastgpt/web/i18n/utils';
export default async function handler(req: NextApiRequest, res: NextApiResponse) { export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try { try {
await connectToDatabase(); await connectToDatabase();
...@@ -69,7 +70,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) ...@@ -69,7 +70,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
data: { data: {
chatId, chatId,
appId: app._id, appId: app._id,
title: chat?.title || '新对话', title: chat?.title || i18nT('chat:new_chat'),
//@ts-ignore //@ts-ignore
userAvatar: tmb?.userId?.avatar, userAvatar: tmb?.userId?.avatar,
variables: chat?.variables || {}, variables: chat?.variables || {},
......
...@@ -18,6 +18,7 @@ import { getAppLatestVersion } from '@fastgpt/service/core/app/controller'; ...@@ -18,6 +18,7 @@ import { getAppLatestVersion } from '@fastgpt/service/core/app/controller';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant'; import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants'; import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { transformPreviewHistories } from '@/global/core/chat/utils'; import { transformPreviewHistories } from '@/global/core/chat/utils';
import { i18nT } from '@fastgpt/web/i18n/utils';
export default async function handler(req: NextApiRequest, res: NextApiResponse) { export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try { try {
...@@ -72,7 +73,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) ...@@ -72,7 +73,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
data: { data: {
chatId, chatId,
appId, appId,
title: chat?.title || '新对话', title: chat?.title || i18nT('chat:new_chat'),
userAvatar: team?.avatar, userAvatar: team?.avatar,
variables: chat?.variables || {}, variables: chat?.variables || {},
history: app.type === AppTypeEnum.plugin ? histories : transformPreviewHistories(histories), history: app.type === AppTypeEnum.plugin ? histories : transformPreviewHistories(histories),
......
...@@ -146,7 +146,7 @@ const InfoModal = ({ onClose }: { onClose: () => void }) => { ...@@ -146,7 +146,7 @@ const InfoModal = ({ onClose }: { onClose: () => void }) => {
() => resumeInheritPer(appDetail._id), () => resumeInheritPer(appDetail._id),
// () => putAppById(appDetail._id, { inheritPermission: true }), // () => putAppById(appDetail._id, { inheritPermission: true }),
{ {
errorToast: '恢复失败', errorToast: t('common:resume_failed'),
onSuccess: () => { onSuccess: () => {
reloadApp(); reloadApp();
} }
......
...@@ -17,7 +17,6 @@ import CloseIcon from '@fastgpt/web/components/common/Icon/close'; ...@@ -17,7 +17,6 @@ import CloseIcon from '@fastgpt/web/components/common/Icon/close';
import ChatBox from '@/components/core/chat/ChatContainer/ChatBox'; import ChatBox from '@/components/core/chat/ChatContainer/ChatBox';
import { useSystem } from '@fastgpt/web/hooks/useSystem'; import { useSystem } from '@fastgpt/web/hooks/useSystem';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
const PluginRunBox = dynamic(() => import('@/components/core/chat/ChatContainer/PluginRunBox')); const PluginRunBox = dynamic(() => import('@/components/core/chat/ChatContainer/PluginRunBox'));
const DetailLogsModal = ({ const DetailLogsModal = ({
...@@ -32,7 +31,6 @@ const DetailLogsModal = ({ ...@@ -32,7 +31,6 @@ const DetailLogsModal = ({
const { t } = useTranslation(); const { t } = useTranslation();
const { isPc } = useSystem(); const { isPc } = useSystem();
const theme = useTheme(); const theme = useTheme();
const { const {
ChatBoxRef, ChatBoxRef,
chatRecords, chatRecords,
...@@ -101,7 +99,7 @@ const DetailLogsModal = ({ ...@@ -101,7 +99,7 @@ const DetailLogsModal = ({
...(chatRecords.length > 0 ...(chatRecords.length > 0
? [ ? [
{ label: t('common:common.Output'), value: PluginRunBoxTabEnum.output }, { 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 = ({ ...@@ -133,7 +131,9 @@ const DetailLogsModal = ({
<> <>
<MyTag colorSchema="blue"> <MyTag colorSchema="blue">
<MyIcon name={'history'} w={'14px'} /> <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> </MyTag>
{!!chatModels && ( {!!chatModels && (
<MyTag ml={2} colorSchema={'green'}> <MyTag ml={2} colorSchema={'green'}>
......
...@@ -122,7 +122,7 @@ const Logs = () => { ...@@ -122,7 +122,7 @@ const Logs = () => {
key={item._id} key={item._id}
_hover={{ bg: 'myWhite.600' }} _hover={{ bg: 'myWhite.600' }}
cursor={'pointer'} cursor={'pointer'}
title={'点击查看对话详情'} title={t('common:core.view_chat_detail')}
onClick={() => setDetailLogsId(item.id)} onClick={() => setDetailLogsId(item.id)}
> >
<Td> <Td>
......
...@@ -13,6 +13,7 @@ import dynamic from 'next/dynamic'; ...@@ -13,6 +13,7 @@ import dynamic from 'next/dynamic';
import { cloneDeep } from 'lodash'; import { cloneDeep } from 'lodash';
import Flow from '../WorkflowComponents/Flow'; import Flow from '../WorkflowComponents/Flow';
import { t } from 'i18next';
const Logs = dynamic(() => import('../Logs/index')); const Logs = dynamic(() => import('../Logs/index'));
const PublishChannel = dynamic(() => import('../Publish')); const PublishChannel = dynamic(() => import('../Publish'));
...@@ -22,8 +23,7 @@ const WorkflowEdit = () => { ...@@ -22,8 +23,7 @@ const WorkflowEdit = () => {
const { openConfirm, ConfirmModal } = useConfirm({ const { openConfirm, ConfirmModal } = useConfirm({
showCancel: false, showCancel: false,
content: content: t('common:info.old_version_attention')
'检测到您的高级编排为旧版,系统将为您自动格式化成新版工作流。\n\n由于版本差异较大,会导致一些工作流无法正常排布,请重新手动连接工作流。如仍异常,可尝试删除对应节点后重新添加。\n\n你可以直接点击调试进行工作流测试,调试完毕后点击发布。直到你点击发布,新工作流才会真正保存生效。\n\n在你发布新工作流前,自动保存不会生效。'
}); });
const initData = useContextSelector(WorkflowContext, (v) => v.initData); const initData = useContextSelector(WorkflowContext, (v) => v.initData);
......
...@@ -123,8 +123,7 @@ const FeiShuEditModal = ({ ...@@ -123,8 +123,7 @@ const FeiShuEditModal = ({
</Flex> </Flex>
<Flex alignItems={'center'} mt={4}> <Flex alignItems={'center'} mt={4}>
<Flex flex={'0 0 90px'} alignItems={'center'}> <Flex flex={'0 0 90px'} alignItems={'center'}>
默认回复 {t('common:default_reply')}
{/* TODO: i18n */}
</Flex> </Flex>
<Input <Input
placeholder={publishT('default_response') || 'link_name'} placeholder={publishT('default_response') || 'link_name'}
...@@ -136,8 +135,7 @@ const FeiShuEditModal = ({ ...@@ -136,8 +135,7 @@ const FeiShuEditModal = ({
</Flex> </Flex>
<Flex alignItems={'center'} mt={4}> <Flex alignItems={'center'} mt={4}>
<Flex flex={'0 0 90px'} alignItems={'center'}> <Flex flex={'0 0 90px'} alignItems={'center'}>
立即回复 {t('common:reply_now')}
{/* TODO: i18n */}
</Flex> </Flex>
<Input <Input
placeholder={publishT('default_response') || 'link_name'} placeholder={publishT('default_response') || 'link_name'}
......
...@@ -13,6 +13,7 @@ import dynamic from 'next/dynamic'; ...@@ -13,6 +13,7 @@ import dynamic from 'next/dynamic';
import { cloneDeep } from 'lodash'; import { cloneDeep } from 'lodash';
import Flow from '../WorkflowComponents/Flow'; import Flow from '../WorkflowComponents/Flow';
import { t } from 'i18next';
const Logs = dynamic(() => import('../Logs/index')); const Logs = dynamic(() => import('../Logs/index'));
const PublishChannel = dynamic(() => import('../Publish')); const PublishChannel = dynamic(() => import('../Publish'));
...@@ -22,8 +23,7 @@ const WorkflowEdit = () => { ...@@ -22,8 +23,7 @@ const WorkflowEdit = () => {
const { openConfirm, ConfirmModal } = useConfirm({ const { openConfirm, ConfirmModal } = useConfirm({
showCancel: false, showCancel: false,
content: content: t('common:info.old_version_attention')
'检测到您的高级编排为旧版,系统将为您自动格式化成新版工作流。\n\n由于版本差异较大,会导致一些工作流无法正常排布,请重新手动连接工作流。如仍异常,可尝试删除对应节点后重新添加。\n\n你可以直接点击调试进行工作流测试,调试完毕后点击发布。直到你点击发布,新工作流才会真正保存生效。\n\n在你发布新工作流前,自动保存不会生效。'
}); });
const initData = useContextSelector(WorkflowContext, (v) => v.initData); const initData = useContextSelector(WorkflowContext, (v) => v.initData);
......
...@@ -77,7 +77,7 @@ const ChatTest = ({ ...@@ -77,7 +77,7 @@ const ChatTest = ({
...(chatRecords.length > 0 ...(chatRecords.length > 0
? [ ? [
{ label: t('common:common.Output'), value: PluginRunBoxTabEnum.output }, { label: t('common:common.Output'), value: PluginRunBoxTabEnum.output },
{ label: '完整结果', value: PluginRunBoxTabEnum.detail } { label: t('common:common.all_result'), value: PluginRunBoxTabEnum.detail }
] ]
: []) : [])
]} ]}
......
...@@ -5,7 +5,7 @@ import { useToast } from '@fastgpt/web/hooks/useToast'; ...@@ -5,7 +5,7 @@ import { useToast } from '@fastgpt/web/hooks/useToast';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
import { WorkflowContext } from '../context'; import { WorkflowContext } from '../context';
import { useI18n } from '@/web/context/I18n'; import { useI18n } from '@/web/context/I18n';
import { useTranslation } from 'next-i18next';
type Props = { type Props = {
onClose: () => void; onClose: () => void;
}; };
...@@ -15,7 +15,7 @@ const ImportSettings = ({ onClose }: Props) => { ...@@ -15,7 +15,7 @@ const ImportSettings = ({ onClose }: Props) => {
const { toast } = useToast(); const { toast } = useToast();
const initData = useContextSelector(WorkflowContext, (v) => v.initData); const initData = useContextSelector(WorkflowContext, (v) => v.initData);
const [value, setValue] = useState(''); const [value, setValue] = useState('');
const { t } = useTranslation();
return ( return (
<MyModal <MyModal
isOpen isOpen
...@@ -50,7 +50,7 @@ const ImportSettings = ({ onClose }: Props) => { ...@@ -50,7 +50,7 @@ const ImportSettings = ({ onClose }: Props) => {
} }
}} }}
> >
确认 {t('common:common.Confirm')}
</Button> </Button>
</ModalFooter> </ModalFooter>
</MyModal> </MyModal>
......
...@@ -263,7 +263,7 @@ const NodeTemplatesModal = ({ isOpen, onClose }: ModuleTemplateListProps) => { ...@@ -263,7 +263,7 @@ const NodeTemplatesModal = ({ isOpen, onClose }: ModuleTemplateListProps) => {
onClick={() => router.push('/app/list')} onClick={() => router.push('/app/list')}
gap={1} gap={1}
> >
<Box>去创建</Box> <Box>{t('common:create')}</Box>
<MyIcon name={'common/rightArrowLight'} w={'0.8rem'} /> <MyIcon name={'common/rightArrowLight'} w={'0.8rem'} />
</Flex> </Flex>
)} )}
...@@ -279,7 +279,7 @@ const NodeTemplatesModal = ({ isOpen, onClose }: ModuleTemplateListProps) => { ...@@ -279,7 +279,7 @@ const NodeTemplatesModal = ({ isOpen, onClose }: ModuleTemplateListProps) => {
onClick={() => window.open(feConfigs.systemPluginCourseUrl)} onClick={() => window.open(feConfigs.systemPluginCourseUrl)}
gap={1} gap={1}
> >
<Box>贡献插件</Box> <Box>{t('common:plugin.contribute')}</Box>
<MyIcon name={'common/rightArrowLight'} w={'0.8rem'} /> <MyIcon name={'common/rightArrowLight'} w={'0.8rem'} />
</Flex> </Flex>
)} )}
......
...@@ -47,7 +47,7 @@ const SelectAppModal = ({ ...@@ -47,7 +47,7 @@ const SelectAppModal = ({
return ( return (
<MyModal <MyModal
isOpen isOpen
title={`选择应用`} title={t('common:core.module.Select app')}
iconSrc="/imgs/workflow/ai.svg" iconSrc="/imgs/workflow/ai.svg"
onClose={onClose} onClose={onClose}
position={'relative'} position={'relative'}
......
...@@ -261,7 +261,7 @@ export const useDebug = () => { ...@@ -261,7 +261,7 @@ export const useDebug = () => {
})} })}
</Box> </Box>
<Flex py={2} justifyContent={'flex-end'} px={6}> <Flex py={2} justifyContent={'flex-end'} px={6}>
<Button onClick={handleSubmit(onClickRun)}>运行</Button> <Button onClick={handleSubmit(onClickRun)}>{t('common:common.Run')}</Button>
</Flex> </Flex>
</MyRightDrawer> </MyRightDrawer>
); );
......
...@@ -25,6 +25,7 @@ import { connectionLineStyle, defaultEdgeOptions } from '../constants'; ...@@ -25,6 +25,7 @@ import { connectionLineStyle, defaultEdgeOptions } from '../constants';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
import { WorkflowContext } from '../context'; import { WorkflowContext } from '../context';
import { useWorkflow } from './hooks/useWorkflow'; import { useWorkflow } from './hooks/useWorkflow';
import { t } from 'i18next';
const NodeSimple = dynamic(() => import('./nodes/NodeSimple')); const NodeSimple = dynamic(() => import('./nodes/NodeSimple'));
const nodeTypes: Record<FlowNodeTypeEnum, any> = { const nodeTypes: Record<FlowNodeTypeEnum, any> = {
...@@ -171,7 +172,7 @@ const FlowController = React.memo(function FlowController() { ...@@ -171,7 +172,7 @@ const FlowController = React.memo(function FlowController() {
showInteractive={false} showInteractive={false}
showFitView={false} showFitView={false}
> >
<MyTooltip label={'页面居中'}> <MyTooltip label={t('common:common.page_center')}>
<ControlButton className="custom-workflow-fix_view" onClick={() => fitView()}> <ControlButton className="custom-workflow-fix_view" onClick={() => fitView()}>
<MyIcon name={'core/modules/fixview'} w={'14px'} /> <MyIcon name={'core/modules/fixview'} w={'14px'} />
</ControlButton> </ControlButton>
......
...@@ -64,7 +64,7 @@ const NodeCQNode = ({ data, selected }: NodeProps<FlowNodeItemType>) => { ...@@ -64,7 +64,7 @@ const NodeCQNode = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
/> />
</MyTooltip> </MyTooltip>
<Box flex={1} color={'myGray.600'} fontWeight={'medium'}> <Box flex={1} color={'myGray.600'} fontWeight={'medium'}>
分类{i + 1} {t('common:classification') + (i + 1)}
</Box> </Box>
</Flex> </Flex>
<Box position={'relative'}> <Box position={'relative'}>
......
...@@ -203,7 +203,10 @@ function Reference({ ...@@ -203,7 +203,10 @@ function Reference({
/> />
</Flex> </Flex>
<ReferSelector <ReferSelector
placeholder={t((inputChildren.referencePlaceholder as any) || '选择知识库引用')} placeholder={t(
(inputChildren.referencePlaceholder as any) ||
t('common:core.module.Dataset quote.select')
)}
list={referenceList} list={referenceList}
value={formatValue} value={formatValue}
onSelect={onSelect} onSelect={onSelect}
......
...@@ -79,10 +79,10 @@ const NodeExtract = ({ data }: NodeProps<FlowNodeItemType>) => { ...@@ -79,10 +79,10 @@ const NodeExtract = ({ data }: NodeProps<FlowNodeItemType>) => {
<Thead> <Thead>
<Tr> <Tr>
<Th bg={'myGray.50'} borderRadius={'none !important'}> <Th bg={'myGray.50'} borderRadius={'none !important'}>
字段名 {t('common:item_name')}
</Th> </Th>
<Th bg={'myGray.50'}>字段描述</Th> <Th bg={'myGray.50'}>{t('common:item_description')}</Th>
<Th bg={'myGray.50'}>必须</Th> <Th bg={'myGray.50'}>{t('common:required')}</Th>
<Th bg={'myGray.50'} borderRadius={'none !important'}></Th> <Th bg={'myGray.50'} borderRadius={'none !important'}></Th>
</Tr> </Tr>
</Thead> </Thead>
...@@ -197,7 +197,7 @@ const NodeExtract = ({ data }: NodeProps<FlowNodeItemType>) => { ...@@ -197,7 +197,7 @@ const NodeExtract = ({ data }: NodeProps<FlowNodeItemType>) => {
const newOutput: FlowNodeOutputItemType = { const newOutput: FlowNodeOutputItemType = {
id: getNanoid(), id: getNanoid(),
key: data.key, key: data.key,
label: `提取结果-${data.desc}`, label: `${t('common:extraction_results')}-${data.desc}`,
valueType: WorkflowIOValueTypeEnum.string, valueType: WorkflowIOValueTypeEnum.string,
type: FlowNodeOutputTypeEnum.static type: FlowNodeOutputTypeEnum.static
}; };
...@@ -215,7 +215,7 @@ const NodeExtract = ({ data }: NodeProps<FlowNodeItemType>) => { ...@@ -215,7 +215,7 @@ const NodeExtract = ({ data }: NodeProps<FlowNodeItemType>) => {
key: data.key, key: data.key,
value: { value: {
...output, ...output,
label: `提取结果-${data.desc}` label: `${t('common:extraction_results')}-${data.desc}`
} }
}); });
} else { } else {
......
...@@ -392,7 +392,7 @@ const ConditionSelect = ({ ...@@ -392,7 +392,7 @@ const ConditionSelect = ({
list={filterQuiredConditionList} list={filterQuiredConditionList}
value={condition} value={condition}
onchange={onSelect} onchange={onSelect}
placeholder="选择条件" placeholder={t('common:chose_condition')}
/> />
); );
}; };
......
...@@ -92,7 +92,7 @@ const NodeLaf = (props: NodeProps<FlowNodeItemType>) => { ...@@ -92,7 +92,7 @@ const NodeLaf = (props: NodeProps<FlowNodeItemType>) => {
onError(err) { onError(err) {
toast({ toast({
status: 'error', status: 'error',
title: getErrText(err, '获取Laf函数列表失败') title: getErrText(err, t('common:get_laf_failed'))
}); });
} }
} }
...@@ -298,7 +298,7 @@ const ConfigLaf = () => { ...@@ -298,7 +298,7 @@ const ConfigLaf = () => {
)} )}
</Center> </Center>
) : ( ) : (
<Box>系统未配置Laf环境</Box> <Box>{t('common:no_laf_env')}</Box>
); );
}; };
......
...@@ -443,7 +443,7 @@ const NodeIntro = React.memo(function NodeIntro({ ...@@ -443,7 +443,7 @@ const NodeIntro = React.memo(function NodeIntro({
// edit intro // edit intro
const { onOpenModal: onOpenIntroModal, EditModal: EditIntroModal } = useEditTextarea({ const { onOpenModal: onOpenIntroModal, EditModal: EditIntroModal } = useEditTextarea({
title: t('common:core.module.Edit intro'), title: t('common:core.module.Edit intro'),
tip: '调整该模块会对工具调用时机有影响。\n你可以通过精确的描述该模块功能,引导模型进行工具调用。', tip: t('common:info.node_info'),
canEmpty: false canEmpty: false
}); });
......
...@@ -85,7 +85,7 @@ const EditFieldModal = ({ ...@@ -85,7 +85,7 @@ const EditFieldModal = ({
); );
return ( return (
<MyModal isOpen iconSrc="modal/edit" title={'工具字段参数配置'} onClose={onClose}> <MyModal isOpen iconSrc="modal/edit" title={t('common:tool_field')} onClose={onClose}>
<ModalBody> <ModalBody>
<Flex alignItems={'center'} mb={5}> <Flex alignItems={'center'} mb={5}>
<Box flex={'0 0 80px'}>{t('common:common.Require Input')}</Box> <Box flex={'0 0 80px'}>{t('common:common.Require Input')}</Box>
...@@ -111,7 +111,7 @@ const EditFieldModal = ({ ...@@ -111,7 +111,7 @@ const EditFieldModal = ({
required: true, required: true,
pattern: { pattern: {
value: /^[a-zA-Z]+[0-9]*$/, value: /^[a-zA-Z]+[0-9]*$/,
message: '字段key必须是纯英文字母或数字,并且不能以数字开头。' message: t('common:info.felid_message')
} }
})} })}
/> />
......
...@@ -64,9 +64,9 @@ const RenderToolInput = ({ ...@@ -64,9 +64,9 @@ const RenderToolInput = ({
<Table bg={'white'}> <Table bg={'white'}>
<Thead> <Thead>
<Tr> <Tr>
<Th>字段名</Th> <Th>{t('common:item_name')}</Th>
<Th>字段描述</Th> <Th>{t('common:item_description')}</Th>
<Th>必须</Th> <Th>{t('common:required')}</Th>
{dynamicInput && <Th></Th>} {dynamicInput && <Th></Th>}
</Tr> </Tr>
</Thead> </Thead>
......
...@@ -387,7 +387,7 @@ const WorkflowContextProvider = ({ ...@@ -387,7 +387,7 @@ const WorkflowContextProvider = ({
if (input) { if (input) {
toast({ toast({
status: 'warning', status: 'warning',
title: 'key 重复' title: t('common:key_repetition')
}); });
} else { } else {
updateObj.inputs.push(props.value); updateObj.inputs.push(props.value);
...@@ -408,7 +408,7 @@ const WorkflowContextProvider = ({ ...@@ -408,7 +408,7 @@ const WorkflowContextProvider = ({
if (output) { if (output) {
toast({ toast({
status: 'warning', status: 'warning',
title: 'key 重复' title: t('common:key_repetition')
}); });
updateObj.outputs = node.data.outputs; updateObj.outputs = node.data.outputs;
} else { } else {
......
import { BoxProps, FlexProps } from '@chakra-ui/react'; import { BoxProps, FlexProps } from '@chakra-ui/react';
import { i18nT } from '@fastgpt/web/i18n/utils';
export const cardStyles: BoxProps = { export const cardStyles: BoxProps = {
borderRadius: 'lg', borderRadius: 'lg',
// overflow: 'hidden', // overflow: 'hidden',
...@@ -20,11 +20,11 @@ export const workflowBoxStyles: FlexProps = { ...@@ -20,11 +20,11 @@ export const workflowBoxStyles: FlexProps = {
export const publishStatusStyle = { export const publishStatusStyle = {
unPublish: { unPublish: {
colorSchema: 'adora' as any, colorSchema: 'adora' as any,
text: '未发布' text: i18nT('common:core.app.have_publish')
}, },
published: { published: {
colorSchema: 'green' as any, colorSchema: 'green' as any,
text: '已发布' text: i18nT('common:core.app.not_published')
} }
}; };
......
...@@ -215,7 +215,7 @@ const ListItem = () => { ...@@ -215,7 +215,7 @@ const ListItem = () => {
fontSize={'xs'} fontSize={'xs'}
color={'myGray.500'} color={'myGray.500'}
> >
<Box className={'textEllipsis2'}>{app.intro || '还没写介绍~'}</Box> <Box className={'textEllipsis2'}>{app.intro || t('common:common.no_intro')}</Box>
</Box> </Box>
<Flex <Flex
h={'24px'} h={'24px'}
...@@ -295,7 +295,7 @@ const ListItem = () => { ...@@ -295,7 +295,7 @@ const ListItem = () => {
children: [ children: [
{ {
icon: 'edit', icon: 'edit',
label: '编辑信息', label: t('common:dataset.Edit Info'),
onClick: () => { onClick: () => {
if (app.type === AppTypeEnum.httpPlugin) { if (app.type === AppTypeEnum.httpPlugin) {
setEditHttpPlugin({ setEditHttpPlugin({
...@@ -383,14 +383,14 @@ const ListItem = () => { ...@@ -383,14 +383,14 @@ const ListItem = () => {
})} })}
</Grid> </Grid>
{myApps.length === 0 && <EmptyTip text={'还没有应用,快去创建一个吧!'} pt={'30vh'} />} {myApps.length === 0 && <EmptyTip text={t('common:core.app.no_app')} pt={'30vh'} />}
<DelConfirmModal /> <DelConfirmModal />
<ConfirmCopyModal /> <ConfirmCopyModal />
{!!editedApp && ( {!!editedApp && (
<EditResourceModal <EditResourceModal
{...editedApp} {...editedApp}
title="应用信息编辑" title={t('common:core.app.edit_content')}
onClose={() => { onClose={() => {
setEditedApp(undefined); setEditedApp(undefined);
}} }}
......
...@@ -31,7 +31,7 @@ const CustomPluginRunBox = (props: PluginRunBoxProps) => { ...@@ -31,7 +31,7 @@ const CustomPluginRunBox = (props: PluginRunBoxProps) => {
<LightRowTabs<PluginRunBoxTabEnum> <LightRowTabs<PluginRunBoxTabEnum>
list={[ list={[
{ label: t('common:common.Output'), value: PluginRunBoxTabEnum.output }, { label: t('common:common.Output'), value: PluginRunBoxTabEnum.output },
{ label: '完整结果', value: PluginRunBoxTabEnum.detail } { label: t('common:common.all_result'), value: PluginRunBoxTabEnum.detail }
]} ]}
value={tab} value={tab}
onChange={setTab} onChange={setTab}
...@@ -52,7 +52,7 @@ const CustomPluginRunBox = (props: PluginRunBoxProps) => { ...@@ -52,7 +52,7 @@ const CustomPluginRunBox = (props: PluginRunBoxProps) => {
list={[ list={[
{ label: t('common:common.Input'), value: PluginRunBoxTabEnum.input }, { label: t('common:common.Input'), value: PluginRunBoxTabEnum.input },
{ label: t('common:common.Output'), value: PluginRunBoxTabEnum.output }, { label: t('common:common.Output'), value: PluginRunBoxTabEnum.output },
{ label: '完整结果', value: PluginRunBoxTabEnum.detail } { label: t('common:common.all_result'), value: PluginRunBoxTabEnum.detail }
]} ]}
value={tab} value={tab}
onChange={setTab} onChange={setTab}
......
...@@ -287,7 +287,7 @@ const FileSelector = ({ ...@@ -287,7 +287,7 @@ const FileSelector = ({
{isMaxSelected ? ( {isMaxSelected ? (
<> <>
<Box color={'myGray.500'} fontSize={'xs'}> <Box color={'myGray.500'} fontSize={'xs'}>
已达到最大文件数量 {t('file:reached_max_file_count')}
</Box> </Box>
</> </>
) : ( ) : (
......
...@@ -243,7 +243,7 @@ const Test = ({ datasetId }: { datasetId: string }) => { ...@@ -243,7 +243,7 @@ const Test = ({ datasetId }: { datasetId: string }) => {
</Box> </Box>
</Flex> </Flex>
<Box mt={3} fontSize={'sm'}> <Box mt={3} fontSize={'sm'}>
读取 CSV 文件第一列进行批量测试,单次最多支持 100 组数据。 {t('common:info.csv_message')}
<Box <Box
as={'span'} as={'span'}
color={'primary.600'} color={'primary.600'}
...@@ -256,7 +256,7 @@ const Test = ({ datasetId }: { datasetId: string }) => { ...@@ -256,7 +256,7 @@ const Test = ({ datasetId }: { datasetId: string }) => {
}); });
}} }}
> >
点击下载批量测试模板 {t('common:info.csv_download')}
</Box> </Box>
</Box> </Box>
</Box> </Box>
......
...@@ -154,7 +154,9 @@ function List() { ...@@ -154,7 +154,9 @@ function List() {
label={ label={
<Flex flexDirection={'column'} alignItems={'center'}> <Flex flexDirection={'column'} alignItems={'center'}>
<Box fontSize={'xs'} color={'myGray.500'}> <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> </Box>
</Flex> </Flex>
} }
......
...@@ -7,7 +7,7 @@ import { useSendCode } from '@/web/support/user/hooks/useSendCode'; ...@@ -7,7 +7,7 @@ import { useSendCode } from '@/web/support/user/hooks/useSendCode';
import type { ResLogin } from '@/global/support/api/userRes.d'; import type { ResLogin } from '@/global/support/api/userRes.d';
import { useToast } from '@fastgpt/web/hooks/useToast'; import { useToast } from '@fastgpt/web/hooks/useToast';
import { useSystemStore } from '@/web/common/system/useSystemStore'; import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useTranslation } from 'next-i18next';
interface Props { interface Props {
setPageType: Dispatch<`${LoginPageTypeEnum}`>; setPageType: Dispatch<`${LoginPageTypeEnum}`>;
loginSuccess: (e: ResLogin) => void; loginSuccess: (e: ResLogin) => void;
...@@ -22,6 +22,7 @@ interface RegisterType { ...@@ -22,6 +22,7 @@ interface RegisterType {
const RegisterForm = ({ setPageType, loginSuccess }: Props) => { const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
const { toast } = useToast(); const { toast } = useToast();
const { t } = useTranslation();
const { feConfigs } = useSystemStore(); const { feConfigs } = useSystemStore();
const { const {
register, register,
...@@ -58,12 +59,12 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => { ...@@ -58,12 +59,12 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
}) })
); );
toast({ toast({
title: `密码已找回`, title: t('user:password.retrieved'),
status: 'success' status: 'success'
}); });
} catch (error: any) { } catch (error: any) {
toast({ toast({
title: error.message || '修改密码异常', title: error.message || t('user:password.change_error'),
status: 'error' status: 'error'
}); });
} }
...@@ -75,7 +76,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => { ...@@ -75,7 +76,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
return ( return (
<> <>
<Box fontWeight={'bold'} fontSize={'2xl'} textAlign={'center'}> <Box fontWeight={'bold'} fontSize={'2xl'} textAlign={'center'}>
找回 {feConfigs?.systemTitle} 账号 {t('user:password.retrieved_account', { account: feConfigs?.systemTitle })}
</Box> </Box>
<Box <Box
mt={'42px'} mt={'42px'}
...@@ -88,13 +89,13 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => { ...@@ -88,13 +89,13 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
<FormControl isInvalid={!!errors.username}> <FormControl isInvalid={!!errors.username}>
<Input <Input
bg={'myGray.50'} bg={'myGray.50'}
placeholder="邮箱/手机号" placeholder={t('user:password.email_phone')}
{...register('username', { {...register('username', {
required: '邮箱/手机号不能为空', required: t('user:password.email_phone_void'),
pattern: { pattern: {
value: value:
/(^1[3456789]\d{9}$)|(^[A-Za-z0-9]+([_\.][A-Za-z0-9]+)*@([A-Za-z0-9\-]+\.)+[A-Za-z]{2,6}$)/, /(^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> ></Input>
...@@ -110,9 +111,9 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => { ...@@ -110,9 +111,9 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
bg={'myGray.50'} bg={'myGray.50'}
flex={1} flex={1}
maxLength={8} maxLength={8}
placeholder="验证码" placeholder={t('user:password.verification_code')}
{...register('code', { {...register('code', {
required: '验证码不能为空' required: t('user:password.code_required')
})} })}
></Input> ></Input>
<Box <Box
...@@ -137,16 +138,16 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => { ...@@ -137,16 +138,16 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
<Input <Input
bg={'myGray.50'} bg={'myGray.50'}
type={'password'} type={'password'}
placeholder="新密码(4~20位)" placeholder={t('user:password.new_password')}
{...register('password', { {...register('password', {
required: '密码不能为空', required: t('user:password.password_required'),
minLength: { minLength: {
value: 4, value: 4,
message: '密码最少 4 位最多 20 位' message: t('user:password.password_condition')
}, },
maxLength: { maxLength: {
value: 20, value: 20,
message: '密码最少 4 位最多 20 位' message: t('user:password.password_condition')
} }
})} })}
></Input> ></Input>
...@@ -155,9 +156,10 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => { ...@@ -155,9 +156,10 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
<Input <Input
bg={'myGray.50'} bg={'myGray.50'}
type={'password'} type={'password'}
placeholder="确认密码" placeholder={t('user:password.confirm')}
{...register('password2', { {...register('password2', {
validate: (val) => (getValues('password') === val ? true : '两次密码不一致') validate: (val) =>
getValues('password') === val ? true : t('user:password.not_match')
})} })}
></Input> ></Input>
</FormControl> </FormControl>
...@@ -171,7 +173,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => { ...@@ -171,7 +173,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
isLoading={requesting} isLoading={requesting}
onClick={handleSubmit(onclickFindPassword)} onClick={handleSubmit(onclickFindPassword)}
> >
找回密码 {t('user:password.retrieve')}
</Button> </Button>
<Box <Box
float={'right'} float={'right'}
...@@ -183,7 +185,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => { ...@@ -183,7 +185,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
_hover={{ textDecoration: 'underline' }} _hover={{ textDecoration: 'underline' }}
onClick={() => setPageType(LoginPageTypeEnum.passwordLogin)} onClick={() => setPageType(LoginPageTypeEnum.passwordLogin)}
> >
去登录 {t('user:password.to_login')}
</Box> </Box>
</Box> </Box>
</> </>
......
...@@ -43,12 +43,12 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => { ...@@ -43,12 +43,12 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => {
}) })
); );
toast({ toast({
title: '登录成功', title: t('user:login.success'),
status: 'success' status: 'success'
}); });
} catch (error: any) { } catch (error: any) {
toast({ toast({
title: error.message || '登录异常', title: error.message || t('user:login.error'),
status: 'error' status: 'error'
}); });
} }
...@@ -101,7 +101,7 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => { ...@@ -101,7 +101,7 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => {
required: true, required: true,
maxLength: { maxLength: {
value: 60, value: 60,
message: '密码最多 60 位' message: t('user:login.password_condition')
} }
})} })}
></Input> ></Input>
......
...@@ -24,7 +24,7 @@ const WechatForm = ({ setPageType, loginSuccess }: Props) => { ...@@ -24,7 +24,7 @@ const WechatForm = ({ setPageType, loginSuccess }: Props) => {
onError(err) { onError(err) {
toast({ toast({
status: 'warning', status: 'warning',
title: getErrText(err, '获取二维码失败') title: getErrText(err, t('common:get_QR_failed'))
}); });
} }
}); });
......
...@@ -10,7 +10,6 @@ import { postCreateApp } from '@/web/core/app/api'; ...@@ -10,7 +10,6 @@ import { postCreateApp } from '@/web/core/app/api';
import { defaultAppTemplates } from '@/web/core/app/templates'; import { defaultAppTemplates } from '@/web/core/app/templates';
import { useSystemStore } from '@/web/common/system/useSystemStore'; import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
interface Props { interface Props {
loginSuccess: (e: ResLogin) => void; loginSuccess: (e: ResLogin) => void;
setPageType: Dispatch<`${LoginPageTypeEnum}`>; setPageType: Dispatch<`${LoginPageTypeEnum}`>;
...@@ -26,6 +25,7 @@ interface RegisterType { ...@@ -26,6 +25,7 @@ interface RegisterType {
const RegisterForm = ({ setPageType, loginSuccess }: Props) => { const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
const { toast } = useToast(); const { toast } = useToast();
const { t } = useTranslation(); const { t } = useTranslation();
const { feConfigs } = useSystemStore(); const { feConfigs } = useSystemStore();
const { const {
register, register,
...@@ -63,7 +63,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => { ...@@ -63,7 +63,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
}) })
); );
toast({ toast({
title: `注册成功`, title: t('user:register.success'),
status: 'success' status: 'success'
}); });
// auto register template app // auto register template app
...@@ -80,7 +80,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => { ...@@ -80,7 +80,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
}, 100); }, 100);
} catch (error: any) { } catch (error: any) {
toast({ toast({
title: error.message || '注册异常', title: error.message || t('user:register.error'),
status: 'error' status: 'error'
}); });
} }
...@@ -92,7 +92,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => { ...@@ -92,7 +92,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
return ( return (
<> <>
<Box fontWeight={'bold'} fontSize={'2xl'} textAlign={'center'}> <Box fontWeight={'bold'} fontSize={'2xl'} textAlign={'center'}>
注册 {feConfigs?.systemTitle} 账号 {t('user:register.register_account', { account: feConfigs?.systemTitle })}
</Box> </Box>
<Box <Box
mt={'42px'} mt={'42px'}
...@@ -105,13 +105,13 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => { ...@@ -105,13 +105,13 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
<FormControl isInvalid={!!errors.username}> <FormControl isInvalid={!!errors.username}>
<Input <Input
bg={'myGray.50'} bg={'myGray.50'}
placeholder="邮箱/手机号" placeholder={t('user:password.email_phone')}
{...register('username', { {...register('username', {
required: '邮箱/手机号不能为空', required: t('user:password.email_phone_void'),
pattern: { pattern: {
value: value:
/(^1[3456789]\d{9}$)|(^[A-Za-z0-9]+([_\.][A-Za-z0-9]+)*@([A-Za-z0-9\-]+\.)+[A-Za-z]{2,6}$)/, /(^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> ></Input>
...@@ -127,9 +127,9 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => { ...@@ -127,9 +127,9 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
bg={'myGray.50'} bg={'myGray.50'}
flex={1} flex={1}
maxLength={8} maxLength={8}
placeholder="验证码" placeholder={t('user:password.verification_code')}
{...register('code', { {...register('code', {
required: '验证码不能为空' required: t('user:password.code_required')
})} })}
></Input> ></Input>
<Box <Box
...@@ -154,16 +154,16 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => { ...@@ -154,16 +154,16 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
<Input <Input
bg={'myGray.50'} bg={'myGray.50'}
type={'password'} type={'password'}
placeholder="密码(4~20位)" placeholder={t('user:password.new_password')}
{...register('password', { {...register('password', {
required: '密码不能为空', required: t('user:password.password_required'),
minLength: { minLength: {
value: 4, value: 4,
message: '密码最少 4 位最多 20 位' message: t('user:password.password_condition')
}, },
maxLength: { maxLength: {
value: 20, value: 20,
message: '密码最少 4 位最多 20 位' message: t('user:password.password_condition')
} }
})} })}
></Input> ></Input>
...@@ -172,9 +172,10 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => { ...@@ -172,9 +172,10 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
<Input <Input
bg={'myGray.50'} bg={'myGray.50'}
type={'password'} type={'password'}
placeholder="确认密码" placeholder={t('user:password.confirm')}
{...register('password2', { {...register('password2', {
validate: (val) => (getValues('password') === val ? true : '两次密码不一致') validate: (val) =>
getValues('password') === val ? true : t('user:password.not_match')
})} })}
></Input> ></Input>
</FormControl> </FormControl>
...@@ -187,7 +188,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => { ...@@ -187,7 +188,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
isLoading={requesting} isLoading={requesting}
onClick={handleSubmit(onclickRegister)} onClick={handleSubmit(onclickRegister)}
> >
确认注册 {t('user:register.confirm')}
</Button> </Button>
<Box <Box
float={'right'} float={'right'}
...@@ -199,7 +200,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => { ...@@ -199,7 +200,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
_hover={{ textDecoration: 'underline' }} _hover={{ textDecoration: 'underline' }}
onClick={() => setPageType(LoginPageTypeEnum.passwordLogin)} onClick={() => setPageType(LoginPageTypeEnum.passwordLogin)}
> >
已有账号,去登录 {t('user:register.to_login')}
</Box> </Box>
</Box> </Box>
</> </>
......
...@@ -9,7 +9,7 @@ import { useToast } from '@fastgpt/web/hooks/useToast'; ...@@ -9,7 +9,7 @@ import { useToast } from '@fastgpt/web/hooks/useToast';
import Loading from '@fastgpt/web/components/common/MyLoading'; import Loading from '@fastgpt/web/components/common/MyLoading';
import { serviceSideProps } from '@/web/common/utils/i18n'; import { serviceSideProps } from '@/web/common/utils/i18n';
import { getErrText } from '@fastgpt/global/common/error/utils'; import { getErrText } from '@fastgpt/global/common/error/utils';
import { useTranslation } from 'next-i18next';
const FastLogin = ({ const FastLogin = ({
code, code,
token, token,
...@@ -23,7 +23,7 @@ const FastLogin = ({ ...@@ -23,7 +23,7 @@ const FastLogin = ({
const { setUserInfo } = useUserStore(); const { setUserInfo } = useUserStore();
const router = useRouter(); const router = useRouter();
const { toast } = useToast(); const { toast } = useToast();
const { t } = useTranslation();
const loginSuccess = useCallback( const loginSuccess = useCallback(
(res: ResLogin) => { (res: ResLogin) => {
setToken(res.token); setToken(res.token);
...@@ -50,7 +50,7 @@ const FastLogin = ({ ...@@ -50,7 +50,7 @@ const FastLogin = ({
if (!res) { if (!res) {
toast({ toast({
status: 'warning', status: 'warning',
title: '登录异常' title: t('common:support.user.login.error')
}); });
return setTimeout(() => { return setTimeout(() => {
router.replace('/login'); router.replace('/login');
...@@ -60,7 +60,7 @@ const FastLogin = ({ ...@@ -60,7 +60,7 @@ const FastLogin = ({
} catch (error) { } catch (error) {
toast({ toast({
status: 'warning', status: 'warning',
title: getErrText(error, '登录异常') title: getErrText(error, t('common:support.user.login.error'))
}); });
setTimeout(() => { setTimeout(() => {
router.replace('/login'); router.replace('/login');
......
...@@ -13,6 +13,7 @@ import { clearToken, setToken } from '@/web/support/user/auth'; ...@@ -13,6 +13,7 @@ import { clearToken, setToken } from '@/web/support/user/auth';
import Script from 'next/script'; import Script from 'next/script';
import Loading from '@fastgpt/web/components/common/MyLoading'; import Loading from '@fastgpt/web/components/common/MyLoading';
import { useMount } from 'ahooks'; import { useMount } from 'ahooks';
import { t } from 'i18next';
const RegisterForm = dynamic(() => import('./components/RegisterForm')); const RegisterForm = dynamic(() => import('./components/RegisterForm'));
const ForgetPasswordForm = dynamic(() => import('./components/ForgetPasswordForm')); const ForgetPasswordForm = dynamic(() => import('./components/ForgetPasswordForm'));
...@@ -115,7 +116,7 @@ const Login = () => { ...@@ -115,7 +116,7 @@ const Login = () => {
textAlign={'center'} textAlign={'center'}
onClick={onOpen} onClick={onOpen}
> >
无法登录,点击联系 {t('common:support.user.login.can_not_login')}
</Box> </Box>
)} )}
</Flex> </Flex>
......
...@@ -57,7 +57,7 @@ const provider = () => { ...@@ -57,7 +57,7 @@ const provider = () => {
if (!res) { if (!res) {
toast({ toast({
status: 'warning', status: 'warning',
title: '登录异常' title: t('common:support.user.login.error')
}); });
return setTimeout(() => { return setTimeout(() => {
router.replace('/login'); router.replace('/login');
...@@ -67,7 +67,7 @@ const provider = () => { ...@@ -67,7 +67,7 @@ const provider = () => {
} catch (error) { } catch (error) {
toast({ toast({
status: 'warning', status: 'warning',
title: getErrText(error, '登录异常') title: getErrText(error, t('common:support.user.login.error'))
}); });
setTimeout(() => { setTimeout(() => {
router.replace('/login'); router.replace('/login');
...@@ -95,7 +95,7 @@ const provider = () => { ...@@ -95,7 +95,7 @@ const provider = () => {
if (state !== loginStore?.state) { if (state !== loginStore?.state) {
toast({ toast({
status: 'warning', status: 'warning',
title: '安全校验失败' title: t('common:support.user.login.security_failed')
}); });
setTimeout(() => { setTimeout(() => {
router.replace('/login'); router.replace('/login');
......
...@@ -46,7 +46,7 @@ const ExtraPlan = () => { ...@@ -46,7 +46,7 @@ const ExtraPlan = () => {
if (datasetSizePayAmount === 0) { if (datasetSizePayAmount === 0) {
return toast({ return toast({
status: 'warning', status: 'warning',
title: '购买数量不能为0' title: t('common:support.wallet.amount_0')
}); });
} }
setLoading(true); setLoading(true);
...@@ -91,7 +91,7 @@ const ExtraPlan = () => { ...@@ -91,7 +91,7 @@ const ExtraPlan = () => {
if (payAmount === 0) { if (payAmount === 0) {
return toast({ return toast({
status: 'warning', status: 'warning',
title: '购买数量不能为0' title: t('common:support.wallet.amount_0')
}); });
} }
setLoading(true); setLoading(true);
...@@ -147,7 +147,7 @@ const ExtraPlan = () => { ...@@ -147,7 +147,7 @@ const ExtraPlan = () => {
{t('common:support.wallet.subscription.Extra dataset size')} {t('common:support.wallet.subscription.Extra dataset size')}
</Box> </Box>
<Box mt={3} fontSize={['28px', '32px']} fontWeight={'bold'}> <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'}> <Box ml={1} as={'span'} fontSize={'md'} color={'myGray.600'} fontWeight={'normal'}>
/{t('common:common.month')} /{t('common:common.month')}
</Box> </Box>
...@@ -164,7 +164,7 @@ const ExtraPlan = () => { ...@@ -164,7 +164,7 @@ const ExtraPlan = () => {
<Box h={'120px'} w={'100%'}> <Box h={'120px'} w={'100%'}>
<Flex mt={4}> <Flex mt={4}>
<MyIcon mr={2} name={'support/bill/shoppingCart'} w={'16px'} color={'primary.600'} /> <MyIcon mr={2} name={'support/bill/shoppingCart'} w={'16px'} color={'primary.600'} />
购买资源包 {t('common:support.wallet.buy_resource')}
</Flex> </Flex>
<Flex mt={4} alignItems={'center'}> <Flex mt={4} alignItems={'center'}>
<Box flex={['0 0 100px', '1 0 0']}> <Box flex={['0 0 100px', '1 0 0']}>
...@@ -252,7 +252,7 @@ const ExtraPlan = () => { ...@@ -252,7 +252,7 @@ const ExtraPlan = () => {
{t('common:support.wallet.subscription.Extra ai points')} {t('common:support.wallet.subscription.Extra ai points')}
</Box> </Box>
<Box mt={3} fontSize={['28px', '32px']} fontWeight={'bold'}> <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'}> <Box ml={1} as={'span'} fontSize={'md'} color={'myGray.600'} fontWeight={'normal'}>
/{t('common:common.month')} /{t('common:common.month')}
</Box> </Box>
...@@ -269,7 +269,7 @@ const ExtraPlan = () => { ...@@ -269,7 +269,7 @@ const ExtraPlan = () => {
<Box h={'120px'} w={'100%'}> <Box h={'120px'} w={'100%'}>
<Flex mt={4}> <Flex mt={4}>
<MyIcon mr={2} name={'support/bill/shoppingCart'} w={'16px'} color={'primary.600'} /> <MyIcon mr={2} name={'support/bill/shoppingCart'} w={'16px'} color={'primary.600'} />
购买资源包 {t('common:support.wallet.buy_resource')}
</Flex> </Flex>
{/* <Flex mt={4} alignItems={'center'}> {/* <Flex mt={4} alignItems={'center'}>
<Box flex={['0 0 100px', '1 0 0']}> <Box flex={['0 0 100px', '1 0 0']}>
...@@ -325,7 +325,7 @@ const ExtraPlan = () => { ...@@ -325,7 +325,7 @@ const ExtraPlan = () => {
</NumberInputStepper> </NumberInputStepper>
</NumberInput> </NumberInput>
<Box position={'absolute'} right={'20px'} color={'myGray.500'} fontSize={'xs'}> <Box position={'absolute'} right={'20px'} color={'myGray.500'} fontSize={'xs'}>
000积分 {'000' + t('common:support.wallet.subscription.point')}
</Box> </Box>
</Flex> </Flex>
</Flex> </Flex>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment