Commit c614f8b9 by Archer Committed by GitHub

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

* perf: send captcha check

* perf: back router

* perf: i18n init

* perf: ui

* i18n

* perf: ui duration
parent 478386c6
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
"js", "js",
"ts" "ts"
], ],
"i18n-ally.keystyle": "nested", "i18n-ally.keystyle": "flat",
"i18n-ally.sortKeys": true, "i18n-ally.sortKeys": true,
"i18n-ally.keepFulfilled": false, "i18n-ally.keepFulfilled": false,
"i18n-ally.sourceLanguage": "zh", // 根据此语言文件翻译其他语言文件的变量和内容 "i18n-ally.sourceLanguage": "zh", // 根据此语言文件翻译其他语言文件的变量和内容
......
...@@ -56,32 +56,35 @@ curl --location --request POST 'https://{{host}}/api/admin/initv4810' \ ...@@ -56,32 +56,35 @@ curl --location --request POST 'https://{{host}}/api/admin/initv4810' \
7. 新增 - 应用调用迁移成单独节点,同时可以传递全局变量和用户的文件。 7. 新增 - 应用调用迁移成单独节点,同时可以传递全局变量和用户的文件。
8. 新增 - 插件增加使用说明配置。 8. 新增 - 插件增加使用说明配置。
9. 新增 - 工作流导出导入,支持直接导出和导入 JSON 文件,便于交流。 9. 新增 - 工作流导出导入,支持直接导出和导入 JSON 文件,便于交流。
10. 商业版新增 - 飞书机器人接入 10. 新增 - HTTP模块支持超时配置、支持更多的 Body 类型,params 和 headers 支持新的变量选择模式。
11. 商业版新增 - 公众号接入接入 11. 新增 - 发送验证码安全校验。
12. 商业版新增 - 自助开票申请 12. 商业版新增 - 飞书机器人接入
13. 商业版新增 - SSO 定制 13. 商业版新增 - 公众号接入接入
14. 优化 - 工作流循环校验,避免 skip 循环空转。同时支持分支完全并发执行。 14. 商业版新增 - 自助开票申请
15. 优化 - SSE 响应优化。 15. 商业版新增 - SSO 定制
16. 优化 - 无 SSL 证书情况下,优化复制。 16. 优化 - 工作流循环校验,避免 skip 循环空转。同时支持分支完全并发执行。
17. 优化 - 单选框打开后自动滚动到选中的位置。 17. 优化 - SSE 响应优化。
18. 优化 - 知识库集合禁用,目录禁用会递归修改其下所有 children 的禁用状态。 18. 优化 - 无 SSL 证书情况下,优化复制。
19. 优化 - 节点选择,避免切换 tab 时候,path 加载报错。 19. 优化 - 单选框打开后自动滚动到选中的位置。
20. 优化 - 最新 React Markdown 组件,支持 Base64 图片。 20. 优化 - 知识库集合禁用,目录禁用会递归修改其下所有 children 的禁用状态。
21. 优化 - 知识库列表 UI。 21. 优化 - 节点选择,避免切换 tab 时候,path 加载报错。
22. 优化 - 知识库详情页 UI。 22. 优化 - 最新 React Markdown 组件,支持 Base64 图片。
23. 优化 - 支持无网络配置情况下运行。 23. 优化 - 知识库列表 UI。
24. 优化 - 部分全局变量,增加数据类型约束。 24. 优化 - 知识库详情页 UI。
25. 优化 - 查看工作流详情,切换 tab 时,自动滚动到顶部。 25. 优化 - 支持无网络配置情况下运行。
26. 修复 - 全局变量 key 可能重复。 26. 优化 - 部分全局变量,增加数据类型约束。
27. 修复 - Prompt 模式调用工具,stream=false 模式下,会携带 0: 开头标记。 27. 优化 - 查看工作流详情,切换 tab 时,自动滚动到顶部。
28. 修复 - 对话日志鉴权问题:仅为 APP 管理员的用户,无法查看对话日志详情。 28. 优化 - 对话框性能问题。
29. 修复 - 选择 Milvus 部署时,无法导出知识库。 29. 修复 - 全局变量 key 可能重复。
30. 修复 - 创建 APP 副本,无法复制系统配置。 30. 修复 - Prompt 模式调用工具,stream=false 模式下,会携带 0: 开头标记。
31. 修复 - 图片识别模式下,自动解析图片链接正则不够严谨问题。 31. 修复 - 对话日志鉴权问题:仅为 APP 管理员的用户,无法查看对话日志详情。
32. 修复 - 内容提取的数据类型与输出数据类型未一致。 32. 修复 - 选择 Milvus 部署时,无法导出知识库。
33. 修复 - 工作流运行时间统计错误。 33. 修复 - 创建 APP 副本,无法复制系统配置。
34. 修复 - stream 模式下,工具调用有可能出现 undefined 34. 修复 - 图片识别模式下,自动解析图片链接正则不够严谨问题。
35. 修复 - 全局变量在 API 中无法持久化。 35. 修复 - 内容提取的数据类型与输出数据类型未一致。
36. 修复 - OpenAPI,detail=false模式下,不应该返回 tool 调用结果,仅返回文字。(可解决 cow 不适配问题) 36. 修复 - 工作流运行时间统计错误。
37. 修复 - 知识库标签重复加载。 37. 修复 - stream 模式下,工具调用有可能出现 undefined
38. 修复 - Debug 模式下,循环调用边问题。 38. 修复 - 全局变量在 API 中无法持久化。
39. 修复 - OpenAPI,detail=false模式下,不应该返回 tool 调用结果,仅返回文字。(可解决 cow 不适配问题)
40. 修复 - 知识库标签重复加载。
41. 修复 - Debug 模式下,循环调用边问题。
...@@ -48,7 +48,7 @@ export class PgVectorCtrl { ...@@ -48,7 +48,7 @@ export class PgVectorCtrl {
const { teamId, datasetId, collectionId, vector, retry = 3 } = props; const { teamId, datasetId, collectionId, vector, retry = 3 } = props;
try { try {
const { rows } = await PgClient.insert(DatasetVectorTableName, { const { rowCount, rows } = await PgClient.insert(DatasetVectorTableName, {
values: [ values: [
[ [
{ key: 'vector', value: `[${vector}]` }, { key: 'vector', value: `[${vector}]` },
...@@ -58,6 +58,11 @@ export class PgVectorCtrl { ...@@ -58,6 +58,11 @@ export class PgVectorCtrl {
] ]
] ]
}); });
if (rowCount === 0) {
return Promise.reject('insertDatasetData: no insert');
}
return { return {
insertId: rows[0].id insertId: rows[0].id
}; };
......
import Cookies, { CookieAttributes } from 'js-cookie';
import { useTranslation } from 'next-i18next';
const setCookie = (key: string, value: string, options?: CookieAttributes) => {
Cookies.set(key, value, options);
};
const getCookie = (key: string) => {
return Cookies.get(key);
};
const LANG_KEY = 'NEXT_LOCALE';
export const useI18nLng = () => {
const { i18n } = useTranslation();
const onChangeLng = (lng: string) => {
setCookie(LANG_KEY, lng, {
expires: 30,
sameSite: 'None',
secure: true
});
i18n?.changeLanguage(lng);
};
const setUserDefaultLng = () => {
if (!navigator || !localStorage) return;
if (getCookie(LANG_KEY)) return onChangeLng(getCookie(LANG_KEY) as string);
const languageMap = {
zh: 'zh',
'zh-CN': 'zh'
};
// @ts-ignore
const lang = languageMap[navigator.language] || 'en';
// currentLng not in userLang
return onChangeLng(lang);
};
return {
onChangeLng,
setUserDefaultLng
};
};
...@@ -15,8 +15,7 @@ ...@@ -15,8 +15,7 @@
"version_initial_copy": "Duplicate - Original State", "version_initial_copy": "Duplicate - Original State",
"version_name_tips": "Version name cannot be empty", "version_name_tips": "Version name cannot be empty",
"version_past": "Previously Published", "version_past": "Previously Published",
"version_publish_tips": "This version will be saved to the team cloud, synchronized with the entire team, and update the app version on all release channels.", "version_publish_tips": "This version will be saved to the team cloud, synchronized with the entire team, and update the app version on all release channels."
"version_save_tips": "This version will be saved to the team cloud, synchronized with the entire team."
}, },
"app_detail": "Application Details", "app_detail": "Application Details",
"chat_debug": "Chat Preview", "chat_debug": "Chat Preview",
...@@ -138,13 +137,11 @@ ...@@ -138,13 +137,11 @@
"Create plugin bot": "Create Plugin", "Create plugin bot": "Create Plugin",
"Create simple bot": "Create Simple App", "Create simple bot": "Create Simple App",
"Create simple bot tip": "Create a simple AI app by filling out a form, suitable for beginners.", "Create simple bot tip": "Create a simple AI app by filling out a form, suitable for beginners.",
"Create template tip": "Explore more features in the template market to help you understand and get started with various applications.",
"Create workflow bot": "Create Workflow", "Create workflow bot": "Create Workflow",
"Create workflow tip": "Build complex multi-turn dialogue AI applications through low-code methods, recommended for advanced users.", "Create workflow tip": "Build complex multi-turn dialogue AI applications through low-code methods, recommended for advanced users.",
"Http plugin": "HTTP Plugin", "Http plugin": "HTTP Plugin",
"Plugin": "Plugin", "Plugin": "Plugin",
"Simple bot": "Simple App", "Simple bot": "Simple App",
"Template": "Create via Template",
"Workflow bot": "Workflow" "Workflow bot": "Workflow"
}, },
"upload_file_max_amount": "Maximum File Quantity", "upload_file_max_amount": "Maximum File Quantity",
...@@ -169,8 +166,6 @@ ...@@ -169,8 +166,6 @@
"workflow": { "workflow": {
"Input guide": "Input Guide", "Input guide": "Input Guide",
"file_url": "Document Link", "file_url": "Document Link",
"option1": "Option 1",
"option2": "Option 2",
"read_files": "Document Parsing", "read_files": "Document Parsing",
"read_files_result": "Document Parsing Result", "read_files_result": "Document Parsing Result",
"read_files_result_desc": "Original document text, consisting of file names and document content, separated by hyphens between multiple files.", "read_files_result_desc": "Original document text, consisting of file names and document content, separated by hyphens between multiple files.",
......
...@@ -24,12 +24,10 @@ ...@@ -24,12 +24,10 @@
"items": "Items", "items": "Items",
"module_runtime_and": "Total Module Runtime", "module_runtime_and": "Total Module Runtime",
"multiple_AI_conversations": "Multiple AI Conversations", "multiple_AI_conversations": "Multiple AI Conversations",
"new_chat": "New Conversation",
"new_input_guide_lexicon": "New Lexicon", "new_input_guide_lexicon": "New Lexicon",
"no_workflow_response": "No workflow data", "no_workflow_response": "No workflow data",
"plugins_output": "Plugin Output", "plugins_output": "Plugin Output",
"question_tip": "From top to bottom, the response order of each module", "question_tip": "From top to bottom, the response order of each module",
"rearrangement": "Rearrangement of Retrieval Results",
"response": { "response": {
"node_inputs": "Node Inputs" "node_inputs": "Node Inputs"
}, },
......
{ {
"Disabled": "Disabled",
"Enable": "Enable", "Enable": "Enable",
"Enabled": "Enabled",
"collection": { "collection": {
"Create update time": "Creation/Update Time", "Create update time": "Creation/Update Time",
"Training type": "Training Mode" "Training type": "Training Mode"
......
{
"Login": "Login",
"forget_password": "Find password",
"login_failed": "Login failed",
"login_success": "Login successful",
"password_condition": "Password maximum 60 characters",
"policy_tip": "By useing, you agree to our",
"privacy": "Privacy policy",
"register": "Register",
"root_password_placeholder": "The root user password is the value of the environment variable DEFAULT_ROOT_PSW",
"terms": "Terms",
"use_root_login": "Log in as root user"
}
\ No newline at end of file
...@@ -4,20 +4,17 @@ ...@@ -4,20 +4,17 @@
"copy_link_hint": "Copy the link below to the specified location", "copy_link_hint": "Copy the link below to the specified location",
"create_api_key": "Create New Key", "create_api_key": "Create New Key",
"create_link": "Create Link", "create_link": "Create Link",
"default_response": "Default Response",
"edit_api_key": "Edit Key Details", "edit_api_key": "Edit Key Details",
"edit_feishu_bot": "Edit Feishu Bot", "edit_feishu_bot": "Edit Feishu Bot",
"edit_link": "Edit", "edit_link": "Edit",
"feishu_api": "Feishu API", "feishu_api": "Feishu API",
"feishu_bot": "Feishu Bot", "feishu_bot": "Feishu Bot",
"feishu_bot_desc": "Connect to Feishu Bot directly via API", "feishu_bot_desc": "Connect to Feishu Bot directly via API",
"feishu_name": "Feishu",
"key_alias": "Key alias, for display only", "key_alias": "Key alias, for display only",
"key_tips": "You can use the API key to access specific interfaces (cannot access the application, use the in-app API key for that)", "key_tips": "You can use the API key to access specific interfaces (cannot access the application, use the in-app API key for that)",
"link_name": "Share Link Name", "link_name": "Share Link Name",
"new_feishu_bot": "Add New Feishu Bot", "new_feishu_bot": "Add New Feishu Bot",
"official_account": { "official_account": {
"api": "WeChat Official Account API",
"create_modal_title": "Create WeChat Official Account Integration", "create_modal_title": "Create WeChat Official Account Integration",
"desc": "Connect to WeChat Official Account directly via API", "desc": "Connect to WeChat Official Account directly via API",
"edit_modal_title": "Edit WeChat Official Account Integration", "edit_modal_title": "Edit WeChat Official Account Integration",
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
"bill": { "bill": {
"balance": "Balance", "balance": "Balance",
"buy_plan": "Purchase Plan", "buy_plan": "Purchase Plan",
"buy_standard_plan_success": "Plan Purchase Successful",
"contact_customer_service": "Contact Support", "contact_customer_service": "Contact Support",
"conversion": "Conversion", "conversion": "Conversion",
"convert_error": "Conversion Failed", "convert_error": "Conversion Failed",
...@@ -20,6 +19,7 @@ ...@@ -20,6 +19,7 @@
"you_can_convert": "You can convert", "you_can_convert": "You can convert",
"yuan": "Yuan" "yuan": "Yuan"
}, },
"bill_and_invoices": "Bill & Invoice",
"bind_inform_account_error": "Failed to Bind Notification Account", "bind_inform_account_error": "Failed to Bind Notification Account",
"bind_inform_account_success": "Notification Account Bound Successfully", "bind_inform_account_success": "Notification Account Bound Successfully",
"delete": { "delete": {
...@@ -27,28 +27,24 @@ ...@@ -27,28 +27,24 @@
"admin_success": "Admin Deleted Successfully" "admin_success": "Admin Deleted Successfully"
}, },
"has_chosen": "Selected", "has_chosen": "Selected",
"individuation": "Individuation",
"login": { "login": {
"error": "Login Error", "error": "Login Error",
"failed": "Login Failed",
"login_account": "Login to {{account}} Account",
"login_error": "Incorrect Username or Password",
"password_condition": "Password can be up to 60 characters", "password_condition": "Password can be up to 60 characters",
"success": "Login Successful", "success": "Login Successful"
"to_register": "No account? Register"
}, },
"name": "Name", "name": "Name",
"notice": "Notice",
"notification": { "notification": {
"Bind Notification Pipe Hint": "Please bind a notification receiving account to ensure you receive notifications such as plan expiration reminders, ensuring your service runs smoothly.", "Bind Notification Pipe Hint": "Please bind a notification receiving account to ensure you receive notifications such as plan expiration reminders, ensuring your service runs smoothly.",
"remind_owner_bind": "Please remind the creator to bind a notification account" "remind_owner_bind": "Please remind the creator to bind a notification account"
}, },
"operations": "Actions", "operations": "Actions",
"password": { "password": {
"change_error": "Password Change Error",
"code_required": "Verification Code Required", "code_required": "Verification Code Required",
"code_send_error": "Failed to Send Verification Code", "code_send_error": "Failed to Send Verification Code",
"code_sended": "Verification Code Sent", "code_sended": "Verification Code Sent",
"confirm": "Confirm Password", "confirm": "Confirm Password",
"email_phone": "Email/Phone Number",
"email_phone_error": "Invalid Email/Phone Number Format", "email_phone_error": "Invalid Email/Phone Number Format",
"email_phone_void": "Email/Phone Number Cannot Be Empty", "email_phone_void": "Email/Phone Number Cannot Be Empty",
"get_code": "Get Verification Code", "get_code": "Get Verification Code",
...@@ -80,19 +76,17 @@ ...@@ -80,19 +76,17 @@
"write": "In addition to readable resources, you can also create new resources" "write": "In addition to readable resources, you can also create new resources"
}, },
"permissions": "Permissions", "permissions": "Permissions",
"promotion": { "personal_information": "Me",
"pay": "Friend Payment", "personalization": "Personalization",
"register": "Friend Registration" "promotion_records": "Promotion",
},
"register": { "register": {
"confirm": "Confirm Registration", "confirm": "Confirm Registration",
"error": "Registration Error",
"failed": "Registration Failed",
"register_account": "Register {{account}} Account", "register_account": "Register {{account}} Account",
"success": "Registration Successful", "success": "Registration Successful",
"to_login": "Already have an account? Login" "to_login": "Already have an account? Login"
}, },
"search_user": "Search Username", "search_user": "Search Username",
"sign_out": "Sign out",
"synchronization": { "synchronization": {
"button": "Sync Now", "button": "Sync Now",
"placeholder": "Enter Sync Tag", "placeholder": "Enter Sync Tag",
...@@ -109,5 +103,6 @@ ...@@ -109,5 +103,6 @@
"official_account": "Official Account", "official_account": "Official Account",
"share": "Share Link", "share": "Share Link",
"wecom": "WeCom" "wecom": "WeCom"
} },
"usage_record": "Usages"
} }
\ No newline at end of file
{ {
" i18nT('workflow": {
"use_user_id'),\n required": {
"workflow": {
"use_user_id": ""
}
}
},
"Code": "Code", "Code": "Code",
"about_xxx_question": "Question regarding xxx", "about_xxx_question": "Question regarding xxx",
"add_new_input": "Add New Input", "add_new_input": "Add New Input",
...@@ -122,15 +115,12 @@ ...@@ -122,15 +115,12 @@
"Custom outputs": "Custom Outputs", "Custom outputs": "Custom Outputs",
"Error": "Error", "Error": "Error",
"Read file result": "Read File Result", "Read file result": "Read File Result",
"User_select_description": "Description",
"User_select_result": "Selected Result",
"read files": "Read Files" "read files": "Read Files"
}, },
"select_an_application": "Select an Application", "select_an_application": "Select an Application",
"select_another_application_to_call": "You can choose another application to call", "select_another_application_to_call": "You can choose another application to call",
"special_array_format": "Special array format, returns an empty array when the search result is empty.", "special_array_format": "Special array format, returns an empty array when the search result is empty.",
"start_with": "Starts With", "start_with": "Starts With",
"system_variables": "system variables",
"target_fields_description": "A target field consists of 'description' and 'key'. Multiple target fields can be extracted.", "target_fields_description": "A target field consists of 'description' and 'key'. Multiple target fields can be extracted.",
"template": { "template": {
"ai_chat": "AI Chat", "ai_chat": "AI Chat",
...@@ -156,9 +146,7 @@ ...@@ -156,9 +146,7 @@
"variable_picker_tips": "Type node name or variable name to search", "variable_picker_tips": "Type node name or variable name to search",
"variable_update": "Variable Update", "variable_update": "Variable Update",
"workflow": { "workflow": {
"Back_to_current_version": "Back to Current Version",
"My edit": "My Edit", "My edit": "My Edit",
"Switch_failed": "Switch Failed",
"Switch_success": "Switch Successful", "Switch_success": "Switch Successful",
"Team cloud": "Team Cloud", "Team cloud": "Team Cloud",
"exit_tips": "Your changes have not been saved. 'Exit directly' will not save your edits." "exit_tips": "Your changes have not been saved. 'Exit directly' will not save your edits."
......
...@@ -15,8 +15,7 @@ ...@@ -15,8 +15,7 @@
"version_initial_copy": "副本-初始状态", "version_initial_copy": "副本-初始状态",
"version_name_tips": "版本名称不能为空", "version_name_tips": "版本名称不能为空",
"version_past": "发布过", "version_past": "发布过",
"version_publish_tips": "该版本将被保存至团队云端,同步给整个团队,同时更新所有发布渠道的应用版本", "version_publish_tips": "该版本将被保存至团队云端,同步给整个团队,同时更新所有发布渠道的应用版本"
"version_save_tips": "该版本将被保存至团队云端,同步给整个团队"
}, },
"app_detail": "应用详情", "app_detail": "应用详情",
"chat_debug": "调试预览", "chat_debug": "调试预览",
...@@ -131,13 +130,11 @@ ...@@ -131,13 +130,11 @@
"Create plugin bot": "创建插件", "Create plugin bot": "创建插件",
"Create simple bot": "创建简易应用", "Create simple bot": "创建简易应用",
"Create simple bot tip": "通过填表单形式,创建简单的 AI 应用,适合新手", "Create simple bot tip": "通过填表单形式,创建简单的 AI 应用,适合新手",
"Create template tip": "在模板市场探索更多玩法,带你理解并上手各种应用",
"Create workflow bot": "创建工作流", "Create workflow bot": "创建工作流",
"Create workflow tip": "通过低代码的方式,构建逻辑复杂的多轮对话 AI 应用,推荐高级玩家使用", "Create workflow tip": "通过低代码的方式,构建逻辑复杂的多轮对话 AI 应用,推荐高级玩家使用",
"Http plugin": "HTTP 插件", "Http plugin": "HTTP 插件",
"Plugin": "插件", "Plugin": "插件",
"Simple bot": "简易应用", "Simple bot": "简易应用",
"Template": "通过模板创建",
"Workflow bot": "工作流" "Workflow bot": "工作流"
}, },
"upload_file_max_amount": "最大文件数量", "upload_file_max_amount": "最大文件数量",
...@@ -162,8 +159,6 @@ ...@@ -162,8 +159,6 @@
"workflow": { "workflow": {
"Input guide": "填写说明", "Input guide": "填写说明",
"file_url": "文档链接", "file_url": "文档链接",
"option1": "选项 1",
"option2": "选项 2",
"read_files": "文档解析", "read_files": "文档解析",
"read_files_result": "文档解析结果", "read_files_result": "文档解析结果",
"read_files_result_desc": "文档原文,由文件名和文档内容组成,多个文件之间通过横线隔开。", "read_files_result_desc": "文档原文,由文件名和文档内容组成,多个文件之间通过横线隔开。",
......
...@@ -24,12 +24,10 @@ ...@@ -24,12 +24,10 @@
"items": "条", "items": "条",
"module_runtime_and": "模块运行时间和", "module_runtime_and": "模块运行时间和",
"multiple_AI_conversations": "多组 AI 对话", "multiple_AI_conversations": "多组 AI 对话",
"new_chat": "新对话",
"new_input_guide_lexicon": "新词库", "new_input_guide_lexicon": "新词库",
"no_workflow_response": "没有运行数据", "no_workflow_response": "没有运行数据",
"plugins_output": "插件输出", "plugins_output": "插件输出",
"question_tip": "从上到下,为各个模块的响应顺序", "question_tip": "从上到下,为各个模块的响应顺序",
"rearrangement": "检索结果重排",
"response": { "response": {
"node_inputs": "节点输入" "node_inputs": "节点输入"
}, },
......
{ {
"Disabled": "已禁用",
"Enable": "启用", "Enable": "启用",
"Enabled": "已启用",
"collection": { "collection": {
"Create update time": "创建/更新时间", "Create update time": "创建/更新时间",
"Training type": "训练模式" "Training type": "训练模式"
......
{
"Login": "登录",
"forget_password": "忘记密码?",
"login_failed": "登录异常",
"login_success": "登录成功",
"password_condition": "密码最多 60 位",
"policy_tip": "使用即代表你同意我们的",
"privacy": "隐私协议",
"register": "注册账号",
"root_password_placeholder": "root 用户密码为环境变量 DEFAULT_ROOT_PSW 的值",
"terms": "服务协议",
"use_root_login": "使用 root 用户登录"
}
\ No newline at end of file
...@@ -4,20 +4,17 @@ ...@@ -4,20 +4,17 @@
"copy_link_hint": "将下面链接复制到指定位置", "copy_link_hint": "将下面链接复制到指定位置",
"create_api_key": "创建新 key", "create_api_key": "创建新 key",
"create_link": "创建链接", "create_link": "创建链接",
"default_response": "默认回复",
"edit_api_key": "编辑 key 信息", "edit_api_key": "编辑 key 信息",
"edit_feishu_bot": "编辑飞书机器人", "edit_feishu_bot": "编辑飞书机器人",
"edit_link": "编辑", "edit_link": "编辑",
"feishu_api": "飞书接口", "feishu_api": "飞书接口",
"feishu_bot": "飞书机器人", "feishu_bot": "飞书机器人",
"feishu_bot_desc": "通过 API 直接接入飞书机器人", "feishu_bot_desc": "通过 API 直接接入飞书机器人",
"feishu_name": "飞书",
"key_alias": "key 的别名,仅用于展示", "key_alias": "key 的别名,仅用于展示",
"key_tips": "你可以使用 API 密钥访问一些特定的接口(无法访问应用,访问应用需使用应用内的 API key)", "key_tips": "你可以使用 API 密钥访问一些特定的接口(无法访问应用,访问应用需使用应用内的 API key)",
"link_name": "分享链接的名字", "link_name": "分享链接的名字",
"new_feishu_bot": "新增飞书机器人", "new_feishu_bot": "新增飞书机器人",
"official_account": { "official_account": {
"api": "微信公众号 API",
"create_modal_title": "创建微信公众号接入", "create_modal_title": "创建微信公众号接入",
"desc": "通过 API 直接接入微信公众号", "desc": "通过 API 直接接入微信公众号",
"edit_modal_title": "编辑微信公众号接入", "edit_modal_title": "编辑微信公众号接入",
......
...@@ -2,13 +2,14 @@ ...@@ -2,13 +2,14 @@
"bill": { "bill": {
"balance": "余额", "balance": "余额",
"buy_plan": "购买套餐", "buy_plan": "购买套餐",
"buy_standard_plan_success": "购买套餐成功",
"contact_customer_service": "联系客服", "contact_customer_service": "联系客服",
"conversion": "兑换", "conversion": "兑换",
"convert_error": "兑换失败", "convert_error": "兑换失败",
"convert_success": "兑换成功", "convert_success": "兑换成功",
"current_token_price": "当前积分价格",
"not_need_invoice": "余额支付,无法开票", "not_need_invoice": "余额支付,无法开票",
"price": "价格", "price": "价格",
"renew_plan": "续费套餐",
"standard_valid_tip": "套餐使用规则:系统优先使用更高级的套餐,原未用完的套餐将延后生效", "standard_valid_tip": "套餐使用规则:系统优先使用更高级的套餐,原未用完的套餐将延后生效",
"token_expire_1year": "积分有效期一年", "token_expire_1year": "积分有效期一年",
"tokens": "积分", "tokens": "积分",
...@@ -16,14 +17,9 @@ ...@@ -16,14 +17,9 @@
"use_balance_hint": "由于系统升级,原“自动续费从余额扣款”模式取消,余额充值入口关闭。您的余额可用于购买积分", "use_balance_hint": "由于系统升级,原“自动续费从余额扣款”模式取消,余额充值入口关闭。您的余额可用于购买积分",
"valid_time": "生效时间", "valid_time": "生效时间",
"you_can_convert": "您可兑换", "you_can_convert": "您可兑换",
"renew_plan": "续费套餐",
"current_token_price": "当前积分价格",
"yuan": "元" "yuan": "元"
}, },
"promotion": { "bill_and_invoices": "账单 & 发票",
"register": "好友注册",
"pay": "好友充值"
},
"bind_inform_account_error": "绑定通知账号异常", "bind_inform_account_error": "绑定通知账号异常",
"bind_inform_account_success": "绑定通知账号成功", "bind_inform_account_success": "绑定通知账号成功",
"delete": { "delete": {
...@@ -31,28 +27,24 @@ ...@@ -31,28 +27,24 @@
"admin_success": "删除管理员成功" "admin_success": "删除管理员成功"
}, },
"has_chosen": "已选择", "has_chosen": "已选择",
"individuation": "个性化",
"login": { "login": {
"error": "登录异常", "error": "登录异常",
"failed": "登录失败",
"login_account": "登录 {{account}} 账号",
"login_error": "用户名或密码错误",
"password_condition": "密码最多 60 位", "password_condition": "密码最多 60 位",
"success": "登录成功", "success": "登录成功"
"to_register": "没有账号,去注册"
}, },
"name": "名称", "name": "名称",
"notice": "通知",
"notification": { "notification": {
"Bind Notification Pipe Hint": "请绑定通知接收账号,以确保您能正常接收套餐过期提醒等通知,保障您的服务正常运行。", "Bind Notification Pipe Hint": "请绑定通知接收账号,以确保您能正常接收套餐过期提醒等通知,保障您的服务正常运行。",
"remind_owner_bind": "请提醒创建者绑定通知账号" "remind_owner_bind": "请提醒创建者绑定通知账号"
}, },
"operations": "操作", "operations": "操作",
"password": { "password": {
"change_error": "修改密码异常",
"code_required": "验证码不能为空", "code_required": "验证码不能为空",
"code_send_error": "验证码发送异常", "code_send_error": "验证码发送异常",
"code_sended": "验证码已发送", "code_sended": "验证码已发送",
"confirm": "确认密码", "confirm": "确认密码",
"email_phone": "邮箱/手机号",
"email_phone_error": "邮箱/手机号格式错误", "email_phone_error": "邮箱/手机号格式错误",
"email_phone_void": "邮箱/手机号不能为空", "email_phone_void": "邮箱/手机号不能为空",
"get_code": "获取验证码", "get_code": "获取验证码",
...@@ -78,16 +70,23 @@ ...@@ -78,16 +70,23 @@
"team_read": "团队可访问", "team_read": "团队可访问",
"team_write": "团队可编辑" "team_write": "团队可编辑"
}, },
"permission_des": {
"manage": "可创建资源、邀请、删除成员",
"read": "成员仅可阅读相关资源,无法新建资源",
"write": "除了可读资源外,还可以新建新的资源"
},
"permissions": "权限", "permissions": "权限",
"personal_information": "个人信息",
"personalization": "个性化",
"promotion_records": "推广记录",
"register": { "register": {
"confirm": "确认注册", "confirm": "确认注册",
"error": "注册异常",
"failed": "注册失败",
"register_account": "注册 {{account}} 账号", "register_account": "注册 {{account}} 账号",
"success": "注册成功", "success": "注册成功",
"to_login": "已有账号,去登录" "to_login": "已有账号,去登录"
}, },
"search_user": "搜索用户名", "search_user": "搜索用户名",
"sign_out": "登出",
"synchronization": { "synchronization": {
"button": "立即同步", "button": "立即同步",
"placeholder": "请输入同步标签", "placeholder": "请输入同步标签",
...@@ -105,9 +104,5 @@ ...@@ -105,9 +104,5 @@
"share": "分享链接", "share": "分享链接",
"wecom": "企业微信" "wecom": "企业微信"
}, },
"permission_des": { "usage_record": "使用记录"
"read": "成员仅可阅读相关资源,无法新建资源",
"write": "除了可读资源外,还可以新建新的资源",
"manage": "可创建资源、邀请、删除成员"
}
} }
\ No newline at end of file
{ {
" i18nT('workflow": {
"use_user_id'),\n required": {
"workflow": {
"use_user_id": "使用者 ID"
}
}
},
"Code": "代码", "Code": "代码",
"about_xxx_question": "关于 xxx 的问题", "about_xxx_question": "关于 xxx 的问题",
"add_new_input": "新增输入", "add_new_input": "新增输入",
...@@ -122,15 +115,12 @@ ...@@ -122,15 +115,12 @@
"Custom outputs": "自定义输出", "Custom outputs": "自定义输出",
"Error": "错误信息", "Error": "错误信息",
"Read file result": "文档解析结果预览", "Read file result": "文档解析结果预览",
"User_select_description": "说明文字",
"User_select_result": "选择的结果",
"read files": "解析的文档" "read files": "解析的文档"
}, },
"select_an_application": "选择一个应用", "select_an_application": "选择一个应用",
"select_another_application_to_call": "可以选择一个其他应用进行调用", "select_another_application_to_call": "可以选择一个其他应用进行调用",
"special_array_format": "特殊数组格式,搜索结果为空时,返回空数组。", "special_array_format": "特殊数组格式,搜索结果为空时,返回空数组。",
"start_with": "开始为", "start_with": "开始为",
"system_variables": "系统变量",
"target_fields_description": "由 '描述' 和 'key' 组成一个目标字段,可提取多个目标字段", "target_fields_description": "由 '描述' 和 'key' 组成一个目标字段,可提取多个目标字段",
"template": { "template": {
"ai_chat": "AI 对话", "ai_chat": "AI 对话",
...@@ -156,9 +146,7 @@ ...@@ -156,9 +146,7 @@
"variable_picker_tips": "可输入节点名或变量名搜索", "variable_picker_tips": "可输入节点名或变量名搜索",
"variable_update": "变量更新", "variable_update": "变量更新",
"workflow": { "workflow": {
"Back_to_current_version": "回到初始状态",
"My edit": "我的编辑", "My edit": "我的编辑",
"Switch_failed": "切换失败",
"Switch_success": "切换成功", "Switch_success": "切换成功",
"Team cloud": "团队云端", "Team cloud": "团队云端",
"exit_tips": "您的更改尚未保存,「直接退出」将不会保存您的编辑记录。" "exit_tips": "您的更改尚未保存,「直接退出」将不会保存您的编辑记录。"
......
...@@ -21,21 +21,23 @@ ...@@ -21,21 +21,23 @@
"ahooks": "^3.7.11", "ahooks": "^3.7.11",
"date-fns": "2.30.0", "date-fns": "2.30.0",
"dayjs": "^1.11.7", "dayjs": "^1.11.7",
"i18next": "23.11.5",
"js-cookie": "^3.0.5",
"lexical": "0.12.6", "lexical": "0.12.6",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"i18next": "23.11.5",
"next-i18next": "15.3.0", "next-i18next": "15.3.0",
"react-i18next": "14.1.2",
"papaparse": "^5.4.1", "papaparse": "^5.4.1",
"react": "18.3.1", "react": "18.3.1",
"react-beautiful-dnd": "^13.1.1", "react-beautiful-dnd": "^13.1.1",
"react-day-picker": "^8.7.1", "react-day-picker": "^8.7.1",
"react-dom": "18.3.1", "react-dom": "18.3.1",
"react-hook-form": "7.43.1", "react-hook-form": "7.43.1",
"react-i18next": "14.1.2",
"react-photo-view": "^1.2.6", "react-photo-view": "^1.2.6",
"use-context-selector": "^1.4.4" "use-context-selector": "^1.4.4"
}, },
"devDependencies": { "devDependencies": {
"@types/js-cookie": "^3.0.5",
"@types/lodash": "^4.14.191", "@types/lodash": "^4.14.191",
"@types/papaparse": "^5.3.7", "@types/papaparse": "^5.3.7",
"@types/react": "18.3.1", "@types/react": "18.3.1",
......
...@@ -7,6 +7,7 @@ import publish from '../i18n/zh/publish.json'; ...@@ -7,6 +7,7 @@ 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';
import login from '../i18n/zh/login.json';
export interface I18nNamespaces { export interface I18nNamespaces {
common: typeof common; common: typeof common;
...@@ -17,18 +18,11 @@ export interface I18nNamespaces { ...@@ -17,18 +18,11 @@ export interface I18nNamespaces {
workflow: typeof workflow; workflow: typeof workflow;
user: typeof user; user: typeof user;
chat: typeof chat; chat: typeof chat;
login: typeof login;
} }
export type I18nNsType = (keyof I18nNamespaces)[]; export type I18nNsType = (keyof I18nNamespaces)[];
export type I18nCommonKey = NestedKeyOf<I18nNamespaces['common']>;
export type I18nDataSetKey = NestedKeyOf<I18nNamespaces['dataset']>;
export type I18nAppKey = NestedKeyOf<I18nNamespaces['app']>;
export type I18nPublishKey = NestedKeyOf<I18nNamespaces['publish']>;
export type I18nWorkflowKey = NestedKeyOf<I18nNamespaces['workflow']>;
export type I18nUserKey = NestedKeyOf<I18nNamespaces['user']>;
export type I18nChatKey = NestedKeyOf<I18nNamespaces['chat']>;
export type NestedKeyOf<ObjectType extends object> = { export type NestedKeyOf<ObjectType extends object> = {
[Key in keyof ObjectType & (string | number)]: ObjectType[Key] extends object [Key in keyof ObjectType & (string | number)]: ObjectType[Key] extends object
? `${Key}` | `${Key}.${NestedKeyOf<ObjectType[Key]>}` ? `${Key}` | `${Key}.${NestedKeyOf<ObjectType[Key]>}`
...@@ -46,7 +40,7 @@ export type I18nKeyFunction = { ...@@ -46,7 +40,7 @@ export type I18nKeyFunction = {
declare module 'i18next' { declare module 'i18next' {
interface CustomTypeOptions { interface CustomTypeOptions {
returnNull: false; returnNull: false;
defaultNS: ['common', 'dataset', 'app', 'file', 'publish', 'workflow', 'user', 'chat']; defaultNS: ['common', 'dataset', 'app', 'file', 'publish', 'workflow', 'user', 'chat', 'login'];
resources: I18nNamespaces; resources: I18nNamespaces;
} }
} }
...@@ -22,7 +22,7 @@ importers: ...@@ -22,7 +22,7 @@ importers:
version: 13.3.0 version: 13.3.0
next-i18next: next-i18next:
specifier: 15.3.0 specifier: 15.3.0
version: 15.3.0(i18next@23.11.5)(next@14.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8))(react-i18next@14.1.2(i18next@23.11.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) version: 15.3.0(i18next@23.11.5)(next@14.2.5(@babel/core@7.24.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8))(react-i18next@14.1.2(i18next@23.11.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)
prettier: prettier:
specifier: 3.2.4 specifier: 3.2.4
version: 3.2.4 version: 3.2.4
...@@ -250,7 +250,7 @@ importers: ...@@ -250,7 +250,7 @@ importers:
version: 2.1.1(@chakra-ui/system@2.6.1(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(react@18.3.1))(react@18.3.1) version: 2.1.1(@chakra-ui/system@2.6.1(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(react@18.3.1))(react@18.3.1)
'@chakra-ui/next-js': '@chakra-ui/next-js':
specifier: 2.1.5 specifier: 2.1.5
version: 2.1.5(@chakra-ui/react@2.8.1(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(framer-motion@9.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(next@14.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8))(react@18.3.1) version: 2.1.5(@chakra-ui/react@2.8.1(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(framer-motion@9.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(next@14.2.5(@babel/core@7.24.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8))(react@18.3.1)
'@chakra-ui/react': '@chakra-ui/react':
specifier: 2.8.1 specifier: 2.8.1
version: 2.8.1(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(framer-motion@9.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) version: 2.8.1(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(framer-motion@9.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
...@@ -302,6 +302,9 @@ importers: ...@@ -302,6 +302,9 @@ importers:
i18next: i18next:
specifier: 23.11.5 specifier: 23.11.5
version: 23.11.5 version: 23.11.5
js-cookie:
specifier: ^3.0.5
version: 3.0.5
lexical: lexical:
specifier: 0.12.6 specifier: 0.12.6
version: 0.12.6 version: 0.12.6
...@@ -310,7 +313,7 @@ importers: ...@@ -310,7 +313,7 @@ importers:
version: 4.17.21 version: 4.17.21
next-i18next: next-i18next:
specifier: 15.3.0 specifier: 15.3.0
version: 15.3.0(i18next@23.11.5)(next@14.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8))(react-i18next@14.1.2(i18next@23.11.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) version: 15.3.0(i18next@23.11.5)(next@14.2.5(@babel/core@7.24.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8))(react-i18next@14.1.2(i18next@23.11.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)
papaparse: papaparse:
specifier: ^5.4.1 specifier: ^5.4.1
version: 5.4.1 version: 5.4.1
...@@ -339,6 +342,9 @@ importers: ...@@ -339,6 +342,9 @@ importers:
specifier: ^1.4.4 specifier: ^1.4.4
version: 1.4.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2) version: 1.4.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)
devDependencies: devDependencies:
'@types/js-cookie':
specifier: ^3.0.5
version: 3.0.6
'@types/lodash': '@types/lodash':
specifier: ^4.14.191 specifier: ^4.14.191
version: 4.17.7 version: 4.17.7
...@@ -368,7 +374,7 @@ importers: ...@@ -368,7 +374,7 @@ importers:
version: 2.1.1(@chakra-ui/system@2.6.1(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(react@18.3.1))(react@18.3.1) version: 2.1.1(@chakra-ui/system@2.6.1(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(react@18.3.1))(react@18.3.1)
'@chakra-ui/next-js': '@chakra-ui/next-js':
specifier: 2.1.5 specifier: 2.1.5
version: 2.1.5(@chakra-ui/react@2.8.1(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(framer-motion@9.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(next@14.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8))(react@18.3.1) version: 2.1.5(@chakra-ui/react@2.8.1(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(framer-motion@9.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(next@14.2.5(@babel/core@7.24.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8))(react@18.3.1)
'@chakra-ui/react': '@chakra-ui/react':
specifier: 2.8.1 specifier: 2.8.1
version: 2.8.1(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(framer-motion@9.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) version: 2.8.1(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(framer-motion@9.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
...@@ -464,7 +470,7 @@ importers: ...@@ -464,7 +470,7 @@ importers:
version: 14.2.5(@babel/core@7.24.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8) version: 14.2.5(@babel/core@7.24.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8)
next-i18next: next-i18next:
specifier: 15.3.0 specifier: 15.3.0
version: 15.3.0(i18next@23.11.5)(next@14.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8))(react-i18next@14.1.2(i18next@23.11.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) version: 15.3.0(i18next@23.11.5)(next@14.2.5(@babel/core@7.24.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8))(react-i18next@14.1.2(i18next@23.11.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)
nextjs-node-loader: nextjs-node-loader:
specifier: ^1.1.5 specifier: ^1.1.5
version: 1.1.5(webpack@5.92.1) version: 1.1.5(webpack@5.92.1)
...@@ -529,9 +535,6 @@ importers: ...@@ -529,9 +535,6 @@ importers:
'@types/formidable': '@types/formidable':
specifier: ^2.0.5 specifier: ^2.0.5
version: 2.0.6 version: 2.0.6
'@types/js-cookie':
specifier: ^3.0.3
version: 3.0.6
'@types/js-yaml': '@types/js-yaml':
specifier: ^4.0.9 specifier: ^4.0.9
version: 4.0.9 version: 4.0.9
...@@ -6015,6 +6018,10 @@ packages: ...@@ -6015,6 +6018,10 @@ packages:
js-cookie@2.2.1: js-cookie@2.2.1:
resolution: {integrity: sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==} resolution: {integrity: sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==}
js-cookie@3.0.5:
resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==}
engines: {node: '>=14'}
js-tokens@4.0.0: js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
...@@ -10024,7 +10031,7 @@ snapshots: ...@@ -10024,7 +10031,7 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- '@types/react' - '@types/react'
'@chakra-ui/next-js@2.1.5(@chakra-ui/react@2.8.1(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(framer-motion@9.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(next@14.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8))(react@18.3.1)': '@chakra-ui/next-js@2.1.5(@chakra-ui/react@2.8.1(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(framer-motion@9.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(next@14.2.5(@babel/core@7.24.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8))(react@18.3.1)':
dependencies: dependencies:
'@chakra-ui/react': 2.8.1(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(framer-motion@9.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@chakra-ui/react': 2.8.1(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(framer-motion@9.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@emotion/cache': 11.11.0 '@emotion/cache': 11.11.0
...@@ -15529,6 +15536,8 @@ snapshots: ...@@ -15529,6 +15536,8 @@ snapshots:
js-cookie@2.2.1: {} js-cookie@2.2.1: {}
js-cookie@3.0.5: {}
js-tokens@4.0.0: {} js-tokens@4.0.0: {}
js-tokens@9.0.0: {} js-tokens@9.0.0: {}
...@@ -16625,7 +16634,7 @@ snapshots: ...@@ -16625,7 +16634,7 @@ snapshots:
neo-async@2.6.2: {} neo-async@2.6.2: {}
next-i18next@15.3.0(i18next@23.11.5)(next@14.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8))(react-i18next@14.1.2(i18next@23.11.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1): next-i18next@15.3.0(i18next@23.11.5)(next@14.2.5(@babel/core@7.24.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8))(react-i18next@14.1.2(i18next@23.11.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1):
dependencies: dependencies:
'@babel/runtime': 7.24.8 '@babel/runtime': 7.24.8
'@types/hoist-non-react-statics': 3.3.5 '@types/hoist-non-react-statics': 3.3.5
......
...@@ -5,11 +5,11 @@ ...@@ -5,11 +5,11 @@
module.exports = { module.exports = {
i18n: { i18n: {
defaultLocale: 'zh', defaultLocale: 'en',
locales: ['en', 'zh'], locales: ['en', 'zh'],
localeDetection: false localeDetection: false
}, },
localePath: localePath:
typeof window === 'undefined' ? require('path').resolve('../../packages/web/i18n') : '/i18n', typeof window === 'undefined' ? require('path').resolve('../../packages/web/i18n') : '/i18n',
reloadOnPrerender: process.env.NODE_ENV === 'development' reloadOnPrerender: process.env.NODE_ENV === 'development'
} };
...@@ -68,7 +68,6 @@ ...@@ -68,7 +68,6 @@
"devDependencies": { "devDependencies": {
"@svgr/webpack": "^6.5.1", "@svgr/webpack": "^6.5.1",
"@types/formidable": "^2.0.5", "@types/formidable": "^2.0.5",
"@types/js-cookie": "^3.0.3",
"@types/js-yaml": "^4.0.9", "@types/js-yaml": "^4.0.9",
"@types/jsonwebtoken": "^9.0.3", "@types/jsonwebtoken": "^9.0.3",
"@types/lodash": "^4.14.191", "@types/lodash": "^4.14.191",
......
...@@ -7,9 +7,11 @@ import { useQuery } from '@tanstack/react-query'; ...@@ -7,9 +7,11 @@ import { useQuery } from '@tanstack/react-query';
import { useUserStore } from '@/web/support/user/useUserStore'; import { useUserStore } from '@/web/support/user/useUserStore';
import { getUnreadCount } from '@/web/support/user/inform/api'; import { getUnreadCount } from '@/web/support/user/inform/api';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import { useI18nLng } from '@fastgpt/web/hooks/useI18n';
import Auth from './auth'; import Auth from './auth';
import { useSystem } from '@fastgpt/web/hooks/useSystem'; import { useSystem } from '@fastgpt/web/hooks/useSystem';
import { useMount } from 'ahooks';
const Navbar = dynamic(() => import('./navbar')); const Navbar = dynamic(() => import('./navbar'));
const NavbarPhone = dynamic(() => import('./navbarPhone')); const NavbarPhone = dynamic(() => import('./navbarPhone'));
const UpdateInviteModal = dynamic(() => import('@/components/support/user/team/UpdateInviteModal')); const UpdateInviteModal = dynamic(() => import('@/components/support/user/team/UpdateInviteModal'));
...@@ -46,6 +48,7 @@ const Layout = ({ children }: { children: JSX.Element }) => { ...@@ -46,6 +48,7 @@ const Layout = ({ children }: { children: JSX.Element }) => {
const { loading, feConfigs, isNotSufficientModal } = useSystemStore(); const { loading, feConfigs, isNotSufficientModal } = useSystemStore();
const { isPc } = useSystem(); const { isPc } = useSystem();
const { userInfo } = useUserStore(); const { userInfo } = useUserStore();
const { setUserDefaultLng } = useI18nLng();
const isChatPage = useMemo( const isChatPage = useMemo(
() => router.pathname === '/chat' && Object.values(router.query).join('').length !== 0, () => router.pathname === '/chat' && Object.values(router.query).join('').length !== 0,
...@@ -61,6 +64,10 @@ const Layout = ({ children }: { children: JSX.Element }) => { ...@@ -61,6 +64,10 @@ const Layout = ({ children }: { children: JSX.Element }) => {
const isHideNavbar = !!pcUnShowLayoutRoute[router.pathname]; const isHideNavbar = !!pcUnShowLayoutRoute[router.pathname];
useMount(() => {
setUserDefaultLng();
});
return ( return (
<> <>
<Box h={'100%'} bg={'myGray.100'}> <Box h={'100%'} bg={'myGray.100'}>
......
...@@ -37,7 +37,7 @@ const SendCodeAuthModal = ({ ...@@ -37,7 +37,7 @@ const SendCodeAuthModal = ({
<Skeleton <Skeleton
minH="200px" minH="200px"
isLoaded={!loading} isLoaded={!loading}
fadeDuration={2} fadeDuration={1}
display={'flex'} display={'flex'}
justifyContent={'center'} justifyContent={'center'}
my={1} my={1}
......
...@@ -15,6 +15,7 @@ import NextHead from '@/components/common/NextHead'; ...@@ -15,6 +15,7 @@ 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(); const { t } = useTranslation();
return ( return (
<> <>
<NextHead <NextHead
......
...@@ -7,8 +7,10 @@ import { UserType } from '@fastgpt/global/support/user/type'; ...@@ -7,8 +7,10 @@ import { UserType } from '@fastgpt/global/support/user/type';
import { useToast } from '@fastgpt/web/hooks/useToast'; import { useToast } from '@fastgpt/web/hooks/useToast';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { UserUpdateParams } from '@/types/user'; import { UserUpdateParams } from '@/types/user';
import { langMap, setLngStore } from '@/web/common/utils/i18n'; import { langMap } from '@/web/common/utils/i18n';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { useI18nLng } from '@fastgpt/web/hooks/useI18n';
import MySelect from '@fastgpt/web/components/common/MySelect'; import MySelect from '@fastgpt/web/components/common/MySelect';
import TimezoneSelect from '@fastgpt/web/components/common/MySelect/TimezoneSelect'; import TimezoneSelect from '@fastgpt/web/components/common/MySelect/TimezoneSelect';
...@@ -16,7 +18,7 @@ const Individuation = () => { ...@@ -16,7 +18,7 @@ const Individuation = () => {
const { t, i18n } = useTranslation(); const { t, i18n } = useTranslation();
const { userInfo, updateUserInfo } = useUserStore(); const { userInfo, updateUserInfo } = useUserStore();
const { toast } = useToast(); const { toast } = useToast();
const router = useRouter(); const { onChangeLng } = useI18nLng();
const { reset } = useForm<UserUpdateParams>({ const { reset } = useForm<UserUpdateParams>({
defaultValues: userInfo as UserType defaultValues: userInfo as UserType
...@@ -55,14 +57,7 @@ const Individuation = () => { ...@@ -55,14 +57,7 @@ const Individuation = () => {
}))} }))}
onchange={(val: any) => { onchange={(val: any) => {
const lang = val; const lang = val;
setLngStore(lang); onChangeLng(lang);
router.replace(
{
query: router.query
},
router.asPath,
{ locale: lang }
);
}} }}
/> />
</Box> </Box>
......
...@@ -40,14 +40,14 @@ const Account = ({ currentTab }: { currentTab: TabEnum }) => { ...@@ -40,14 +40,14 @@ const Account = ({ currentTab }: { currentTab: TabEnum }) => {
const tabList = [ const tabList = [
{ {
icon: 'support/user/userLight', icon: 'support/user/userLight',
label: t('common:user.Personal Information'), label: t('user:personal_information'),
value: TabEnum.info value: TabEnum.info
}, },
...(feConfigs?.isPlus ...(feConfigs?.isPlus
? [ ? [
{ {
icon: 'support/usage/usageRecordLight', icon: 'support/usage/usageRecordLight',
label: t('common:user.Usage Record'), label: t('user:usage_record'),
value: TabEnum.usage value: TabEnum.usage
} }
] ]
...@@ -57,7 +57,7 @@ const Account = ({ currentTab }: { currentTab: TabEnum }) => { ...@@ -57,7 +57,7 @@ const Account = ({ currentTab }: { currentTab: TabEnum }) => {
? [ ? [
{ {
icon: 'support/bill/payRecordLight', icon: 'support/bill/payRecordLight',
label: t('common:support.wallet.Bills'), label: t('user:bill_and_invoices'),
value: TabEnum.bill value: TabEnum.bill
} }
] ]
...@@ -67,7 +67,7 @@ const Account = ({ currentTab }: { currentTab: TabEnum }) => { ...@@ -67,7 +67,7 @@ const Account = ({ currentTab }: { currentTab: TabEnum }) => {
? [ ? [
{ {
icon: 'support/account/promotionLight', icon: 'support/account/promotionLight',
label: t('common:user.Promotion Record'), label: t('user:promotion_records'),
value: TabEnum.promotion value: TabEnum.promotion
} }
] ]
...@@ -83,14 +83,14 @@ const Account = ({ currentTab }: { currentTab: TabEnum }) => { ...@@ -83,14 +83,14 @@ const Account = ({ currentTab }: { currentTab: TabEnum }) => {
: []), : []),
{ {
icon: 'support/user/individuation', icon: 'support/user/individuation',
label: t('common:support.account.Individuation'), label: t('user:personalization'),
value: TabEnum.individuation value: TabEnum.individuation
}, },
...(feConfigs.isPlus ...(feConfigs.isPlus
? [ ? [
{ {
icon: 'support/user/informLight', icon: 'support/user/informLight',
label: t('common:user.Notice'), label: t('user:notice'),
value: TabEnum.inform value: TabEnum.inform
} }
] ]
...@@ -98,7 +98,7 @@ const Account = ({ currentTab }: { currentTab: TabEnum }) => { ...@@ -98,7 +98,7 @@ const Account = ({ currentTab }: { currentTab: TabEnum }) => {
{ {
icon: 'support/account/loginoutLight', icon: 'support/account/loginoutLight',
label: t('common:user.Sign Out'), label: t('user:sign_out'),
value: TabEnum.loginout value: TabEnum.loginout
} }
]; ];
......
...@@ -125,9 +125,14 @@ const Header = () => { ...@@ -125,9 +125,14 @@ const Header = () => {
try { try {
localStorage.removeItem(`${appDetail._id}-past`); localStorage.removeItem(`${appDetail._id}-past`);
localStorage.removeItem(`${appDetail._id}-future`); localStorage.removeItem(`${appDetail._id}-future`);
router.back(); router.push({
pathname: '/app/list',
query: {
parentId: appDetail.parentId
}
});
} catch (error) {} } catch (error) {}
}, [appDetail._id, router]); }, [appDetail._id, appDetail.parentId, router]);
const Render = useMemo(() => { const Render = useMemo(() => {
return ( return (
......
...@@ -125,9 +125,14 @@ const Header = () => { ...@@ -125,9 +125,14 @@ const Header = () => {
try { try {
localStorage.removeItem(`${appDetail._id}-past`); localStorage.removeItem(`${appDetail._id}-past`);
localStorage.removeItem(`${appDetail._id}-future`); localStorage.removeItem(`${appDetail._id}-future`);
router.back(); router.push({
pathname: '/app/list',
query: {
parentId: appDetail.parentId
}
});
} catch (error) {} } catch (error) {}
}, [appDetail._id, router]); }, [appDetail._id, appDetail.parentId, router]);
const Render = useMemo(() => { const Render = useMemo(() => {
return ( return (
......
...@@ -272,7 +272,7 @@ function List() { ...@@ -272,7 +272,7 @@ function List() {
{owner && ( {owner && (
<HStack spacing={1}> <HStack spacing={1}>
<Avatar src={owner.avatar} w={'0.875rem'} borderRadius={'50%'} /> <Avatar src={owner.avatar} w={'0.875rem'} borderRadius={'50%'} />
<Box maxW={'150px'} className="textEllipsis"> <Box maxW={'150px'} className="textEllipsis" fontSize={'mini'}>
{owner.memberName} {owner.memberName}
</Box> </Box>
</HStack> </HStack>
...@@ -288,7 +288,9 @@ function List() { ...@@ -288,7 +288,9 @@ function List() {
{isPc && ( {isPc && (
<HStack spacing={1} className="time"> <HStack spacing={1} className="time">
<MyIcon name={'history'} w={'0.85rem'} color={'myGray.400'} /> <MyIcon name={'history'} w={'0.85rem'} color={'myGray.400'} />
<Box color={'myGray.500'}>{formatTimeToChatTime(dataset.updateTime)}</Box> <Box color={'myGray.500'} fontSize={'mini'}>
{formatTimeToChatTime(dataset.updateTime)}
</Box>
</HStack> </HStack>
)} )}
{dataset.permission.hasWritePer && ( {dataset.permission.hasWritePer && (
......
...@@ -43,12 +43,12 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => { ...@@ -43,12 +43,12 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => {
}) })
); );
toast({ toast({
title: t('user:login.success'), title: t('login:login_success'),
status: 'success' status: 'success'
}); });
} catch (error: any) { } catch (error: any) {
toast({ toast({
title: error.message || t('user:login.error'), title: error.message || t('login:login_failed'),
status: 'error' status: 'error'
}); });
} }
...@@ -61,7 +61,7 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => { ...@@ -61,7 +61,7 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => {
const placeholder = (() => { const placeholder = (() => {
if (isCommunityVersion) { if (isCommunityVersion) {
return t('common:support.user.login.Root login'); return t('login:use_root_login');
} }
return [t('common:support.user.login.Username')] return [t('common:support.user.login.Username')]
.concat( .concat(
...@@ -102,36 +102,36 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => { ...@@ -102,36 +102,36 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => {
type={'password'} type={'password'}
placeholder={ placeholder={
isCommunityVersion isCommunityVersion
? t('common:support.user.login.Root password placeholder') ? t('login:root_password_placeholder')
: t('common:support.user.login.Password') : t('common:support.user.login.Password')
} }
{...register('password', { {...register('password', {
required: true, required: true,
maxLength: { maxLength: {
value: 60, value: 60,
message: t('user:login.password_condition') message: t('login:password_condition')
} }
})} })}
></Input> ></Input>
</FormControl> </FormControl>
{feConfigs?.docUrl && ( {feConfigs?.docUrl && (
<Flex alignItems={'center'} mt={7} fontSize={'sm'}> <Flex alignItems={'center'} mt={7} fontSize={'mini'}>
{t('common:support.user.login.Policy tip')} {t('login:policy_tip')}
<Link <Link
ml={1} ml={1}
href={getDocPath('/docs/agreement/terms/')} href={getDocPath('/docs/agreement/terms/')}
target={'_blank'} target={'_blank'}
color={'primary.500'} color={'primary.500'}
> >
{t('common:support.user.login.Terms')} {t('login:terms')}
</Link> </Link>
<Box mx={1}>{t('common:support.user.login.And')}</Box> <Box mx={1}>&</Box>
<Link <Link
href={getDocPath('/docs/agreement/privacy/')} href={getDocPath('/docs/agreement/privacy/')}
target={'_blank'} target={'_blank'}
color={'primary.500'} color={'primary.500'}
> >
{t('common:support.user.login.Privacy')} {t('login:privacy')}
</Link> </Link>
</Flex> </Flex>
)} )}
...@@ -145,7 +145,7 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => { ...@@ -145,7 +145,7 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => {
isLoading={requesting} isLoading={requesting}
onClick={handleSubmit(onclickLogin)} onClick={handleSubmit(onclickLogin)}
> >
{t('common:Login')} {t('login:Login')}
</Button> </Button>
<Flex align={'center'} justifyContent={'flex-end'} color={'primary.700'}> <Flex align={'center'} justifyContent={'flex-end'} color={'primary.700'}>
...@@ -156,7 +156,7 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => { ...@@ -156,7 +156,7 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => {
onClick={() => setPageType('forgetPassword')} onClick={() => setPageType('forgetPassword')}
fontSize="sm" fontSize="sm"
> >
{t('common:support.user.login.Forget Password')} {t('login:forget_password')}
</Box> </Box>
)} )}
{feConfigs?.register_method && feConfigs.register_method.length > 0 && ( {feConfigs?.register_method && feConfigs.register_method.length > 0 && (
...@@ -168,7 +168,7 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => { ...@@ -168,7 +168,7 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => {
onClick={() => setPageType('register')} onClick={() => setPageType('register')}
fontSize="sm" fontSize="sm"
> >
{t('common:support.user.login.Register')} {t('login:register')}
</Box> </Box>
</> </>
)} )}
......
...@@ -129,7 +129,7 @@ const Login = () => { ...@@ -129,7 +129,7 @@ const Login = () => {
export async function getServerSideProps(context: any) { export async function getServerSideProps(context: any) {
return { return {
props: { ...(await serviceSideProps(context, ['app', 'user'])) } props: { ...(await serviceSideProps(context, ['app', 'user', 'login'])) }
}; };
} }
......
import { I18nNsType } from '@fastgpt/web/types/i18next'; import { I18nNsType } from '@fastgpt/web/types/i18next';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
export const LANG_KEY = 'NEXT_LOCALE_LANG';
export enum LangEnum { export enum LangEnum {
'zh' = 'zh', 'zh' = 'zh',
'en' = 'en' 'en' = 'en'
...@@ -20,24 +19,3 @@ export const langMap = { ...@@ -20,24 +19,3 @@ export const langMap = {
export const serviceSideProps = (content: any, ns: I18nNsType = []) => { export const serviceSideProps = (content: any, ns: I18nNsType = []) => {
return serverSideTranslations(content.locale, ['common', 'error', ...ns], null, content.locales); return serverSideTranslations(content.locale, ['common', 'error', ...ns], null, content.locales);
}; };
export const getLng = (lng: string) => {
return lng.split('-')[0];
};
export const change2DefaultLng = (currentLng: string) => {
if (!navigator || !localStorage) return;
if (localStorage.getItem(LANG_KEY)) return;
const userLang = navigator.language;
if (userLang.includes(currentLng)) {
return;
}
// currentLng not in userLang
return getLng(userLang);
};
export const setLngStore = (lng: string) => {
if (!localStorage) return;
localStorage.setItem(LANG_KEY, lng);
};
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { clientInitData } from '@/web/common/system/staticData'; import { clientInitData } from '@/web/common/system/staticData';
import { useTranslation } from 'next-i18next';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { useSystemStore } from '@/web/common/system/useSystemStore'; import { useSystemStore } from '@/web/common/system/useSystemStore';
import type { FastGPTFeConfigsType } from '@fastgpt/global/common/system/types/index.d'; import type { FastGPTFeConfigsType } from '@fastgpt/global/common/system/types/index.d';
import { change2DefaultLng, LangEnum, setLngStore } from '@/web/common/utils/i18n';
import { useMemoizedFn, useMount } from 'ahooks'; import { useMemoizedFn, useMount } from 'ahooks';
import { TrackEventName } from '../common/system/constants'; import { TrackEventName } from '../common/system/constants';
export const useInitApp = () => { export const useInitApp = () => {
const router = useRouter(); const router = useRouter();
const { hiId } = router.query as { hiId?: string }; const { hiId } = router.query as { hiId?: string };
const { i18n } = useTranslation();
const { loadGitStar, setInitd, feConfigs } = useSystemStore(); const { loadGitStar, setInitd, feConfigs } = useSystemStore();
const [scripts, setScripts] = useState<FastGPTFeConfigsType['scripts']>([]); const [scripts, setScripts] = useState<FastGPTFeConfigsType['scripts']>([]);
const [title, setTitle] = useState(process.env.SYSTEM_NAME || 'AI'); const [title, setTitle] = useState(process.env.SYSTEM_NAME || 'AI');
...@@ -38,20 +35,8 @@ export const useInitApp = () => { ...@@ -38,20 +35,8 @@ export const useInitApp = () => {
setInitd(); setInitd();
}); });
const initUserLanguage = useMemoizedFn(() => {
// get default language
const targetLng =
change2DefaultLng(i18n.language) ||
(['zh', 'zh-CN'].includes(navigator.language) ? 'zh' : 'en');
setLngStore(targetLng);
router.replace(router.asPath, undefined, { locale: targetLng });
});
useMount(() => { useMount(() => {
initFetch(); initFetch();
initUserLanguage();
const errorTrack = (event: ErrorEvent) => { const errorTrack = (event: ErrorEvent) => {
window.umami?.track(TrackEventName.windowError, { window.umami?.track(TrackEventName.windowError, {
......
const fs = require('fs').promises
const path = require('path')
// 配置项
const CONFIG = {
i18nDirectory: path.join(__dirname, '../../packages/web/i18n'),
sourceDirectories: ['../../packages', '../../projects/app'].map(dir => path.join(__dirname, dir)),
fileExtensions: ['.ts', '.tsx', '.js', '.jsx'],
ignoreDirectories: ['node_modules', '.next', 'public', 'i18n']
}
// 从文件中加载 JSON
const loadJSON = async (filePath) => {
try {
const data = await fs.readFile(filePath, 'utf8')
return JSON.parse(data)
} catch (error) {
console.error(`读取 JSON 文件 ${filePath} 时出错:`, error)
return null
}
}
// 递归提取 JSON 对象的所有键
const extractKeysFromJSON = (obj, parentKey = '') => {
let keys = []
for (const [key, value] of Object.entries(obj)) {
const fullKey = parentKey ? `${parentKey}.${key}` : key
if (typeof value === 'object' && value !== null) {
keys = keys.concat(extractKeysFromJSON(value, fullKey))
} else {
keys.push(fullKey)
}
}
return keys
}
// 递归获取文件夹中的所有 JSON 文件
const getAllJSONFiles = async (dir) => {
let results = []
const list = await fs.readdir(dir)
await Promise.all(list.map(async (file) => {
const filePath = path.join(dir, file)
const stat = await fs.stat(filePath)
if (stat.isDirectory()) {
if (!CONFIG.ignoreDirectories.includes(file)) {
results = results.concat(await getAllJSONFiles(filePath))
}
} else if (filePath.endsWith('.json')) {
results.push(filePath)
}
}))
return results
}
// 提取文件夹中所有 JSON 文件的键
const extractKeysFromDirectory = async (dir) => {
let allKeys = new Set()
const subDirs = await fs.readdir(dir)
await Promise.all(subDirs.map(async (subDir) => {
const subDirPath = path.join(dir, subDir)
const stat = await fs.stat(subDirPath)
if (stat.isDirectory() && !CONFIG.ignoreDirectories.includes(subDir)) {
const files = await getAllJSONFiles(subDirPath)
await Promise.all(files.map(async (file) => {
const jsonObject = await loadJSON(file)
if (jsonObject) {
const keys = extractKeysFromJSON(jsonObject)
keys.forEach(key => allKeys.add(key))
}
}))
}
}))
return Array.from(allKeys)
}
// 检查键是否在内容中使用
const isKeyUsedInContent = (content, key) => {
const regex = new RegExp(`\\b${key}\\b`, 'g')
return regex.test(content)
}
// 递归在文件夹中搜索键
const searchKeysInFiles = async (dir, keys, usedKeys) => {
const list = await fs.readdir(dir)
await Promise.all(list.map(async (file) => {
const filePath = path.join(dir, file)
const stat = await fs.stat(filePath)
if (stat.isDirectory()) {
if (!CONFIG.ignoreDirectories.includes(file)) {
await searchKeysInFiles(filePath, keys, usedKeys)
}
} else if (CONFIG.fileExtensions.includes(path.extname(file))) {
const data = await fs.readFile(filePath, 'utf8')
keys.forEach(key => {
if (isKeyUsedInContent(data, key)) {
usedKeys.add(key)
}
})
}
}))
}
// 从扁平键路径恢复嵌套对象
const restoreNestedStructure = (keys) => {
const result = {}
keys.forEach(key => {
const parts = key.split('.')
let current = result
parts.forEach((part, index) => {
if (!current[part]) {
current[part] = index === parts.length - 1 ? null : {}
}
current = current[part]
})
})
return result
}
// 递归删除未使用的键
const removeUnusedKeys = (target, unusedKeys) => {
for (const [key, value] of Object.entries(unusedKeys)) {
if (value === null) {
delete target[key]
} else if (typeof target[key] === 'object' && target[key] !== null) {
removeUnusedKeys(target[key], value)
if (Object.keys(target[key]).length === 0) {
delete target[key]
}
}
}
}
// 处理 JSON 文件
const processJSONFile = async (filePath, unusedKeys) => {
try {
const jsonObject = await loadJSON(filePath)
if (jsonObject) {
removeUnusedKeys(jsonObject, unusedKeys)
await fs.writeFile(filePath, JSON.stringify(jsonObject, null, 2), 'utf8')
console.log(`已处理文件 ${filePath}`)
}
} catch (error) {
console.error(`处理文件 ${filePath} 时出错:`, error)
}
}
// 递归处理目录中的所有 JSON 文件
const processJSONFilesInDirectory = async (dir, unusedKeys) => {
const list = await fs.readdir(dir)
await Promise.all(list.map(async (file) => {
const filePath = path.join(dir, file)
const stat = await fs.stat(filePath)
if (stat.isDirectory() && !CONFIG.ignoreDirectories.includes(file)) {
await processJSONFilesInDirectory(filePath, unusedKeys)
} else if (stat.isFile() && file.endsWith('.json')) {
await processJSONFile(filePath, unusedKeys)
}
}))
}
// 将 keys 写入 JSON 文件
const writeKeysToFile = async (filePath, keys) => {
try {
await fs.writeFile(filePath, JSON.stringify(keys, null, 2), 'utf8')
console.log(`已将 keys 写入文件 ${filePath}`)
} catch (error) {
console.error(`写入文件 ${filePath} 时出错:`, error)
}
}
// 主函数
const main = async () => {
const allKeys = await extractKeysFromDirectory(CONFIG.i18nDirectory)
// await writeKeysToFile(path.join(__dirname, 'allKeys.json'), allKeys)
const usedKeys = new Set()
await Promise.all(CONFIG.sourceDirectories.map(dir => searchKeysInFiles(dir, allKeys, usedKeys)))
const unusedKeys = allKeys.filter(key => !usedKeys.has(key))
// await writeKeysToFile(path.join(__dirname, 'unusedKeys.json'), unusedKeys)
console.log(unusedKeys)
const nestedUnusedKeys = restoreNestedStructure(unusedKeys)
await processJSONFilesInDirectory(CONFIG.i18nDirectory, nestedUnusedKeys)
}
main().catch(err => console.error('执行过程中出错:', err))
\ No newline at end of file
{
"name": "i18n",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@phenomnomnominal/tsquery": "^6.1.3"
}
}
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
Object.defineProperty(exports, "__esModule", { value: true });
var tsquery_1 = require("@phenomnomnominal/tsquery");
var path = require("path");
var fs = require("fs");
//
var root = path.join(__dirname, '../../');
// get all files in the project recursively
function getAllFiles(dirPath, arrayOfFiles) {
if (arrayOfFiles === void 0) { arrayOfFiles = []; }
var files = fs.readdirSync(dirPath);
files.forEach(function (file) {
var filePath = path.join(dirPath, file);
if (fs.statSync(filePath).isDirectory()) {
arrayOfFiles = getAllFiles(filePath, arrayOfFiles);
}
else {
arrayOfFiles.push(filePath);
}
});
return arrayOfFiles;
}
var allFiles = getAllFiles(root).filter(function (file) { return file.endsWith('.ts') || file.endsWith('.tsx'); })
.filter(function (file) { return !file.includes('node_modules'); })
.filter(function (file) { return !file.includes('jieba'); });
function processFiles(allFiles) {
return __awaiter(this, void 0, void 0, function () {
var fileContents, error_1;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
_a.trys.push([0, 2, , 3]);
return [4 /*yield*/, Promise.all(allFiles.map(function (file) { return fs.readFileSync(file, 'utf-8'); }))];
case 1:
fileContents = _a.sent();
// 处理每个文件的内容
fileContents.forEach(function (content, index) {
var astTree = (0, tsquery_1.ast)(content);
var res = (0, tsquery_1.query)(astTree, 'JsxText,StringLiteral');
for (var _i = 0, res_1 = res; _i < res_1.length; _i++) {
var node = res_1[_i];
var text = node.getText().trim();
if (text.length > 0 && text.match(/[\u4e00-\u9fa5]/g)) {
console.log(allFiles[index], text);
}
}
});
return [3 /*break*/, 3];
case 2:
error_1 = _a.sent();
console.error('Error processing files:', error_1);
return [3 /*break*/, 3];
case 3: return [2 /*return*/];
}
});
});
}
processFiles(allFiles);
import { ast, query } from '@phenomnomnominal/tsquery';
import * as path from 'path';
import * as fs from 'fs';
//
const root = path.join(__dirname, '../../');
// get all files in the project recursively
function getAllFiles(dirPath: string, arrayOfFiles: string[] = []): string[] {
const files = fs.readdirSync(dirPath);
files.forEach((file) => {
const filePath = path.join(dirPath, file);
if (fs.statSync(filePath).isDirectory()) {
arrayOfFiles = getAllFiles(filePath, arrayOfFiles);
} else {
arrayOfFiles.push(filePath);
}
});
return arrayOfFiles;
}
const allFiles = getAllFiles(root)
.filter((file) => file.endsWith('.ts') || file.endsWith('.tsx'))
.filter((file) => !file.includes('node_modules'))
.filter((file) => !file.includes('jieba'));
async function processFiles(allFiles: string[]) {
try {
// 并行读取所有文件内容
const fileContents = await Promise.all(allFiles.map((file) => fs.readFileSync(file, 'utf-8')));
// 处理每个文件的内容
fileContents.forEach((content, index) => {
const astTree = ast(content);
const res = query(astTree, 'JsxText,StringLiteral');
for (const node of res) {
const text = node.getText().trim();
if (text.length > 0 && text.match(/[\u4e00-\u9fa5]/g)) {
console.log(allFiles[index], text);
}
}
});
} catch (error) {
console.error('Error processing files:', error);
}
}
processFiles(allFiles);
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