Compare commits

..

6 Commits

Author SHA1 Message Date
Archer
59fd94384d fix: session (#1455)
* fix: session

* doc

* fix: i188n
2024-05-13 11:04:50 +08:00
Archer
ee8cb0915e i18n (#1444)
* adapt not input type

* adapt not input type

* file i18n

* publish i18n

* translate

* i18n
2024-05-11 00:21:01 +08:00
Archer
8cf643d972 adapt not input type (#1443)
* adapt not input type

* adapt not input type
2024-05-10 22:27:32 +08:00
Archer
26f4c92124 Perf: i18n ns (#1441)
* i18n

* fix: handle
2024-05-10 18:41:41 +08:00
heheer
f351d4ea68 fix: openapi integer & array type (#1439) 2024-05-10 18:40:01 +08:00
Archer
d70efe1d6f Fix export dataset (#1436)
* fix: export dataset

* remove file buffer
2024-05-10 16:15:23 +08:00
58 changed files with 1477 additions and 1436 deletions

43
.vscode/i18n-ally-custom-framework.yml vendored Normal file
View File

@@ -0,0 +1,43 @@
# .vscode/i18n-ally-custom-framework.yml
# An array of strings which contain Language Ids defined by VS Code
# You can check available language ids here: https://code.visualstudio.com/docs/languages/identifiers
languageIds:
- javascript
- typescript
- javascriptreact
- typescriptreact
# An array of RegExes to find the key usage. **The key should be captured in the first match group**.
# You should unescape RegEx strings in order to fit in the YAML file
# To help with this, you can use https://www.freeformatter.com/json-escape.html
usageMatchRegex:
# The following example shows how to detect `t("your.i18n.keys")`
# the `{key}` will be placed by a proper keypath matching regex,
# you can ignore it and use your own matching rules as well
- "[^\\w\\d]t\\(['\"`]({key})['\"`]"
- "[^\\w\\d]commonT\\(['\"`]({key})['\"`]"
# 支持 appT("your.i18n.keys")
- "[^\\w\\d]appT\\(['\"`]({key})['\"`]"
# 支持 datasetT("your.i18n.keys")
- "[^\\w\\d]datasetT\\(['\"`]({key})['\"`]"
- "[^\\w\\d]fileT\\(['\"`]({key})['\"`]"
- "[^\\w\\d]publishT\\(['\"`]({key})['\"`]"
# A RegEx to set a custom scope range. This scope will be used as a prefix when detecting keys
# and works like how the i18next framework identifies the namespace scope from the
# useTranslation() hook.
# You should unescape RegEx strings in order to fit in the YAML file
# To help with this, you can use https://www.freeformatter.com/json-escape.html
scopeRangeRegex: "useTranslation\\(\\s*\\[?\\s*['\"`](.*?)['\"`]"
# An array of strings containing refactor templates.
# The "$1" will be replaced by the keypath specified.
# Optional: uncomment the following two lines to use
# refactorTemplates:
# - i18n.get("$1")
# If set to true, only enables this custom framework (will disable all built-in frameworks)
monopoly: true

View File

@@ -257,6 +257,13 @@ PG 数据库没有连接上/初始化失败可以查看日志。FastGPT 会
2. 非 docker 部署的,需要手动安装 pg vector 插件
3. 查看 fastgpt 日志,有没有相关报错
### Illegal instruction
可能原因:
1. arm架构。需要使用 Mongo 官方镜像: mongo:5.0.18
2. cpu 不支持 AVX无法用 mongo5需要换成 mongo4.x。把 mongo 的 image 换成: mongo:4.4.29
### Operation `auth_codes.findOne()` buffering timed out after 10000ms
mongo连接失败查看mongo的运行状态对应日志。

View File

@@ -1,12 +1,13 @@
# 数据库的默认账号和密码仅首次运行时设置有效
# 如果修改了账号密码,记得改数据库和项目连接参数,别只改一处~
# 该配置文件只是给快速启动,测试使用。正式使用,记得务必修改账号密码,以及调整合适的知识库参数,共享内存等。
# 如何无法访问 dockerhub 和 git可以用阿里云阿里云没有arm包
version: '3.3'
services:
pg:
# image: pgvector/pgvector:0.7.0-pg15 # docker hub
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/pgvector:v0.7.0 # 阿里云
image: pgvector/pgvector:0.7.0-pg15 # docker hub
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/pgvector:v0.7.0 # 阿里云
container_name: pg
restart: always
ports: # 生产环境建议不要暴露
@@ -21,7 +22,9 @@ services:
volumes:
- ./pg/data:/var/lib/postgresql/data
mongo:
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/mongo:5.0.18
image: mongo:5.0.18 # dockerhub
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/mongo:5.0.18 # 阿里云
# image: mongo:4.4.29 # cpu不支持AVX时候使用
container_name: mongo
restart: always
ports:
@@ -66,8 +69,8 @@ services:
wait $$!
fastgpt:
container_name: fastgpt
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.7 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.7 # 阿里云
image: ghcr.io/labring/fastgpt:v4.8 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8 # 阿里云
ports:
- 3000:3000
networks:

View File

@@ -4,7 +4,7 @@ export enum BucketNameEnum {
}
export const bucketNameMap = {
[BucketNameEnum.dataset]: {
label: 'common.file.bucket.dataset'
label: 'file.bucket.dataset'
}
};

View File

@@ -13,36 +13,36 @@ export enum MongoImageTypeEnum {
}
export const mongoImageTypeMap = {
[MongoImageTypeEnum.systemAvatar]: {
label: 'common.file.type.appAvatar',
label: 'appAvatar',
unique: true
},
[MongoImageTypeEnum.appAvatar]: {
label: 'common.file.type.appAvatar',
label: 'appAvatar',
unique: true
},
[MongoImageTypeEnum.pluginAvatar]: {
label: 'common.file.type.pluginAvatar',
label: 'pluginAvatar',
unique: true
},
[MongoImageTypeEnum.datasetAvatar]: {
label: 'common.file.type.datasetAvatar',
label: 'datasetAvatar',
unique: true
},
[MongoImageTypeEnum.userAvatar]: {
label: 'common.file.type.userAvatar',
label: 'userAvatar',
unique: true
},
[MongoImageTypeEnum.teamAvatar]: {
label: 'common.file.type.teamAvatar',
label: 'teamAvatar',
unique: true
},
[MongoImageTypeEnum.chatImage]: {
label: 'common.file.type.chatImage',
label: 'chatImage',
unique: false
},
[MongoImageTypeEnum.collectionImage]: {
label: 'common.file.type.collectionImage',
label: 'collectionImage',
unique: false
}
};

View File

@@ -52,13 +52,35 @@ export const str2OpenApiSchema = async (yamlStr = ''): Promise<OpenApiJsonSchema
})
.flat()
.filter(Boolean) as OpenApiJsonSchema['pathData'];
return { pathData, serverPath };
} catch (err) {
throw new Error('Invalid Schema');
}
};
export const getType = (schema: { type: string; items?: { type: string } }) => {
const typeMap: { [key: string]: WorkflowIOValueTypeEnum } = {
string: WorkflowIOValueTypeEnum.arrayString,
number: WorkflowIOValueTypeEnum.arrayNumber,
integer: WorkflowIOValueTypeEnum.arrayNumber,
boolean: WorkflowIOValueTypeEnum.arrayBoolean,
object: WorkflowIOValueTypeEnum.arrayObject
};
if (schema?.type === 'integer') {
return WorkflowIOValueTypeEnum.number;
}
if (schema?.type === 'array' && schema?.items) {
const itemType = typeMap[schema.items.type];
if (itemType) {
return itemType;
}
}
return schema?.type as WorkflowIOValueTypeEnum;
};
export const httpApiSchema2Plugins = async ({
parentId,
apiSchemaStr = '',
@@ -87,7 +109,7 @@ export const httpApiSchema2Plugins = async ({
...(item.params?.map((param: any) => {
return {
key: param.name,
valueType: param.schema.type,
valueType: getType(param.schema),
label: param.name,
renderTypeList: [FlowNodeInputTypeEnum.reference],
required: param.required,
@@ -109,7 +131,7 @@ export const httpApiSchema2Plugins = async ({
const prop = properties[key];
return {
key,
valueType: prop.type,
valueType: getType(prop),
label: key,
renderTypeList: [FlowNodeInputTypeEnum.reference],
required: false,
@@ -136,7 +158,7 @@ export const httpApiSchema2Plugins = async ({
return {
id,
key: param.name,
valueType: param.schema.type,
valueType: getType(param.schema),
label: param.name,
type: FlowNodeOutputTypeEnum.source
};
@@ -147,7 +169,7 @@ export const httpApiSchema2Plugins = async ({
return {
id,
key,
valueType: properties[key].type,
valueType: getType(properties[key]),
label: key,
type: FlowNodeOutputTypeEnum.source,
edit: true
@@ -159,7 +181,7 @@ export const httpApiSchema2Plugins = async ({
...(item.params?.map((param: any) => {
return {
key: param.name,
valueType: param.schema.type,
valueType: getType(param.schema),
label: param.name,
renderTypeList: [FlowNodeInputTypeEnum.reference],
canEdit: true,
@@ -173,7 +195,7 @@ export const httpApiSchema2Plugins = async ({
...(propsKeys?.map((key) => {
return {
key,
valueType: properties[key].type,
valueType: getType(properties[key]),
label: key,
renderTypeList: [FlowNodeInputTypeEnum.reference],
canEdit: true,
@@ -197,7 +219,7 @@ export const httpApiSchema2Plugins = async ({
if (param.in === 'header') {
httpNodeHeaders.push({
key: param.name,
type: param.schema?.type || WorkflowIOValueTypeEnum.string,
type: getType(param.schema) || WorkflowIOValueTypeEnum.string,
value: `{{${param.name}}}`
});
} else if (param.in === 'body') {
@@ -209,7 +231,7 @@ export const httpApiSchema2Plugins = async ({
} else if (param.in === 'query') {
httpNodeParams.push({
key: param.name,
type: param.schema?.type || WorkflowIOValueTypeEnum.string,
type: getType(param.schema) || WorkflowIOValueTypeEnum.string,
value: `{{${param.name}}}`
});
}

View File

@@ -8,14 +8,14 @@ export const mongoSessionRun = async <T = unknown>(fn: (session: ClientSession)
const result = await fn(session);
await session.commitTransaction();
session.endSession();
await session.endSession();
return result as T;
} catch (error) {
console.log(error);
await session.abortTransaction();
session.endSession();
await session.endSession();
return Promise.reject(error);
}
};

View File

@@ -28,6 +28,7 @@
"papaparse": "^5.4.1",
"pdfjs-dist": "4.0.269",
"react": "18.2.0",
"use-context-selector": "^1.4.4",
"react-day-picker": "^8.7.1",
"react-dom": "18.2.0",
"react-i18next": "13.5.0",

3
pnpm-lock.yaml generated
View File

@@ -301,6 +301,9 @@ importers:
react-i18next:
specifier: 13.5.0
version: 13.5.0(i18next@23.10.0)(react-dom@18.2.0)(react@18.2.0)
use-context-selector:
specifier: ^1.4.4
version: 1.4.4(react-dom@18.2.0)(react@18.2.0)(scheduler@0.23.0)
devDependencies:
'@types/lodash':
specifier: ^4.14.191

View File

@@ -0,0 +1,45 @@
{
"AI Advanced Settings": "AI Advanced Settings",
"AI Settings": "AI Settings",
"Advance App TestTip": "Current app may be in advanced orchestration mode\nTo switch to【Simple Mode】please click the save button on the left",
"App Detail": "App Details",
"Apps Share": "Apps Share",
"Basic Settings": "Basic Settings",
"Chat Debug": "Chat Debug",
"Chat Logs Tips": "Logs will record online, shared and API (chatId required) conversation records for this app",
"Chat logs": "Chat Logs",
"Confirm Del App Tip": "Confirm to delete this app and all its chat records?",
"Connection is invalid": "Connection is invalid",
"Connection type is different": "Connection type is different",
"Copy Module Config": "Copy Config",
"Dataset Quote Template": "Knowledge Base QA Mode",
"Export Config Successful": "Config copied, please check for important data",
"Export Configs": "Export Configs",
"Feedback Count": "User Feedback",
"Import Configs": "Import Configs",
"Import Configs Failed": "Failed to import configs, please ensure configs are valid!",
"Input Field Settings": "Input Field Settings",
"Logs Empty": "No logs yet~",
"Logs Message Total": "Total Messages",
"Logs Source": "Source",
"Logs Time": "Time",
"Logs Title": "Title",
"Mark Count": "Marked Answer Count",
"My Apps": "My Apps",
"Output Field Settings": "Output Field Settings",
"Paste Config": "Paste Config",
"To Chat": "Go to Chat",
"To Settings": "View Details",
"Variable Key Repeat Tip": "Variable key is duplicate",
"module": {
"Combine Modules": "Combine Modules",
"Custom Title Tip": "This title will be displayed during the conversation",
"My Modules": "My Modules",
"No Modules": "No modules yet~",
"System Module": "System Module",
"type": "\"{{type}}\" type\n{{description}}"
},
"modules": {
"Title is required": "Module name cannot be empty"
}
}

File diff suppressed because it is too large Load Diff

View File

View File

@@ -0,0 +1,23 @@
{
"Click to view file": "Click to view the original file",
"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",
"Empty file tip": "The file content is empty, the file may not be readable or it may be a pure image file.",
"File Content": "File Content",
"File Name": "File Name",
"File Size": "File Size",
"File content can not be empty": "File content cannot be empty",
"Filename Can not Be Empty": "Filename cannot be empty",
"Read File Error": "File parsing failed",
"Select and drag file tip": "Click or drag files here to upload",
"Select failed": "File selection failed",
"Select file amount limit": "You can select up to {{max}} files",
"Select file amount limit 100": "You can select up to 100 files at a time",
"Some file count exceeds limit": "Exceeds {{maxCount}} files, automatically truncated",
"Some file size exceeds limit": "Some files exceed: {{maxSize}}, have been filtered",
"Support file type": "Supports {{fileType}} type files",
"Support max count": "Supports up to {{maxCount}} files.",
"Support max size": "Maximum size per file: {{maxSize}}.",
"Upload failed": "Upload failed"
}

View File

@@ -0,0 +1,22 @@
{
"Copy IFrame": "Embed webpage",
"Copy Link": "Copy",
"Create API Key": "Create new Key",
"Create Link": "Create link",
"Default Response": "Default Response",
"Delete Link": "Delete link",
"Edit API Key": "Edit Key information",
"Edit IFrame Link": "Update embed link",
"Edit Link": "Edit",
"Edit Share Window": "Update share window",
"Feishu name": "Lark",
"Link Name": "Name of the share link",
"QPM Tips": "How many times per minute can each IP ask at most",
"QPM is empty": "QPM cannot be empty",
"app key tips": "These keys have the current application identification, refer to the document for specific use ",
"key alias": "key alias, for display only ",
"key tips": "You can use the API Key to access certain interfaces (you can't access the app, you need to use the API key within the app to access the app)",
"token auth": "Token authentication",
"token auth Tips": "Identity verification server address, if this value is filled, a request will be sent to the specified server before each conversation to perform identity verification",
"token auth use cases": "View usage instructions for identity verification"
}

View File

@@ -0,0 +1,44 @@
{
"AI Settings": "AI 配置",
"Advance App TestTip": "当前应用可能为高级编排模式\n如需切换为【简易模式】请点击左侧保存按键",
"App Detail": "应用详情",
"Apps Share": "应用分享",
"Basic Settings": "基本信息",
"Chat Debug": "调试预览",
"Chat Logs Tips": "日志会记录该应用的在线、分享和 API(需填写 chatId) 对话记录",
"Chat logs": "对话日志",
"Confirm Del App Tip": "确认删除该应用及其所有聊天记录?",
"Connection is invalid": "连接无效",
"Connection type is different": "连接的类型不一致",
"Copy Module Config": "复制配置",
"Dataset Quote Template": "知识库问答模式",
"Export Config Successful": "已复制配置,自动过滤部分敏感信息,请注意检查是否仍有敏感数据",
"Export Configs": "导出配置",
"Feedback Count": "用户反馈",
"Import Configs": "导入配置",
"Import Configs Failed": "导入配置失败,请确保配置正常!",
"Input Field Settings": "输入字段编辑",
"Logs Empty": "还没有日志噢~",
"Logs Message Total": "消息总数",
"Logs Source": "来源",
"Logs Time": "时间",
"Logs Title": "标题",
"Mark Count": "标注答案数量",
"My Apps": "我的应用",
"Output Field Settings": "输出字段编辑",
"Paste Config": "粘贴配置",
"To Chat": "前去对话",
"To Settings": "查看详情",
"Variable Key Repeat Tip": "变量 key 重复",
"module": {
"Combine Modules": "组合模块",
"Custom Title Tip": "该标题名字会展示在对话过程中",
"My Modules": "",
"No Modules": "还没有模块~",
"System Module": "系统模块",
"type": "\"{{type}}\"类型\n{{description}}"
},
"modules": {
"Title is required": "模块名不能为空"
}
}

View File

@@ -2,57 +2,14 @@
"App": "应用",
"Export": "导出",
"Folder": "文件夹",
"Login": "登录",
"Move": "移动",
"Name": "名称",
"New Create": "新建",
"Rename": "重命名",
"Running": "运行中",
"UnKnow": "未知",
"Warning": "提示",
"app": {
"AI Advanced Settings": "AI 高级配置",
"AI Settings": "AI 配置",
"Advance App TestTip": "当前应用可能为高级编排模式\n如需切换为【简易模式】请点击左侧保存按键",
"App Detail": "应用详情",
"Apps Share": "应用分享",
"Basic Settings": "基本信息",
"Chat Debug": "调试预览",
"Chat Logs Tips": "日志会记录该应用的在线、分享和 API(需填写 chatId) 对话记录",
"Chat logs": "对话日志",
"Confirm Del App Tip": "确认删除该应用及其所有聊天记录?",
"Connection is invalid": "连接无效",
"Connection type is different": "连接的类型不一致",
"Copy Module Config": "复制配置",
"Dataset Quote Template": "知识库问答模式",
"Export Config Successful": "已复制配置,自动过滤部分敏感信息,请注意检查是否仍有敏感数据",
"Export Configs": "导出配置",
"Feedback Count": "用户反馈",
"Import Configs": "导入配置",
"Import Configs Failed": "导入配置失败,请确保配置正常!",
"Input Field Settings": "输入字段编辑",
"Logs Empty": "还没有日志噢~",
"Logs Message Total": "消息总数",
"Logs Source": "来源",
"Logs Time": "时间",
"Logs Title": "标题",
"Mark Count": "标注答案数量",
"My Apps": "我的应用",
"Output Field Settings": "输出字段编辑",
"Paste Config": "粘贴配置",
"To Chat": "前去对话",
"To Settings": "查看详情",
"Variable Key Repeat Tip": "变量 key 重复",
"module": {
"Combine Modules": "组合模块",
"Custom Title Tip": "该标题名字会展示在对话过程中",
"My Modules": "",
"No Modules": "还没有模块~",
"System Module": "系统模块",
"type": "\"{{type}}\"类型\n{{description}}"
},
"modules": {
"Title is required": "模块名不能为空"
}
},
"common": {
"Action": "操作",
"Add": "添加",
@@ -113,7 +70,6 @@
"Name": "名称",
"Name Can": "名称不能为空",
"Name is empty": "名称不能为空",
"New Create": "新建",
"Next Step": "下一步",
"No more data": "没有更多了~",
"Not open": "未开启",
@@ -193,26 +149,6 @@
"Update error": "更新失败",
"unKnow": "出现了点意外~"
},
"export": "",
"file": {
"Empty file tip": "文件内容为空,可能该文件无法读取或为纯图片文件内容。",
"File Content": "文件内容",
"File Name": "文件名",
"File Size": "文件大小",
"File content can not be empty": "文件内容不能为空",
"Filename Can not Be Empty": "文件名不能为空",
"Read File Error": "解析文件失败",
"Select and drag file tip": "点击或拖动文件到此处上传",
"Select failed": "选择文件异常",
"Select file amount limit": "最多选择 {{max}} 个文件",
"Select file amount limit 100": "每次最多选择100个文件",
"Some file count exceeds limit": "超出{{maxCount}}个文件,已自动截取",
"Some file size exceeds limit": "部分文件超出: {{maxSize}},已被过滤",
"Support file type": "支持 {{fileType}} 类型文件",
"Support max count": "最多支持 {{maxCount}} 个文件。",
"Support max size": "单个文件最大 {{maxSize}}。",
"Upload failed": "上传异常"
},
"folder": {
"Drag Tip": "点我可拖动",
"Move Success": "移动成功",
@@ -644,7 +580,8 @@
"success": "开始同步"
}
},
"training": {}
"training": {
}
},
"data": {
"Auxiliary Data": "辅助数据",
@@ -707,7 +644,6 @@
"Estimated Price Tips": "QA计费为\n输入: {{charsPointsPrice}}积分/1k Tokens",
"Estimated points": "预估消耗 {{points}} 积分",
"Fetch Error": "获取链接失败",
"Fetch Url": "网络链接",
"Fetch url placeholder": "最多10个链接每行一个。",
"Fetch url tip": "仅支持读取静态链接,请注意检查结果",
"File chunk amount": "分段: {{amount}}",
@@ -933,10 +869,10 @@
"params": "Params"
},
"input": {
"Add Branch": "添加分支",
"Add Input": "添加入参",
"Input Number": "入参: {{length}}",
"add": "添加条件",
"Add Branch": "添加分支",
"description": {
"Background": "你可以添加一些特定内容的介绍,从而更好的识别用户的问题类型。这个内容通常是给模型介绍一个它不知道的内容。",
"HTTP Dynamic Input": "接收前方节点的输出值作为变量这些变量可以被HTTP请求参数使用。",
@@ -1252,73 +1188,6 @@
"overSize": "团队成员超出上限"
}
},
"file": {
"Click to download file template": "点击下载模板:{{name}}",
"Click to view file": "点击查看原始文件",
"Create File": "创建新文件",
"Create file": "创建文件",
"Drag and drop": "拖拽文件至此",
"Fetch Url": "链接读取",
"If the imported file is garbled, please convert CSV to UTF-8 encoding format": "如果导入文件乱码,请将 CSV 转成 UTF-8 编码格式",
"Parse": "{{name}} 解析中...",
"Release the mouse to upload the file": "松开鼠标上传文件",
"Select a maximum of 10 files": "最多选择10个文件",
"Uploading": "正在上传 {{name}},进度: {{percent}}%",
"max 10": "最多选择 10 个文件",
"select a document": "选择文件",
"support": "支持 {{fileExtension}} 文件",
"upload error description": "单次只支持上传多个文件或者一个文件夹"
},
"home": {
"AI Assistant": "AI 客服",
"AI Assistant Desc": "无论对内还是对外AI 将 24 小时为您的用户提供服务",
"Advanced Settings": "高级编排",
"Advanced Settings Desc": "基于 Flow 的流程编排模式,让你的 AI 轻松实现数据库查询、IO 操作、联网通信等扩展能力",
"Choice Debug": "调试便捷",
"Choice Debug Desc": "拥有搜索测试、引用修改、完整对话预览等多种调试途径",
"Choice Desc": "",
"Choice Extension": "无限扩展",
"Choice Extension Desc": "基于 HTTP 实现扩展,轻松实现定制功能",
"Choice Fast": "开箱即用",
"Choice Fast Desc": "{{title}} 提供开箱即用的可视化操作,点点点即可构建 AI 应用",
"Choice Models": "支持多种模型",
"Choice Models Desc": "支持 GPT、Claude、Spark、ChatGLM等多模型",
"Choice Open": "更开放",
"Choice Open Desc": "{{title}} 遵循 Apache License 2.0 开源协议",
"Choice QA": "独特的 QA 结构",
"Choice QA Desc": "采用 QA 对的结构构建索引,适应问答、阅读等多种场景",
"Choice Visual": "可视化工作流",
"Choice Visual Desc": "可视化模块操作,轻松实现复杂工作流,让你的 AI 不再单一",
"Commercial": "商务咨询",
"Community": "社区",
"Dateset": "自动数据预处理",
"Dateset Desc": "提供手动输入、直接分段、LLM 自动处理和 CSV 等多种数据导入途径",
"Docs": "文档",
"FastGPT Ability": "{{title}} 能力",
"FastGPT Desc": "{{title}} 是一个基于 LLM 大语言模型的知识库问答系统,提供开箱即用的数据处理、模型调用等能力。同时可以通过 Flow 可视化进行工作流编排,从而实现复杂的问答场景!",
"Features": "特点",
"Footer Developer": "开发者",
"Footer Docs": "文档",
"Footer FastGPT Cloud": "{{title}} 线上服务",
"Footer Feedback": "反馈",
"Footer Git": "源码",
"Footer Product": "产品",
"Footer Support": "支持",
"Login": "登录",
"Open": "",
"OpenAPI": "OpenAPI",
"OpenAPI Desc": "与 GPT API 一致的对外接口,助你轻松接入已有应用",
"Quickly build AI question and answer library": "快速搭建 AI 问答系统",
"Start Now": "立即开始",
"Visual AI orchestration": "可视化 AI 编排",
"Why FastGPT": "为什么选择 {{title}}",
"desc": "基于 LLM 大模型的 AI 知识库问答平台",
"navbar": {
"Use guidance": "使用引导",
"chatbot": "问答机器人"
},
"slogan": "让 AI 更懂你的知识"
},
"module": {
"Confirm Delete Module": "确认删除该自定义模块?",
"Confirm Sync Plugin": "确认同步插件最新信息?插件的连线和输入的内容将会被清空,请确认!",
@@ -1343,30 +1212,6 @@
"Store": "应用市场",
"Tools": "工具"
},
"openapi": {
"app key tips": "这些 key 已有当前应用标识,具体使用可参考文档",
"key alias": "key 的别名,仅用于展示",
"key tips": "你可以使用 API 秘钥访问一些特定的接口(无法访问应用访问应用需使用应用内的API Key)"
},
"outlink": {
"Copy IFrame": "嵌入网页",
"Copy Link": "复制",
"Create API Key": "创建新 Key",
"Create Link": "创建链接",
"Delete Link": "删除链接",
"Edit API Key": "编辑 Key 信息",
"Edit IFrame Link": "更新嵌入链接",
"Edit Link": "编辑",
"Edit Share Window": "更新分享窗口",
"Link Name": "分享链接的名字",
"Link is empty": "",
"QPM": "",
"QPM Tips": "每个 IP 每分钟最多提问多少次",
"QPM is empty": "QPM 不能为空",
"token auth": "身份验证",
"token auth Tips": "身份校验服务器地址,如填写该值,每次对话前都会向指定服务器发送一个请求,进行身份校验",
"token auth use cases": "查看身份验证使用说明"
},
"permission": {
"Private": "私有",
"Private Tip": "仅自己可用",

View File

View File

@@ -0,0 +1,23 @@
{
"Click to view file": "点击查看原始文件",
"Release the mouse to upload the file": "松开鼠标上传文件",
"upload error description": "单次只支持上传多个文件或者一个文件夹",
"Empty file tip": "文件内容为空,可能该文件无法读取或为纯图片文件内容。",
"File Content": "文件内容",
"File Name": "文件名",
"File Size": "文件大小",
"File content can not be empty": "文件内容不能为空",
"Filename Can not Be Empty": "文件名不能为空",
"Read File Error": "解析文件失败",
"Select and drag file tip": "点击或拖动文件到此处上传",
"Select failed": "选择文件异常",
"Select file amount limit": "最多选择 {{max}} 个文件",
"Select file amount limit 100": "每次最多选择100个文件",
"Some file count exceeds limit": "超出{{maxCount}}个文件,已自动截取",
"Some file size exceeds limit": "部分文件超出: {{maxSize}},已被过滤",
"Support file type": "支持 {{fileType}} 类型文件",
"Support max count": "最多支持 {{maxCount}} 个文件。",
"Support max size": "单个文件最大 {{maxSize}}。",
"Upload failed": "上传异常"
}

View File

@@ -0,0 +1,20 @@
{
"Create API Key": "创建新 Key",
"Create Link": "创建链接",
"Default Response": "默认回复",
"Delete Link": "删除链接",
"Edit API Key": "编辑 Key 信息",
"Edit IFrame Link": "更新嵌入链接",
"Edit Link": "编辑",
"Edit Share Window": "更新分享窗口",
"Feishu name": "飞书",
"Link Name": "分享链接的名字",
"QPM Tips": "每个 IP 每分钟最多提问多少次",
"QPM is empty": "QPM 不能为空",
"app key tips": "这些 key 已有当前应用标识,具体使用可参考文档",
"key alias": "key 的别名,仅用于展示",
"key tips": "你可以使用 API 秘钥访问一些特定的接口(无法访问应用访问应用需使用应用内的API Key)",
"token auth": "身份验证",
"token auth Tips": "身份校验服务器地址,如填写该值,每次对话前都会向指定服务器发送一个请求,进行身份校验",
"token auth use cases": "查看身份验证使用说明"
}

View File

@@ -1,7 +1,7 @@
import React, { useMemo, useState } from 'react';
import { type ChatHistoryItemResType } from '@fastgpt/global/core/chat/type.d';
import { DispatchNodeResponseType } from '@fastgpt/global/core/workflow/runtime/type.d';
import { Flex, BoxProps, useDisclosure, useTheme, Box } from '@chakra-ui/react';
import { Flex, useDisclosure, useTheme, Box } from '@chakra-ui/react';
import { useTranslation } from 'next-i18next';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import type { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type';

View File

@@ -1,5 +1,5 @@
import React, { useMemo } from 'react';
import { Box, BoxProps, Image } from '@chakra-ui/react';
import { Box, BoxProps } from '@chakra-ui/react';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { getErrText } from '@fastgpt/global/common/error/utils';
import MyTooltip from '@/components/MyTooltip';
@@ -8,6 +8,7 @@ import { getFileAndOpen } from '@/web/core/dataset/utils';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { getSourceNameIcon } from '@fastgpt/global/core/dataset/utils';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useI18n } from '@/web/context/I18n';
type Props = BoxProps & {
sourceName?: string;
@@ -17,6 +18,7 @@ type Props = BoxProps & {
const RawSourceBox = ({ sourceId, sourceName = '', canView = true, ...props }: Props) => {
const { t } = useTranslation();
const { fileT } = useI18n();
const { toast } = useToast();
const { setLoading } = useSystemStore();
@@ -25,10 +27,7 @@ const RawSourceBox = ({ sourceId, sourceName = '', canView = true, ...props }: P
const icon = useMemo(() => getSourceNameIcon({ sourceId, sourceName }), [sourceId, sourceName]);
return (
<MyTooltip
label={canPreview ? t('file.Click to view file') || '' : ''}
shouldWrapChildren={false}
>
<MyTooltip label={canPreview ? fileT('Click to view file') : ''} shouldWrapChildren={false}>
<Box
color={'myGray.900'}
fontWeight={'medium'}
@@ -44,7 +43,7 @@ const RawSourceBox = ({ sourceId, sourceName = '', canView = true, ...props }: P
await getFileAndOpen(sourceId as string);
} catch (error) {
toast({
title: t(getErrText(error, 'error.fileNotFound')),
title: getErrText(error, t('error.fileNotFound')),
status: 'error'
});
}

View File

@@ -1,19 +1,18 @@
import React, { useState } from 'react';
import { Textarea, Button, ModalBody, ModalFooter } from '@chakra-ui/react';
import MyModal from '@fastgpt/web/components/common/MyModal';
import { useTranslation } from 'next-i18next';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { useContextSelector } from 'use-context-selector';
import { WorkflowContext } from '../context';
import { useI18n } from '@/web/context/I18n';
type Props = {
onClose: () => void;
};
const ImportSettings = ({ onClose }: Props) => {
const { t } = useTranslation();
const { appT } = useI18n();
const { toast } = useToast();
const initData = useContextSelector(WorkflowContext, (v) => v.initData);
const [value, setValue] = useState('');
@@ -23,11 +22,11 @@ const ImportSettings = ({ onClose }: Props) => {
w={'600px'}
onClose={onClose}
iconSrc="/imgs/modal/params.svg"
title={t('app.Import Configs')}
title={appT('Import Configs')}
>
<ModalBody>
<Textarea
placeholder={t('app.Paste Config') || 'app.Paste Config'}
placeholder={appT('Paste Config')}
defaultValue={value}
rows={16}
onChange={(e) => setValue(e.target.value)}
@@ -46,7 +45,7 @@ const ImportSettings = ({ onClose }: Props) => {
onClose();
} catch (error) {
toast({
title: t('app.Import Configs Failed')
title: appT('Import Configs Failed')
});
}
}}

View File

@@ -28,6 +28,7 @@ import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import { useContextSelector } from 'use-context-selector';
import { WorkflowContext } from '../context';
import { useCreation } from 'ahooks';
import { useI18n } from '@/web/context/I18n';
type ModuleTemplateListProps = {
isOpen: boolean;
@@ -251,6 +252,8 @@ const RenderList = React.memo(function RenderList({
setCurrentParent
}: RenderListProps) {
const { t } = useTranslation();
const { appT } = useI18n();
const { isPc } = useSystemStore();
const { x, y, zoom } = useViewport();
const { setLoading } = useSystemStore();
@@ -323,7 +326,7 @@ const RenderList = React.memo(function RenderList({
const Render = useMemo(() => {
return templates.length === 0 ? (
<EmptyTip text={t('app.module.No Modules')} />
<EmptyTip text={appT('module.No Modules')} />
) : (
<Box flex={'1 0 0'} overflow={'overlay'} px={'20px'}>
<Box mx={'auto'}>

View File

@@ -9,7 +9,7 @@ import { useTranslation } from 'next-i18next';
import { getLafAppDetail } from '@/web/support/laf/api';
import MySelect from '@fastgpt/web/components/common/MySelect';
import { getApiSchemaByUrl } from '@/web/core/plugin/api';
import { str2OpenApiSchema } from '@fastgpt/global/core/plugin/httpPlugin/utils';
import { getType, str2OpenApiSchema } from '@fastgpt/global/core/plugin/httpPlugin/utils';
import { useUserStore } from '@/web/support/user/useUserStore';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { ChevronRightIcon } from '@chakra-ui/icons';
@@ -155,7 +155,7 @@ const NodeLaf = (props: NodeProps<FlowNodeItemType>) => {
desc: bodyParams[key].description,
required: requiredParams?.includes(key) || false,
value: `{{${key}}}`,
type: bodyParams[key].type
type: getType(bodyParams[key])
}))
].filter((item) => !inputs.find((input) => input.key === item.name));
@@ -189,7 +189,7 @@ const NodeLaf = (props: NodeProps<FlowNodeItemType>) => {
const allResponseParams = [
...Object.keys(responseParams).map((key) => ({
valueType: responseParams[key].type,
valueType: getType(responseParams[key]),
name: key,
desc: responseParams[key].description,
required: requiredResponseParams?.includes(key) || false

View File

@@ -203,16 +203,16 @@ const MyTargetHandle = React.memo(function MyTargetHandle({
if (connectingEdge?.handleId && !connectingEdge.handleId?.includes('source')) return false;
// From same source node
// From same source node and same handle
if (
connectedEdges.some(
(item) => item.source === connectingEdge?.nodeId && item.target === nodeId
(item) => item.sourceHandle === connectingEdge?.handleId && item.target === nodeId
)
)
return false;
return true;
}, [connectedEdges, connectingEdge?.handleId, connectingEdge?.nodeId, edges, node, nodeId]);
}, [connectedEdges, connectingEdge?.handleId, edges, node, nodeId]);
const RenderHandle = useMemo(() => {
return (

View File

@@ -22,6 +22,7 @@ import { storeNode2FlowNode } from '@/web/core/workflow/utils';
import { getNanoid } from '@fastgpt/global/common/string/tools';
import { useContextSelector } from 'use-context-selector';
import { WorkflowContext } from '../../../context';
import { useI18n } from '@/web/context/I18n';
type Props = FlowNodeItemType & {
children?: React.ReactNode | React.ReactNode[] | string;
@@ -38,6 +39,8 @@ type Props = FlowNodeItemType & {
const NodeCard = (props: Props) => {
const { t } = useTranslation();
const { appT } = useI18n();
const { toast } = useToast();
const {
@@ -66,7 +69,7 @@ const NodeCard = (props: Props) => {
// custom title edit
const { onOpenModal: onOpenCustomTitleModal, EditModal: EditTitleModal } = useEditTitle({
title: t('common.Custom Title'),
placeholder: t('app.module.Custom Title Tip') || ''
placeholder: appT('module.Custom Title Tip') || ''
});
const showToolHandle = useMemo(
@@ -105,7 +108,7 @@ const NodeCard = (props: Props) => {
onSuccess: (e) => {
if (!e) {
return toast({
title: t('app.modules.Title is required'),
title: appT('modules.Title is required'),
status: 'warning'
});
}
@@ -132,8 +135,8 @@ const NodeCard = (props: Props) => {
</Box>
);
}, [
nodeId,
showToolHandle,
nodeId,
avatar,
t,
name,
@@ -143,7 +146,8 @@ const NodeCard = (props: Props) => {
intro,
onOpenCustomTitleModal,
onChangeNode,
toast
toast,
appT
]);
return (

View File

@@ -43,6 +43,7 @@ import MyTooltip from '@/components/MyTooltip';
import { getDocPath } from '@/web/common/system/doc';
import MyMenu from '@fastgpt/web/components/common/MyMenu';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import { useI18n } from '@/web/context/I18n';
type EditProps = EditApiKeyProps & { _id?: string };
const defaultEditData: EditProps = {
@@ -135,7 +136,7 @@ const ApiKeyTable = ({ tips, appId }: { tips: string; appId?: string }) => {
})
}
>
{t('common.New Create')}
{t('New Create')}
</Button>
</Box>
</Box>
@@ -295,6 +296,7 @@ function EditKeyModal({
onEdit: () => void;
}) {
const { t } = useTranslation();
const { publishT } = useI18n();
const isEdit = useMemo(() => !!defaultData._id, [defaultData]);
const { feConfigs } = useSystemStore();
@@ -324,13 +326,13 @@ function EditKeyModal({
<MyModal
isOpen={true}
iconSrc="/imgs/modal/key.svg"
title={isEdit ? t('outlink.Edit API Key') : t('outlink.Create API Key')}
title={isEdit ? publishT('Edit API Key') : publishT('Create API Key')}
>
<ModalBody>
<Flex alignItems={'center'}>
<Box flex={'0 0 90px'}>{t('Name')}:</Box>
<Input
placeholder={t('openapi.key alias') || 'key alias'}
placeholder={publishT('key alias') || 'key alias'}
maxLength={20}
{...register('name', {
required: t('common.Name is empty') || 'Name is empty'

View File

@@ -6,6 +6,7 @@ import { appWithTranslation } from 'next-i18next';
import QueryClientContext from '@/web/context/QueryClient';
import ChakraUIContext from '@/web/context/ChakraUI';
import I18nContextProvider from '@/web/context/I18n';
import { useInitApp } from '@/web/context/useInitApp';
import '@/web/styles/reset.scss';
@@ -34,11 +35,13 @@ function App({ Component, pageProps }: AppProps) {
{scripts?.map((item, i) => <Script key={i} strategy="lazyOnload" {...item}></Script>)}
<QueryClientContext>
<ChakraUIContext>
<Layout>
<Component {...pageProps} />
</Layout>
</ChakraUIContext>
<I18nContextProvider>
<ChakraUIContext>
<Layout>
<Component {...pageProps} />
</Layout>
</ChakraUIContext>
</I18nContextProvider>
</QueryClientContext>
</>
);

View File

@@ -1,10 +1,11 @@
import React from 'react';
import { useTranslation } from 'next-i18next';
import ApiKeyTable from '@/components/support/apikey/Table';
import { useI18n } from '@/web/context/I18n';
const ApiKey = () => {
const { t } = useTranslation();
return <ApiKeyTable tips={t('openapi.key tips')}></ApiKeyTable>;
const { publishT } = useI18n();
return <ApiKeyTable tips={publishT('key tips')}></ApiKeyTable>;
};
export default ApiKey;

View File

@@ -190,7 +190,7 @@ export async function getServerSideProps(content: any) {
return {
props: {
currentTab: content?.query?.currentTab || TabEnum.info,
...(await serviceSideProps(content))
...(await serviceSideProps(content, ['publish']))
}
};
}

View File

@@ -1,10 +1,7 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response';
import { connectToDatabase } from '@/service/mongo';
import {
delFileByFileIdList,
readFileContentFromMongo
} from '@fastgpt/service/common/file/gridfs/controller';
import { readFileContentFromMongo } from '@fastgpt/service/common/file/gridfs/controller';
import { authDataset } from '@fastgpt/service/support/permission/auth/dataset';
import { FileIdCreateDatasetCollectionParams } from '@fastgpt/global/core/dataset/api';
import { createOneCollection } from '@fastgpt/service/core/dataset/collection/controller';
@@ -24,6 +21,7 @@ import { UsageSourceEnum } from '@fastgpt/global/support/wallet/usage/constants'
import { getLLMModel, getVectorModel } from '@fastgpt/service/core/ai/model';
import { hashStr } from '@fastgpt/global/common/string/tools';
import { startTrainingQueue } from '@/service/core/dataset/training/utils';
import { MongoRawTextBuffer } from '@fastgpt/service/common/buffer/rawText/schema';
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
const {
@@ -139,6 +137,9 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
return collectionId;
});
// remove buffer
await MongoRawTextBuffer.deleteOne({ sourceId: fileId });
startTrainingQueue(true);
jsonRes(res);

View File

@@ -1,34 +1,24 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response';
import { connectToDatabase } from '@/service/mongo';
import { authDataset } from '@fastgpt/service/support/permission/auth/dataset';
import { checkExportDatasetLimit } from '@fastgpt/service/support/user/utils';
import { NextAPI } from '@/service/middle/entry';
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try {
await connectToDatabase();
const { datasetId } = req.query as {
datasetId: string;
};
async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
const { datasetId } = req.query as {
datasetId: string;
};
if (!datasetId) {
throw new Error('datasetId is required');
}
// 凭证校验
const { teamId } = await authDataset({ req, authToken: true, datasetId, per: 'w' });
await checkExportDatasetLimit({
teamId,
limitMinutes: global.feConfigs?.limit?.exportDatasetLimitMinutes
});
jsonRes(res);
} catch (err) {
res.status(500);
jsonRes(res, {
code: 500,
error: err
});
if (!datasetId) {
throw new Error('datasetId is required');
}
// 凭证校验
const { teamId } = await authDataset({ req, authToken: true, datasetId, per: 'w' });
await checkExportDatasetLimit({
teamId,
limitMinutes: global.feConfigs?.limit?.exportDatasetLimitMinutes
});
}
export default NextAPI(handler);

View File

@@ -26,6 +26,7 @@ import { formatTime2HM } from '@fastgpt/global/common/string/time';
import { useContextSelector } from 'use-context-selector';
import { WorkflowContext, getWorkflowStore } from '@/components/core/workflow/context';
import { useInterval, useUpdateEffect } from 'ahooks';
import { useI18n } from '@/web/context/I18n';
const ImportSettings = dynamic(() => import('@/components/core/workflow/Flow/ImportSettings'));
const PublishHistories = dynamic(
@@ -56,6 +57,8 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
const theme = useTheme();
const { toast } = useToast();
const { t } = useTranslation();
const { appT } = useI18n();
const { copyData } = useCopyData();
const { openConfirm: openConfigPublish, ConfirmModal } = useConfirm({
content: t('core.app.Publish Confirm')
@@ -177,10 +180,10 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
null,
2
),
t('app.Export Config Successful')
appT('Export Config Successful')
);
}
}, [copyData, flowData2StoreDataAndCheck, t]);
}, [appT, copyData, flowData2StoreDataAndCheck]);
// effect
useBeforeunload({
@@ -254,12 +257,12 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
}
menuList={[
{
label: t('app.Import Configs'),
label: appT('Import Configs'),
icon: 'common/importLight',
onClick: onOpenImport
},
{
label: t('app.Export Configs'),
label: appT('Export Configs'),
icon: 'export',
onClick: onExportWorkflow
}
@@ -316,6 +319,7 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
isV2Workflow,
t,
saveLabel,
appT,
onOpenImport,
onExportWorkflow,
openConfigPublish,

View File

@@ -33,9 +33,12 @@ import { usePagination } from '@fastgpt/web/hooks/usePagination';
import DateRangePicker, { DateRangeType } from '@fastgpt/web/components/common/DateRangePicker';
import { formatChatValue2InputType } from '@/components/ChatBox/utils';
import { getNanoid } from '@fastgpt/global/common/string/tools';
import { useI18n } from '@/web/context/I18n';
const Logs = ({ appId }: { appId: string }) => {
const { t } = useTranslation();
const { appT } = useI18n();
const { isPc } = useSystemStore();
const [dateRange, setDateRange] = useState<DateRangeType>({
@@ -73,10 +76,10 @@ const Logs = ({ appId }: { appId: string }) => {
{isPc && (
<>
<Box fontWeight={'bold'} fontSize={['md', 'xl']} mb={2}>
{t('app.Chat logs')}
{appT('Chat logs')}
</Box>
<Box color={'myGray.500'} fontSize={'sm'}>
{t('app.Chat Logs Tips')},{' '}
{appT('Chat Logs Tips')},{' '}
<Box
as={'span'}
mr={2}
@@ -97,11 +100,11 @@ const Logs = ({ appId }: { appId: string }) => {
<Thead>
<Tr>
<Th>{t('core.app.logs.Source And Time')}</Th>
<Th>{t('app.Logs Title')}</Th>
<Th>{t('app.Logs Message Total')}</Th>
<Th>{t('app.Feedback Count')}</Th>
<Th>{appT('Logs Title')}</Th>
<Th>{appT('Logs Message Total')}</Th>
<Th>{appT('Feedback Count')}</Th>
<Th>{t('core.app.feedback.Custom feedback')}</Th>
<Th>{t('app.Mark Count')}</Th>
<Th>{appT('Mark Count')}</Th>
</Tr>
</Thead>
<Tbody>
@@ -176,7 +179,7 @@ const Logs = ({ appId }: { appId: string }) => {
<Flex h={'100%'} flexDirection={'column'} alignItems={'center'} pt={'10vh'}>
<MyIcon name="empty" w={'48px'} h={'48px'} color={'transparent'} />
<Box mt={2} color={'myGray.500'}>
{t('app.Logs Empty')}
{appT('Logs Empty')}
</Box>
</Flex>
)}

View File

@@ -2,12 +2,13 @@ import React, { useEffect, useState } from 'react';
import ApiKeyTable from '@/components/support/apikey/Table';
import { useTranslation } from 'next-i18next';
import { Box } from '@chakra-ui/react';
import { useI18n } from '@/web/context/I18n';
const API = ({ appId }: { appId: string }) => {
const { t } = useTranslation();
const { publishT } = useI18n();
return (
<Box pt={3}>
<ApiKeyTable tips={t('openapi.app key tips')} appId={appId} />
<ApiKeyTable tips={publishT('app key tips')} appId={appId} />
</Box>
);
};

View File

@@ -10,6 +10,7 @@ import { useForm } from 'react-hook-form';
import { useRequest } from '@/web/common/hooks/useRequest';
import dayjs from 'dayjs';
import { createShareChat, updateShareChat } from '@/web/support/outLink/api';
import { useI18n } from '@/web/context/I18n';
const FeiShuEditModal = ({
appId,
@@ -25,6 +26,7 @@ const FeiShuEditModal = ({
onEdit: () => void;
}) => {
const { t } = useTranslation();
const { publishT } = useI18n();
const {
register,
setValue,
@@ -58,13 +60,13 @@ const FeiShuEditModal = ({
<MyModal
isOpen={true}
iconSrc="/imgs/modal/shareFill.svg"
title={isEdit ? t('outlink.Edit Link') : t('outlink.Create Link')}
title={isEdit ? publishT('Edit Link') : publishT('Create Link')}
>
<ModalBody>
<Flex alignItems={'center'}>
<Box flex={'0 0 90px'}>{t('Name')}</Box>
<Input
placeholder={t('outlink.Feishu name') || 'Link Name'} // TODO: i18n
placeholder={publishT('Feishu name') || 'Link Name'} // TODO: i18n
maxLength={20}
{...register('name', {
required: t('common.Name is empty') || 'Name is empty'
@@ -74,7 +76,7 @@ const FeiShuEditModal = ({
<Flex alignItems={'center'} mt={4}>
<Flex flex={'0 0 90px'} alignItems={'center'}>
QPM
<MyTooltip label={t('outlink.QPM Tips' || '')}>
<MyTooltip label={publishT('QPM Tips' || '')}>
<QuestionOutlineIcon ml={1} />
</MyTooltip>
</Flex>
@@ -84,7 +86,7 @@ const FeiShuEditModal = ({
min: 0,
max: 1000,
valueAsNumber: true,
required: t('outlink.QPM is empty') || ''
required: publishT('QPM is empty') || ''
})}
/>
</Flex>
@@ -126,10 +128,10 @@ const FeiShuEditModal = ({
{/* TODO: i18n */}
</Flex>
<Input
placeholder={t('outlink.Default Response') || 'Link Name'}
placeholder={publishT('Default Response') || 'Link Name'}
maxLength={20}
{...register('defaultResponse', {
required: t('common.default Response is empty') || 'Name is empty'
required: true
})}
/>
</Flex>
@@ -139,10 +141,10 @@ const FeiShuEditModal = ({
{/* TODO: i18n */}
</Flex>
<Input
placeholder={t('outlink.Default Response') || 'Link Name'}
placeholder={publishT('Default Response') || 'Link Name'}
maxLength={20}
{...register('immediateResponse', {
required: t('common.default Response is empty') || 'Name is empty'
required: true
})}
/>
</Flex>
@@ -152,14 +154,14 @@ const FeiShuEditModal = ({
placeholder={t('core.module.http.appId') || 'Link Name'}
// maxLength={20}
{...register('app.appId', {
required: t('common.Name is empty') || 'Name is empty'
required: true
})}
/>
</Flex>
<Flex alignItems={'center'} mt={4}>
<Box flex={'0 0 90px'}>{t('core.module.http.AppSecret')}</Box>
<Input
placeholder={t('outlink.AppSecret') || 'Link Name'}
placeholder={'App Secret'}
// maxLength={20}
{...register('app.appSecret', {
required: t('common.Name is empty') || 'Name is empty'

View File

@@ -44,6 +44,7 @@ import { getDocPath } from '@/web/common/system/doc';
import dynamic from 'next/dynamic';
import MyMenu from '@fastgpt/web/components/common/MyMenu';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import { useI18n } from '@/web/context/I18n';
const SelectUsingWayModal = dynamic(() => import('./SelectUsingWayModal'));
@@ -261,6 +262,7 @@ function EditLinkModal({
}) {
const { feConfigs } = useSystemStore();
const { t } = useTranslation();
const { publishT } = useI18n();
const {
register,
setValue,
@@ -293,13 +295,13 @@ function EditLinkModal({
<MyModal
isOpen={true}
iconSrc="/imgs/modal/shareFill.svg"
title={isEdit ? t('outlink.Edit Link') : t('outlink.Create Link')}
title={isEdit ? publishT('Edit Link') : publishT('Create Link')}
>
<ModalBody>
<Flex alignItems={'center'}>
<Box flex={'0 0 90px'}>{t('Name')}</Box>
<Input
placeholder={t('outlink.Link Name') || 'Link Name'}
placeholder={publishT('Link Name')}
maxLength={20}
{...register('name', {
required: t('common.Name is empty') || 'Name is empty'
@@ -327,7 +329,7 @@ function EditLinkModal({
<Flex alignItems={'center'} mt={4}>
<Flex flex={'0 0 90px'} alignItems={'center'}>
QPM
<MyTooltip label={t('outlink.QPM Tips' || '')}>
<MyTooltip label={publishT('QPM Tips' || '')}>
<QuestionOutlineIcon ml={1} />
</MyTooltip>
</Flex>
@@ -337,7 +339,7 @@ function EditLinkModal({
min: 0,
max: 1000,
valueAsNumber: true,
required: t('outlink.QPM is empty') || ''
required: publishT('QPM is empty') || ''
})}
/>
</Flex>
@@ -360,13 +362,13 @@ function EditLinkModal({
<Flex alignItems={'center'} mt={4}>
<Flex flex={'0 0 90px'} alignItems={'center'}>
{t('outlink.token auth')}
<MyTooltip label={t('outlink.token auth Tips') || ''}>
{publishT('token auth')}
<MyTooltip label={publishT('token auth Tips') || ''}>
<QuestionOutlineIcon ml={1} />
</MyTooltip>
</Flex>
<Input
placeholder={t('outlink.token auth Tips') || ''}
placeholder={publishT('token auth Tips') || ''}
fontSize={'sm'}
{...register('limit.hookUrl')}
/>
@@ -377,7 +379,7 @@ function EditLinkModal({
fontSize={'sm'}
color={'myGray.500'}
>
{t('outlink.token auth use cases')}
{publishT('token auth use cases')}
</Link>
</>
)}

View File

@@ -15,11 +15,14 @@ import Avatar from '@/components/Avatar';
import MyIcon from '@fastgpt/web/components/common/Icon';
import TagsEditModal from './TagsEditModal';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useI18n } from '@/web/context/I18n';
const InfoModal = dynamic(() => import('../InfoModal'));
const AppCard = ({ appId }: { appId: string }) => {
const router = useRouter();
const { t } = useTranslation();
const { appT } = useI18n();
const { toast } = useToast();
const { appDetail } = useAppStore();
const { feConfigs } = useSystemStore();
@@ -27,7 +30,7 @@ const AppCard = ({ appId }: { appId: string }) => {
const [TeamTagsSet, setTeamTagsSet] = useState<AppSchema>();
const { openConfirm: openConfirmDel, ConfirmModal: ConfirmDelModal } = useConfirm({
content: t('app.Confirm Del App Tip'),
content: appT('Confirm Del App Tip'),
type: 'delete'
});

View File

@@ -21,6 +21,7 @@ import { UseFormReturn } from 'react-hook-form';
import { AppSimpleEditFormType } from '@fastgpt/global/core/app/type';
import { useAppStore } from '@/web/core/app/store/useAppStore';
import { form2AppWorkflow } from '@/web/core/app/utils';
import { useI18n } from '@/web/context/I18n';
const ChatTest = ({
editForm,
@@ -30,6 +31,8 @@ const ChatTest = ({
appId: string;
}) => {
const { t } = useTranslation();
const { appT } = useI18n();
const { userInfo } = useUserStore();
const ChatBoxRef = useRef<ComponentRef>(null);
const { appDetail } = useAppStore();
@@ -114,7 +117,7 @@ const ChatTest = ({
>
<Flex px={[2, 5]}>
<Box fontSize={['md', 'xl']} fontWeight={'bold'} flex={1}>
{t('app.Chat Debug')}
{appT('Chat Debug')}
</Box>
<MyTooltip label={t('core.chat.Restart')}>
<IconButton
@@ -160,7 +163,7 @@ const ChatTest = ({
whiteSpace={'pre-wrap'}
textAlign={'center'}
>
<Box>{t('app.Advance App TestTip')}</Box>
<Box>{appT('Advance App TestTip')}</Box>
</Flex>
)}
</Flex>

View File

@@ -29,6 +29,7 @@ import DeleteIcon, { hoverDeleteStyles } from '@fastgpt/web/components/common/Ic
import { TTSTypeEnum } from '@/constants/app';
import { getSystemVariables } from '@/web/core/app/utils';
import { useUpdate } from 'ahooks';
import { useI18n } from '@/web/context/I18n';
const DatasetSelectModal = dynamic(() => import('@/components/core/app/DatasetSelectModal'));
const DatasetParamsModal = dynamic(() => import('@/components/core/app/DatasetParamsModal'));
@@ -61,6 +62,8 @@ const EditForm = ({
const theme = useTheme();
const router = useRouter();
const { t } = useTranslation();
const { appT } = useI18n();
const { publishApp, appDetail } = useAppStore();
const { allDatasets } = useDatasetStore();
@@ -200,7 +203,7 @@ const EditForm = ({
<Flex alignItems={'center'}>
<MyIcon name={'core/app/simpleMode/ai'} w={'20px'} />
<Box ml={2} flex={1}>
{t('app.AI Settings')}
{appT('AI Settings')}
</Box>
</Flex>
<Flex alignItems={'center'} mt={5}>

View File

@@ -17,6 +17,7 @@ import { serviceSideProps } from '@/web/common/utils/i18n';
import { useAppStore } from '@/web/core/app/store/useAppStore';
import Head from 'next/head';
import { useTranslation } from 'next-i18next';
import { useI18n } from '@/web/context/I18n';
const FlowEdit = dynamic(() => import('./components/FlowEdit'), {
loading: () => <Loading />
@@ -34,6 +35,8 @@ enum TabEnum {
const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
const { t } = useTranslation();
const { appT } = useI18n();
const router = useRouter();
const theme = useTheme();
const { feConfigs } = useSystemStore();
@@ -74,10 +77,10 @@ const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
id: TabEnum.publish,
icon: 'support/outlink/shareLight'
},
{ label: t('app.Chat logs'), id: TabEnum.logs, icon: 'core/app/logsLight' },
{ label: appT('Chat logs'), id: TabEnum.logs, icon: 'core/app/logsLight' },
{ label: t('core.Start chat'), id: TabEnum.startChat, icon: 'core/chat/chatLight' }
],
[feConfigs?.hide_app_flow, t]
[appT, feConfigs?.hide_app_flow, t]
);
const onCloseFlowEdit = useCallback(() => setCurrentTab(TabEnum.simpleEdit), [setCurrentTab]);
@@ -150,7 +153,7 @@ const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
borderRadius={'50%'}
aria-label={''}
/>
{t('app.My Apps')}
{appT('My Apps')}
</Flex>
</Box>
{/* phone tab */}
@@ -193,7 +196,7 @@ export async function getServerSideProps(context: any) {
const currentTab = context?.query?.currentTab || TabEnum.simpleEdit;
return {
props: { currentTab, ...(await serviceSideProps(context)) }
props: { currentTab, ...(await serviceSideProps(context, ['app', 'file', 'publish'])) }
};
}

View File

@@ -1,4 +1,4 @@
import React, { useCallback, useState, useEffect } from 'react';
import React, { useCallback } from 'react';
import { Box, Grid, Flex, IconButton, Button, useDisclosure } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import { useQuery } from '@tanstack/react-query';
@@ -7,7 +7,6 @@ import { delModelById } from '@/web/core/app/api';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import { serviceSideProps } from '@/web/common/utils/i18n';
import { useTranslation } from 'next-i18next';
import MyIcon from '@fastgpt/web/components/common/Icon';
import PageContainer from '@/components/PageContainer';
import Avatar from '@/components/Avatar';
@@ -16,10 +15,12 @@ import CreateModal from './component/CreateModal';
import { useAppStore } from '@/web/core/app/store/useAppStore';
import PermissionIconText from '@/components/support/permission/IconText';
import { useUserStore } from '@/web/support/user/useUserStore';
import { useI18n } from '@/web/context/I18n';
const MyApps = () => {
const { toast } = useToast();
const { t } = useTranslation();
const { appT, commonT } = useI18n();
const router = useRouter();
const { userInfo } = useUserStore();
const { myApps, loadMyApps } = useAppStore();
@@ -62,10 +63,10 @@ const MyApps = () => {
<PageContainer isLoading={isFetching} insertProps={{ px: [5, '48px'] }}>
<Flex pt={[4, '30px']} alignItems={'center'} justifyContent={'space-between'}>
<Box letterSpacing={1} fontSize={['20px', '24px']} color={'myGray.900'}>
{t('app.My Apps')}
{appT('My Apps')}
</Box>
<Button leftIcon={<AddIcon />} variant={'primaryOutline'} onClick={onOpenCreateModal}>
{t('common.New Create')}
{commonT('New Create')}
</Button>
</Flex>
<Grid
@@ -76,7 +77,7 @@ const MyApps = () => {
{myApps.map((app) => (
<MyTooltip
key={app._id}
label={userInfo?.team.canWrite ? t('app.To Settings') : t('app.To Chat')}
label={userInfo?.team.canWrite ? appT('To Settings') : appT('To Chat')}
>
<Box
lineHeight={1.5}
@@ -168,9 +169,6 @@ const MyApps = () => {
</MyTooltip>
))}
</Grid>
{/* (
<ShareBox></ShareBox>
) */}
{myApps.length === 0 && (
<Flex mt={'35vh'} flexDirection={'column'} alignItems={'center'}>
@@ -191,7 +189,7 @@ const MyApps = () => {
export async function getServerSideProps(content: any) {
return {
props: {
...(await serviceSideProps(content))
...(await serviceSideProps(content, ['app']))
}
};
}

View File

@@ -23,6 +23,7 @@ import { useUserStore } from '@/web/support/user/useUserStore';
import { AppListItemType } from '@fastgpt/global/core/app/type';
import { useQuery } from '@tanstack/react-query';
import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
import { useI18n } from '@/web/context/I18n';
type HistoryItemType = {
id: string;
@@ -68,6 +69,8 @@ const ChatHistorySlider = ({
const theme = useTheme();
const router = useRouter();
const { t } = useTranslation();
const { appT } = useI18n();
const { isPc } = useSystemStore();
const { userInfo } = useUserStore();
@@ -86,7 +89,8 @@ const ChatHistorySlider = ({
const concatHistory = useMemo<HistoryItemType[]>(
() =>
!activeChatId
? [{ id: activeChatId, title: t('core.chat.New Chat') }].concat(history)
? //@ts-ignore
[{ id: activeChatId, title: t('core.chat.New Chat') }].concat(history)
: history,
[activeChatId, history, t]
);
@@ -115,7 +119,7 @@ const ChatHistorySlider = ({
whiteSpace={'nowrap'}
>
{isPc && (
<MyTooltip label={canRouteToDetail ? t('app.App Detail') : ''} offset={[0, 0]}>
<MyTooltip label={canRouteToDetail ? appT('App Detail') : ''} offset={[0, 0]}>
<Flex
pt={5}
pb={2}

View File

@@ -371,7 +371,7 @@ export async function getServerSideProps(context: any) {
props: {
appId: context?.query?.appId || '',
chatId: context?.query?.chatId || '',
...(await serviceSideProps(context))
...(await serviceSideProps(context, ['file']))
}
};
}

View File

@@ -431,7 +431,7 @@ export async function getServerSideProps(context: any) {
appName: app?.appId?.name || '',
appAvatar: app?.appId?.avatar || '',
appIntro: app?.appId?.intro || '',
...(await serviceSideProps(context))
...(await serviceSideProps(context, ['file']))
}
};
}

View File

@@ -382,7 +382,7 @@ const OutLink = () => {
export async function getServerSideProps(context: any) {
return {
props: {
...(await serviceSideProps(context))
...(await serviceSideProps(context, ['file']))
}
};
}

View File

@@ -27,9 +27,11 @@ import {
postCreateDatasetTextCollection
} from '@/web/core/dataset/api';
import Tag from '@fastgpt/web/components/common/Tag/index';
import { useI18n } from '@/web/context/I18n';
const Upload = () => {
const { t } = useTranslation();
const { fileT } = useI18n();
const { toast } = useToast();
const router = useRouter();
const { datasetDetail } = useDatasetStore();
@@ -131,7 +133,7 @@ const Upload = () => {
)
);
},
errorToast: t('common.file.Upload failed')
errorToast: fileT('Upload failed')
});
return (

View File

@@ -13,6 +13,7 @@ import { useSystemStore } from '@/web/common/system/useSystemStore';
import { uploadFile2DB } from '@/web/common/file/controller';
import { BucketNameEnum } from '@fastgpt/global/common/file/constants';
import { ImportSourceItemType } from '@/web/core/dataset/type';
import { useI18n } from '@/web/context/I18n';
export type SelectFileItemType = {
fileId: string;
@@ -35,6 +36,8 @@ const FileSelector = ({
onFinishSelect: () => void;
} & FlexProps) => {
const { t } = useTranslation();
const { fileT } = useI18n();
const { toast } = useToast();
const { feConfigs } = useSystemStore();
@@ -129,7 +132,7 @@ const FileSelector = ({
files = files.slice(0, maxCount - selectFiles.length);
toast({
status: 'warning',
title: t('common.file.Some file count exceeds limit', { maxCount })
title: fileT('Some file count exceeds limit', { maxCount })
});
}
// size check
@@ -141,7 +144,7 @@ const FileSelector = ({
if (filterFiles.length < files.length) {
toast({
status: 'warning',
title: t('common.file.Some file size exceeds limit', { maxSize: formatFileSize(maxSize) })
title: fileT('Some file size exceeds limit', { maxSize: formatFileSize(maxSize) })
});
}
@@ -203,7 +206,7 @@ const FileSelector = ({
let isErr = files.some((item) => item.type === '');
if (isErr) {
return toast({
title: t('file.upload error description'),
title: fileT('upload error description'),
status: 'error'
});
}
@@ -262,18 +265,18 @@ const FileSelector = ({
<>
<Box fontWeight={'bold'}>
{isDragging
? t('file.Release the mouse to upload the file')
: t('common.file.Select and drag file tip')}
? fileT('Release the mouse to upload the file')
: fileT('Select and drag file tip')}
</Box>
{/* file type */}
<Box color={'myGray.500'} fontSize={'xs'}>
{t('common.file.Support file type', { fileType })}
{fileT('Support file type', { fileType })}
</Box>
<Box color={'myGray.500'} fontSize={'xs'}>
{/* max count */}
{maxCount && t('common.file.Support max count', { maxCount })}
{maxCount && fileT('Support max count', { maxCount })}
{/* max size */}
{maxSize && t('common.file.Support max size', { maxSize: formatFileSize(maxSize) })}
{maxSize && fileT('Support max size', { maxSize: formatFileSize(maxSize) })}
</Box>
<File

View File

@@ -16,6 +16,7 @@ import MyIcon from '@fastgpt/web/components/common/Icon';
import { useTranslation } from 'next-i18next';
import MyTooltip from '@/components/MyTooltip';
import dynamic from 'next/dynamic';
import { useI18n } from '@/web/context/I18n';
const PreviewRawText = dynamic(() => import('./PreviewRawText'));
@@ -29,6 +30,7 @@ export const RenderUploadFiles = ({
showPreviewContent?: boolean;
}) => {
const { t } = useTranslation();
const { fileT } = useI18n();
const [previewFile, setPreviewFile] = useState<ImportSourceItemType>();
return files.length > 0 ? (
@@ -38,13 +40,13 @@ export const RenderUploadFiles = ({
<Thead draggable={false}>
<Tr bg={'myGray.100'} mb={2}>
<Th borderLeftRadius={'md'} borderBottom={'none'} py={4}>
{t('common.file.File Name')}
{fileT('File Name')}
</Th>
<Th borderBottom={'none'} py={4}>
{t('core.dataset.import.Upload file progress')}
</Th>
<Th borderBottom={'none'} py={4}>
{t('common.file.File Size')}
{fileT('File Size')}
</Th>
<Th borderRightRadius={'md'} borderBottom={'none'} py={4}>
{t('common.Action')}

View File

@@ -295,7 +295,7 @@ export async function getServerSideProps(context: any) {
const datasetId = context?.query?.datasetId;
return {
props: { currentTab, datasetId, ...(await serviceSideProps(context)) }
props: { currentTab, datasetId, ...(await serviceSideProps(context, ['file'])) }
};
}

View File

@@ -137,7 +137,7 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => {
isLoading={requesting}
onClick={handleSubmit(onclickLogin)}
>
{t('home.Login')}
{t('Login')}
</Button>
{feConfigs?.show_register && (

View File

@@ -17,6 +17,7 @@ import {
} from '@/web/core/workflow/utils';
import { useContextSelector } from 'use-context-selector';
import { WorkflowContext, getWorkflowStore } from '@/components/core/workflow/context';
import { useI18n } from '@/web/context/I18n';
const ImportSettings = dynamic(() => import('@/components/core/workflow/Flow/ImportSettings'));
@@ -25,6 +26,8 @@ type Props = { plugin: PluginItemSchema; onClose: () => void };
const Header = ({ plugin, onClose }: Props) => {
const theme = useTheme();
const { t } = useTranslation();
const { appT } = useI18n();
const { toast } = useToast();
const { copyData } = useCopyData();
const edges = useContextSelector(WorkflowContext, (v) => v.edges);
@@ -77,10 +80,10 @@ const Header = ({ plugin, onClose }: Props) => {
null,
2
),
t('app.Export Config Successful')
appT('Export Config Successful')
);
}
}, [copyData, flowData2StoreDataAndCheck, t]);
}, [appT, copyData, flowData2StoreDataAndCheck]);
const Render = useMemo(() => {
return (
@@ -118,9 +121,9 @@ const Header = ({ plugin, onClose }: Props) => {
/>
}
menuList={[
{ label: t('app.Import Configs'), icon: 'common/importLight', onClick: onOpenImport },
{ label: appT('Import Configs'), icon: 'common/importLight', onClick: onOpenImport },
{
label: t('app.Export Configs'),
label: appT('Export Configs'),
icon: 'export',
onClick: onCopy
}
@@ -139,6 +142,7 @@ const Header = ({ plugin, onClose }: Props) => {
</>
);
}, [
appT,
isLoading,
isOpenImport,
onClose,

View File

@@ -13,7 +13,8 @@ export const NextAPI = (...args: NextApiHandler[]): NextApiHandler => {
response = await handler(req, res);
}
if (!res.writableFinished) {
const contentType = res.getHeader('Content-Type');
if ((!contentType || contentType === 'application/json') && !res.writableFinished) {
return jsonRes(res, {
code: 200,
data: response

View File

@@ -1,14 +1,24 @@
import 'i18next';
//import common from '../../i18n/en/common.json';
import common from '../../i18n/zh/common.json';
import dataset from '../../i18n/zh/dataset.json';
import app from '../../i18n/zh/app.json';
import file from '../../i18n/zh/file.json';
import publish from '../../i18n/zh/publish.json';
interface I18nNamespaces {
common: any;
export interface I18nNamespaces {
common: typeof common;
dataset: typeof dataset;
app: typeof app;
file: typeof file;
publish: typeof publish;
}
export type I18nNsType = (keyof I18nNamespaces)[];
declare module 'i18next' {
interface CustomTypeOptions {
returnNull: false;
defaultNs: 'common';
// resources: I18nNamespaces;
resources: I18nNamespaces;
}
}

View File

@@ -2,6 +2,7 @@ import React, { useRef, useCallback } from 'react';
import { Box } from '@chakra-ui/react';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { useTranslation } from 'next-i18next';
import { useI18n } from '@/web/context/I18n';
export const useSelectFile = (props?: {
fileType?: string;
@@ -9,6 +10,7 @@ export const useSelectFile = (props?: {
maxCount?: number;
}) => {
const { t } = useTranslation();
const { fileT } = useI18n();
const { fileType = '*', multiple = false, maxCount = 10 } = props || {};
const { toast } = useToast();
const SelectFileDom = useRef<HTMLInputElement>(null);
@@ -30,7 +32,7 @@ export const useSelectFile = (props?: {
if (fileList.length > maxCount) {
toast({
status: 'warning',
title: t('common.file.Select file amount limit', { max: maxCount })
title: fileT('Select file amount limit', { max: maxCount })
});
fileList = fileList.slice(0, maxCount);
}
@@ -39,7 +41,7 @@ export const useSelectFile = (props?: {
/>
</Box>
),
[fileType, maxCount, multiple, t, toast]
[fileT, fileType, maxCount, multiple, toast]
);
const onOpen = useCallback((sign?: any) => {

View File

@@ -1,3 +1,4 @@
import { I18nNsType } from '@/types/i18n';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
export const LANG_KEY = 'NEXT_LOCALE_LANG';
@@ -16,8 +17,8 @@ export const langMap = {
}
};
export const serviceSideProps = (content: any) => {
return serverSideTranslations(content.locale, undefined, null, content.locales);
export const serviceSideProps = (content: any, ns: I18nNsType = []) => {
return serverSideTranslations(content.locale, ['common', ...ns], null, content.locales);
};
export const getLng = (lng: string) => {

View File

@@ -0,0 +1,44 @@
import { createContext, useContextSelector } from 'use-context-selector';
import { useTranslation } from 'next-i18next';
import { TFunction } from 'i18next';
type I18nContextType = {
commonT: TFunction<['common'], undefined>;
appT: TFunction<['app'], undefined>;
datasetT: TFunction<['dataset'], undefined>;
fileT: TFunction<['file'], undefined>;
publishT: TFunction<['publish'], undefined>;
};
export const I18nContext = createContext<I18nContextType>({
// @ts-ignore
commonT: undefined
});
const I18nContextProvider = ({ children }: { children: React.ReactNode }) => {
const { t: commonT } = useTranslation('common');
const { t: appT } = useTranslation('app');
const { t: datasetT } = useTranslation('dataset');
const { t: fileT } = useTranslation('file');
const { t: publishT } = useTranslation('publish');
return (
<I18nContext.Provider
value={{
commonT,
appT,
datasetT,
fileT,
publishT
}}
>
{children}
</I18nContext.Provider>
);
};
export default I18nContextProvider;
export const useI18n = () => {
return useContextSelector(I18nContext, (ctx) => ctx);
};

View File

@@ -290,7 +290,11 @@ export const v1Workflow2V2 = (
const newInput: FlowNodeInputItemType = {
...input,
selectedTypeIndex: 0,
renderTypeList: inputTypeMap[input.type] ? [inputTypeMap[input.type]] : [],
renderTypeList: !input.type
? [FlowNodeInputTypeEnum.custom]
: inputTypeMap[input.type]
? [inputTypeMap[input.type]]
: [],
key: input.key,
value: input.value,