Explorar o código

Feat/portuguese support (#2075)

crazywoola hai 1 ano
pai
achega
c17baef172
Modificáronse 78 ficheiros con 2847 adicións e 374 borrados
  1. 1 22
      api/commands.py
  2. 326 0
      api/constants/languages.py
  3. 0 255
      api/constants/model_template.py
  4. 1 1
      api/controllers/console/admin.py
  5. 3 2
      api/controllers/console/app/app.py
  6. 1 1
      api/controllers/console/app/site.py
  7. 2 1
      api/controllers/console/auth/activate.py
  8. 5 4
      api/controllers/console/auth/oauth.py
  9. 2 1
      api/controllers/console/explore/recommended_app.py
  10. 2 1
      api/controllers/console/workspace/account.py
  11. 2 0
      api/core/tools/provider/builtin/chart/chart.yaml
  12. 6 0
      api/core/tools/provider/builtin/chart/tools/bar.yaml
  13. 6 0
      api/core/tools/provider/builtin/chart/tools/line.yaml
  14. 6 0
      api/core/tools/provider/builtin/chart/tools/pie.yaml
  15. 11 0
      api/core/tools/provider/builtin/dalle/dalle.yaml
  16. 11 0
      api/core/tools/provider/builtin/dalle/tools/dalle2.yaml
  17. 20 0
      api/core/tools/provider/builtin/dalle/tools/dalle3.yaml
  18. 5 0
      api/core/tools/provider/builtin/google/google.yaml
  19. 8 0
      api/core/tools/provider/builtin/google/tools/google_search.yaml
  20. 6 0
      api/core/tools/provider/builtin/stablediffusion/stablediffusion.yaml
  21. 14 0
      api/core/tools/provider/builtin/stablediffusion/tools/stable_diffusion.yaml
  22. 2 0
      api/core/tools/provider/builtin/time/time.yaml
  23. 2 0
      api/core/tools/provider/builtin/time/tools/current_time.yaml
  24. 6 0
      api/core/tools/provider/builtin/vectorizer/tools/vectorizer.yaml
  25. 8 0
      api/core/tools/provider/builtin/vectorizer/vectorizer.yaml
  26. 6 0
      api/core/tools/provider/builtin/webscraper/tools/webscraper.yaml
  27. 2 0
      api/core/tools/provider/builtin/webscraper/webscraper.yaml
  28. 4 0
      api/core/tools/provider/builtin/wikipedia/tools/wikipedia_search.yaml
  29. 2 0
      api/core/tools/provider/builtin/wikipedia/wikipedia.yaml
  30. 4 0
      api/core/tools/provider/builtin/wolframalpha/tools/wolframalpha.yaml
  31. 5 0
      api/core/tools/provider/builtin/wolframalpha/wolframalpha.yaml
  32. 8 0
      api/core/tools/provider/builtin/yahoo/tools/analytics.yaml
  33. 4 0
      api/core/tools/provider/builtin/yahoo/tools/news.yaml
  34. 4 0
      api/core/tools/provider/builtin/yahoo/tools/ticker.yaml
  35. 2 0
      api/core/tools/provider/builtin/yahoo/yahoo.yaml
  36. 8 0
      api/core/tools/provider/builtin/youtube/tools/videos.yaml
  37. 5 0
      api/core/tools/provider/builtin/youtube/youtube.yaml
  38. 0 10
      api/libs/helper.py
  39. 5 6
      api/services/account_service.py
  40. 2 1
      api/tasks/mail_invite_member_task.py
  41. 0 6
      web/app/(commonLayout)/apps/page.tsx
  42. 2 2
      web/app/activate/activateForm.tsx
  43. 3 6
      web/app/components/app/overview/settings/index.tsx
  44. 0 2
      web/app/components/base/auto-height-textarea/index.tsx
  45. 1 9
      web/app/components/base/select/locale.tsx
  46. 3 3
      web/app/components/header/account-setting/language-page/index.tsx
  47. 1 0
      web/app/components/header/account-setting/model-provider-page/declarations.ts
  48. 2 3
      web/app/components/header/account-setting/model-provider-page/hooks.ts
  49. 0 8
      web/app/components/header/account-setting/model-provider-page/utils.ts
  50. 6 13
      web/app/components/header/maintenance-notice.tsx
  51. 1 1
      web/app/components/tools/index.tsx
  52. 3 2
      web/app/signin/_header.tsx
  53. 2 2
      web/app/signin/oneMoreStep.tsx
  54. 48 2
      web/i18n/i18next-config.ts
  55. 3 1
      web/i18n/index.ts
  56. 87 0
      web/i18n/lang/app-annotation.pt.ts
  57. 79 0
      web/i18n/lang/app-api.pt.ts
  58. 388 0
      web/i18n/lang/app-debug.pt.ts
  59. 69 0
      web/i18n/lang/app-log.pt.ts
  60. 139 0
      web/i18n/lang/app-overview.pt.ts
  61. 49 0
      web/i18n/lang/app.pt.ts
  62. 109 0
      web/i18n/lang/billing.pt.ts
  63. 313 0
      web/i18n/lang/common.pt.ts
  64. 30 0
      web/i18n/lang/custom.pt.ts
  65. 126 0
      web/i18n/lang/dataset-creation.pt.ts
  66. 349 0
      web/i18n/lang/dataset-documents.pt.ts
  67. 28 0
      web/i18n/lang/dataset-hit-testing.pt.ts
  68. 33 0
      web/i18n/lang/dataset-settings.pt.ts
  69. 45 0
      web/i18n/lang/dataset.pt.ts
  70. 80 0
      web/i18n/lang/explore.pt.ts
  71. 4 0
      web/i18n/lang/layout.pt.ts
  72. 57 0
      web/i18n/lang/login.pt.ts
  73. 4 0
      web/i18n/lang/register.pt.ts
  74. 74 0
      web/i18n/lang/share-app.pt.ts
  75. 102 0
      web/i18n/lang/tools.pt.ts
  76. 2 5
      web/models/common.ts
  77. 1 1
      web/types/app.ts
  78. 87 3
      web/utils/language.ts

+ 1 - 22
api/commands.py

@@ -11,6 +11,7 @@ import uuid
 
 import click
 import qdrant_client
+from constants.languages import user_input_form_template
 from core.embedding.cached_embedding import CacheEmbedding
 from core.index.index import IndexBuilder
 from core.model_manager import ModelManager
@@ -583,28 +584,6 @@ def deal_dataset_vector(flask_app: Flask, dataset: Dataset, normalization_count:
 @click.option("--batch-size", default=500, help="Number of records to migrate in each batch.")
 def update_app_model_configs(batch_size):
     pre_prompt_template = '{{default_input}}'
-    user_input_form_template = {
-        "en-US": [
-            {
-                "paragraph": {
-                    "label": "Query",
-                    "variable": "default_input",
-                    "required": False,
-                    "default": ""
-                }
-            }
-        ],
-        "zh-Hans": [
-            {
-                "paragraph": {
-                    "label": "查询内容",
-                    "variable": "default_input",
-                    "required": False,
-                    "default": ""
-                }
-            }
-        ]
-    }
 
     click.secho("Start migrate old data that the text generator can support paragraph variable.", fg='green')
 

+ 326 - 0
api/constants/languages.py

@@ -0,0 +1,326 @@
+
+import json
+from models.model import AppModelConfig
+
+languages = ['en-US', 'zh-Hans', 'pt-BR', 'es-ES', 'fr-FR', 'de-DE', 'ja-JP', 'ko-KR', 'ru-RU', 'it-IT']
+
+language_timezone_mapping = {
+    'en-US': 'America/New_York',
+    'zh-Hans': 'Asia/Shanghai',
+    'pt-BR': 'America/Sao_Paulo',
+    'es-ES': 'Europe/Madrid',
+    'fr-FR': 'Europe/Paris',
+    'de-DE': 'Europe/Berlin',
+    'ja-JP': 'Asia/Tokyo',
+    'ko-KR': 'Asia/Seoul',
+    'ru-RU': 'Europe/Moscow',
+    'it-IT': 'Europe/Rome',
+}
+
+def supported_language(lang):
+    if lang in languages:
+        return lang
+
+    error = ('{lang} is not a valid language.'
+             .format(lang=lang))
+    raise ValueError(error)
+
+user_input_form_template = {
+    "en-US": [
+        {
+            "paragraph": {
+                "label": "Query",
+                "variable": "default_input",
+                "required": False,
+                "default": ""
+            }
+        }
+    ],
+    "zh-Hans": [
+        {
+            "paragraph": {
+                "label": "查询内容",
+                "variable": "default_input",
+                "required": False,
+                "default": ""
+            }
+        }
+    ],
+    "pt-BR": [
+        {
+            "paragraph": {
+                "label": "Consulta",
+                "variable": "default_input",
+                "required": False,
+                "default": ""
+            }
+        }
+    ],
+    "es-ES": [
+        {
+            "paragraph": {
+                "label": "Consulta",
+                "variable": "default_input",
+                "required": False,
+                "default": ""
+            }
+        }
+    ],
+}
+
+demo_model_templates = {
+    'en-US': [
+        {
+            'name': 'Translation Assistant',
+            'icon': '',
+            'icon_background': '',
+            'description': 'A multilingual translator that provides translation capabilities in multiple languages, translating user input into the language they need.',
+            'mode': 'completion',
+            'model_config': AppModelConfig(
+                provider='openai',
+                model_id='gpt-3.5-turbo-instruct',
+                configs={
+                    'prompt_template': "Please translate the following text into {{target_language}}:\n",
+                    'prompt_variables': [
+                        {
+                            "key": "target_language",
+                            "name": "Target Language",
+                            "description": "The language you want to translate into.",
+                            "type": "select",
+                            "default": "Chinese",
+                            'options': [
+                                'Chinese',
+                                'English',
+                                'Japanese',
+                                'French',
+                                'Russian',
+                                'German',
+                                'Spanish',
+                                'Korean',
+                                'Italian',
+                            ]
+                        }
+                    ],
+                    'completion_params': {
+                        'max_token': 1000,
+                        'temperature': 0,
+                        'top_p': 0,
+                        'presence_penalty': 0.1,
+                        'frequency_penalty': 0.1,
+                    }
+                },
+                opening_statement='',
+                suggested_questions=None,
+                pre_prompt="Please translate the following text into {{target_language}}:\n{{query}}\ntranslate:",
+                model=json.dumps({
+                    "provider": "openai",
+                    "name": "gpt-3.5-turbo-instruct",
+                    "mode": "completion",
+                    "completion_params": {
+                        "max_tokens": 1000,
+                        "temperature": 0,
+                        "top_p": 0,
+                        "presence_penalty": 0.1,
+                        "frequency_penalty": 0.1
+                    }
+                }),
+                user_input_form=json.dumps([
+                    {
+                        "select": {
+                            "label": "Target Language",
+                            "variable": "target_language",
+                            "description": "The language you want to translate into.",
+                            "default": "Chinese",
+                            "required": True,
+                            'options': [
+                                'Chinese',
+                                'English',
+                                'Japanese',
+                                'French',
+                                'Russian',
+                                'German',
+                                'Spanish',
+                                'Korean',
+                                'Italian',
+                            ]
+                        }
+                    },{
+                        "paragraph": {
+                            "label": "Query",
+                            "variable": "query",
+                            "required": True,
+                            "default": ""
+                        }
+                    }
+                ])
+            )
+        },
+        {
+            'name': 'AI Front-end Interviewer',
+            'icon': '',
+            'icon_background': '',
+            'description': 'A simulated front-end interviewer that tests the skill level of front-end development through questioning.',
+            'mode': 'chat',
+            'model_config': AppModelConfig(
+                provider='openai',
+                model_id='gpt-3.5-turbo',
+                configs={
+                    'introduction': 'Hi, welcome to our interview. I am the interviewer for this technology company, and I will test your web front-end development skills. Next, I will ask you some technical questions. Please answer them as thoroughly as possible. ',
+                    'prompt_template': "You will play the role of an interviewer for a technology company, examining the user's web front-end development skills and posing 5-10 sharp technical questions.\n\nPlease note:\n- Only ask one question at a time.\n- After the user answers a question, ask the next question directly, without trying to correct any mistakes made by the candidate.\n- If you think the user has not answered correctly for several consecutive questions, ask fewer questions.\n- After asking the last question, you can ask this question: Why did you leave your last job? After the user answers this question, please express your understanding and support.\n",
+                    'prompt_variables': [],
+                    'completion_params': {
+                        'max_token': 300,
+                        'temperature': 0.8,
+                        'top_p': 0.9,
+                        'presence_penalty': 0.1,
+                        'frequency_penalty': 0.1,
+                    }
+                },
+                opening_statement='Hi, welcome to our interview. I am the interviewer for this technology company, and I will test your web front-end development skills. Next, I will ask you some technical questions. Please answer them as thoroughly as possible. ',
+                suggested_questions=None,
+                pre_prompt="You will play the role of an interviewer for a technology company, examining the user's web front-end development skills and posing 5-10 sharp technical questions.\n\nPlease note:\n- Only ask one question at a time.\n- After the user answers a question, ask the next question directly, without trying to correct any mistakes made by the candidate.\n- If you think the user has not answered correctly for several consecutive questions, ask fewer questions.\n- After asking the last question, you can ask this question: Why did you leave your last job? After the user answers this question, please express your understanding and support.\n",
+                model=json.dumps({
+                    "provider": "openai",
+                    "name": "gpt-3.5-turbo",
+                    "mode": "chat",
+                    "completion_params": {
+                        "max_tokens": 300,
+                        "temperature": 0.8,
+                        "top_p": 0.9,
+                        "presence_penalty": 0.1,
+                        "frequency_penalty": 0.1
+                    }
+                }),
+                user_input_form=None
+            )
+        }
+    ],
+
+    'zh-Hans': [
+        {
+            'name': '翻译助手',
+            'icon': '',
+            'icon_background': '',
+            'description': '一个多语言翻译器,提供多种语言翻译能力,将用户输入的文本翻译成他们需要的语言。',
+            'mode': 'completion',
+            'model_config': AppModelConfig(
+                provider='openai',
+                model_id='gpt-3.5-turbo-instruct',
+                configs={
+                    'prompt_template': "请将以下文本翻译为{{target_language}}:\n",
+                    'prompt_variables': [
+                        {
+                            "key": "target_language",
+                            "name": "目标语言",
+                            "description": "翻译的目标语言",
+                            "type": "select",
+                            "default": "中文",
+                            "options": [
+                                "中文",
+                                "英文",
+                                "日语",
+                                "法语",
+                                "俄语",
+                                "德语",
+                                "西班牙语",
+                                "韩语",
+                                "意大利语",
+                            ]
+                        }
+                    ],
+                    'completion_params': {
+                        'max_token': 1000,
+                        'temperature': 0,
+                        'top_p': 0,
+                        'presence_penalty': 0.1,
+                        'frequency_penalty': 0.1,
+                    }
+                },
+                opening_statement='',
+                suggested_questions=None,
+                pre_prompt="请将以下文本翻译为{{target_language}}:\n{{query}}\n翻译:",
+                model=json.dumps({
+                    "provider": "openai",
+                    "name": "gpt-3.5-turbo-instruct",
+                    "mode": "completion",
+                    "completion_params": {
+                        "max_tokens": 1000,
+                        "temperature": 0,
+                        "top_p": 0,
+                        "presence_penalty": 0.1,
+                        "frequency_penalty": 0.1
+                    }
+                }),
+                user_input_form=json.dumps([
+                    {
+                        "select": {
+                            "label": "目标语言",
+                            "variable": "target_language",
+                            "description": "翻译的目标语言",
+                            "default": "中文",
+                            "required": True,
+                            'options': [
+                                "中文",
+                                "英文",
+                                "日语",
+                                "法语",
+                                "俄语",
+                                "德语",
+                                "西班牙语",
+                                "韩语",
+                                "意大利语",
+                            ]
+                        }
+                    },{
+                        "paragraph": {
+                            "label": "文本内容",
+                            "variable": "query",
+                            "required": True,
+                            "default": ""
+                        }
+                    }
+                ])
+            )
+        },
+        {
+            'name': 'AI 前端面试官',
+            'icon': '',
+            'icon_background': '',
+            'description': '一个模拟的前端面试官,通过提问的方式对前端开发的技能水平进行检验。',
+            'mode': 'chat',
+            'model_config': AppModelConfig(
+                provider='openai',
+                model_id='gpt-3.5-turbo',
+                configs={
+                    'introduction': '你好,欢迎来参加我们的面试,我是这家科技公司的面试官,我将考察你的 Web 前端开发技能。接下来我会向您提出一些技术问题,请您尽可能详尽地回答。',
+                    'prompt_template': "你将扮演一个科技公司的面试官,考察用户作为候选人的 Web 前端开发水平,提出 5-10 个犀利的技术问题。\n\n请注意:\n- 每次只问一个问题\n- 用户回答问题后请直接问下一个问题,而不要试图纠正候选人的错误;\n- 如果你认为用户连续几次回答的都不对,就少问一点;\n- 问完最后一个问题后,你可以问这样一个问题:上一份工作为什么离职?用户回答该问题后,请表示理解与支持。\n",
+                    'prompt_variables': [],
+                    'completion_params': {
+                        'max_token': 300,
+                        'temperature': 0.8,
+                        'top_p': 0.9,
+                        'presence_penalty': 0.1,
+                        'frequency_penalty': 0.1,
+                    }
+                },
+                opening_statement='你好,欢迎来参加我们的面试,我是这家科技公司的面试官,我将考察你的 Web 前端开发技能。接下来我会向您提出一些技术问题,请您尽可能详尽地回答。',
+                suggested_questions=None,
+                pre_prompt="你将扮演一个科技公司的面试官,考察用户作为候选人的 Web 前端开发水平,提出 5-10 个犀利的技术问题。\n\n请注意:\n- 每次只问一个问题\n- 用户回答问题后请直接问下一个问题,而不要试图纠正候选人的错误;\n- 如果你认为用户连续几次回答的都不对,就少问一点;\n- 问完最后一个问题后,你可以问这样一个问题:上一份工作为什么离职?用户回答该问题后,请表示理解与支持。\n",
+                model=json.dumps({
+                    "provider": "openai",
+                    "name": "gpt-3.5-turbo",
+                    "mode": "chat",
+                    "completion_params": {
+                        "max_tokens": 300,
+                        "temperature": 0.8,
+                        "top_p": 0.9,
+                        "presence_penalty": 0.1,
+                        "frequency_penalty": 0.1
+                    }
+                }),
+                user_input_form=None
+            )
+        }
+    ],
+
+}

+ 0 - 255
api/constants/model_template.py

@@ -96,258 +96,3 @@ model_templates = {
 }
 
 
-demo_model_templates = {
-    'en-US': [
-        {
-            'name': 'Translation Assistant',
-            'icon': '',
-            'icon_background': '',
-            'description': 'A multilingual translator that provides translation capabilities in multiple languages, translating user input into the language they need.',
-            'mode': 'completion',
-            'model_config': AppModelConfig(
-                provider='openai',
-                model_id='gpt-3.5-turbo-instruct',
-                configs={
-                    'prompt_template': "Please translate the following text into {{target_language}}:\n",
-                    'prompt_variables': [
-                        {
-                            "key": "target_language",
-                            "name": "Target Language",
-                            "description": "The language you want to translate into.",
-                            "type": "select",
-                            "default": "Chinese",
-                            'options': [
-                                'Chinese',
-                                'English',
-                                'Japanese',
-                                'French',
-                                'Russian',
-                                'German',
-                                'Spanish',
-                                'Korean',
-                                'Italian',
-                            ]
-                        }
-                    ],
-                    'completion_params': {
-                        'max_token': 1000,
-                        'temperature': 0,
-                        'top_p': 0,
-                        'presence_penalty': 0.1,
-                        'frequency_penalty': 0.1,
-                    }
-                },
-                opening_statement='',
-                suggested_questions=None,
-                pre_prompt="Please translate the following text into {{target_language}}:\n{{query}}\ntranslate:",
-                model=json.dumps({
-                    "provider": "openai",
-                    "name": "gpt-3.5-turbo-instruct",
-                    "mode": "completion",
-                    "completion_params": {
-                        "max_tokens": 1000,
-                        "temperature": 0,
-                        "top_p": 0,
-                        "presence_penalty": 0.1,
-                        "frequency_penalty": 0.1
-                    }
-                }),
-                user_input_form=json.dumps([
-                    {
-                        "select": {
-                            "label": "Target Language",
-                            "variable": "target_language",
-                            "description": "The language you want to translate into.",
-                            "default": "Chinese",
-                            "required": True,
-                            'options': [
-                                'Chinese',
-                                'English',
-                                'Japanese',
-                                'French',
-                                'Russian',
-                                'German',
-                                'Spanish',
-                                'Korean',
-                                'Italian',
-                            ]
-                        }
-                    },{
-                        "paragraph": {
-                            "label": "Query",
-                            "variable": "query",
-                            "required": True,
-                            "default": ""
-                        }
-                    }
-                ])
-            )
-        },
-        {
-            'name': 'AI Front-end Interviewer',
-            'icon': '',
-            'icon_background': '',
-            'description': 'A simulated front-end interviewer that tests the skill level of front-end development through questioning.',
-            'mode': 'chat',
-            'model_config': AppModelConfig(
-                provider='openai',
-                model_id='gpt-3.5-turbo',
-                configs={
-                    'introduction': 'Hi, welcome to our interview. I am the interviewer for this technology company, and I will test your web front-end development skills. Next, I will ask you some technical questions. Please answer them as thoroughly as possible. ',
-                    'prompt_template': "You will play the role of an interviewer for a technology company, examining the user's web front-end development skills and posing 5-10 sharp technical questions.\n\nPlease note:\n- Only ask one question at a time.\n- After the user answers a question, ask the next question directly, without trying to correct any mistakes made by the candidate.\n- If you think the user has not answered correctly for several consecutive questions, ask fewer questions.\n- After asking the last question, you can ask this question: Why did you leave your last job? After the user answers this question, please express your understanding and support.\n",
-                    'prompt_variables': [],
-                    'completion_params': {
-                        'max_token': 300,
-                        'temperature': 0.8,
-                        'top_p': 0.9,
-                        'presence_penalty': 0.1,
-                        'frequency_penalty': 0.1,
-                    }
-                },
-                opening_statement='Hi, welcome to our interview. I am the interviewer for this technology company, and I will test your web front-end development skills. Next, I will ask you some technical questions. Please answer them as thoroughly as possible. ',
-                suggested_questions=None,
-                pre_prompt="You will play the role of an interviewer for a technology company, examining the user's web front-end development skills and posing 5-10 sharp technical questions.\n\nPlease note:\n- Only ask one question at a time.\n- After the user answers a question, ask the next question directly, without trying to correct any mistakes made by the candidate.\n- If you think the user has not answered correctly for several consecutive questions, ask fewer questions.\n- After asking the last question, you can ask this question: Why did you leave your last job? After the user answers this question, please express your understanding and support.\n",
-                model=json.dumps({
-                    "provider": "openai",
-                    "name": "gpt-3.5-turbo",
-                    "mode": "chat",
-                    "completion_params": {
-                        "max_tokens": 300,
-                        "temperature": 0.8,
-                        "top_p": 0.9,
-                        "presence_penalty": 0.1,
-                        "frequency_penalty": 0.1
-                    }
-                }),
-                user_input_form=None
-            )
-        }
-    ],
-
-    'zh-Hans': [
-        {
-            'name': '翻译助手',
-            'icon': '',
-            'icon_background': '',
-            'description': '一个多语言翻译器,提供多种语言翻译能力,将用户输入的文本翻译成他们需要的语言。',
-            'mode': 'completion',
-            'model_config': AppModelConfig(
-                provider='openai',
-                model_id='gpt-3.5-turbo-instruct',
-                configs={
-                    'prompt_template': "请将以下文本翻译为{{target_language}}:\n",
-                    'prompt_variables': [
-                        {
-                            "key": "target_language",
-                            "name": "目标语言",
-                            "description": "翻译的目标语言",
-                            "type": "select",
-                            "default": "中文",
-                            "options": [
-                                "中文",
-                                "英文",
-                                "日语",
-                                "法语",
-                                "俄语",
-                                "德语",
-                                "西班牙语",
-                                "韩语",
-                                "意大利语",
-                            ]
-                        }
-                    ],
-                    'completion_params': {
-                        'max_token': 1000,
-                        'temperature': 0,
-                        'top_p': 0,
-                        'presence_penalty': 0.1,
-                        'frequency_penalty': 0.1,
-                    }
-                },
-                opening_statement='',
-                suggested_questions=None,
-                pre_prompt="请将以下文本翻译为{{target_language}}:\n{{query}}\n翻译:",
-                model=json.dumps({
-                    "provider": "openai",
-                    "name": "gpt-3.5-turbo-instruct",
-                    "mode": "completion",
-                    "completion_params": {
-                        "max_tokens": 1000,
-                        "temperature": 0,
-                        "top_p": 0,
-                        "presence_penalty": 0.1,
-                        "frequency_penalty": 0.1
-                    }
-                }),
-                user_input_form=json.dumps([
-                    {
-                        "select": {
-                            "label": "目标语言",
-                            "variable": "target_language",
-                            "description": "翻译的目标语言",
-                            "default": "中文",
-                            "required": True,
-                            'options': [
-                                "中文",
-                                "英文",
-                                "日语",
-                                "法语",
-                                "俄语",
-                                "德语",
-                                "西班牙语",
-                                "韩语",
-                                "意大利语",
-                            ]
-                        }
-                    },{
-                        "paragraph": {
-                            "label": "文本内容",
-                            "variable": "query",
-                            "required": True,
-                            "default": ""
-                        }
-                    }
-                ])
-            )
-        },
-        {
-            'name': 'AI 前端面试官',
-            'icon': '',
-            'icon_background': '',
-            'description': '一个模拟的前端面试官,通过提问的方式对前端开发的技能水平进行检验。',
-            'mode': 'chat',
-            'model_config': AppModelConfig(
-                provider='openai',
-                model_id='gpt-3.5-turbo',
-                configs={
-                    'introduction': '你好,欢迎来参加我们的面试,我是这家科技公司的面试官,我将考察你的 Web 前端开发技能。接下来我会向您提出一些技术问题,请您尽可能详尽地回答。',
-                    'prompt_template': "你将扮演一个科技公司的面试官,考察用户作为候选人的 Web 前端开发水平,提出 5-10 个犀利的技术问题。\n\n请注意:\n- 每次只问一个问题\n- 用户回答问题后请直接问下一个问题,而不要试图纠正候选人的错误;\n- 如果你认为用户连续几次回答的都不对,就少问一点;\n- 问完最后一个问题后,你可以问这样一个问题:上一份工作为什么离职?用户回答该问题后,请表示理解与支持。\n",
-                    'prompt_variables': [],
-                    'completion_params': {
-                        'max_token': 300,
-                        'temperature': 0.8,
-                        'top_p': 0.9,
-                        'presence_penalty': 0.1,
-                        'frequency_penalty': 0.1,
-                    }
-                },
-                opening_statement='你好,欢迎来参加我们的面试,我是这家科技公司的面试官,我将考察你的 Web 前端开发技能。接下来我会向您提出一些技术问题,请您尽可能详尽地回答。',
-                suggested_questions=None,
-                pre_prompt="你将扮演一个科技公司的面试官,考察用户作为候选人的 Web 前端开发水平,提出 5-10 个犀利的技术问题。\n\n请注意:\n- 每次只问一个问题\n- 用户回答问题后请直接问下一个问题,而不要试图纠正候选人的错误;\n- 如果你认为用户连续几次回答的都不对,就少问一点;\n- 问完最后一个问题后,你可以问这样一个问题:上一份工作为什么离职?用户回答该问题后,请表示理解与支持。\n",
-                model=json.dumps({
-                    "provider": "openai",
-                    "name": "gpt-3.5-turbo",
-                    "mode": "chat",
-                    "completion_params": {
-                        "max_tokens": 300,
-                        "temperature": 0.8,
-                        "top_p": 0.9,
-                        "presence_penalty": 0.1,
-                        "frequency_penalty": 0.1
-                    }
-                }),
-                user_input_form=None
-            )
-        }
-    ],
-}

+ 1 - 1
api/controllers/console/admin.py

@@ -6,7 +6,7 @@ from controllers.console.wraps import only_edition_cloud
 from extensions.ext_database import db
 from flask import request
 from flask_restful import Resource, reqparse
-from libs.helper import supported_language
+from constants.languages import supported_language
 from models.model import App, InstalledApp, RecommendedApp
 from werkzeug.exceptions import NotFound, Unauthorized
 

+ 3 - 2
api/controllers/console/app/app.py

@@ -3,7 +3,8 @@ import json
 import logging
 from datetime import datetime
 
-from constants.model_template import demo_model_templates, model_templates
+from constants.model_template import model_templates
+from constants.languages import demo_model_templates, languages
 from controllers.console import api
 from controllers.console.app.error import AppNotFoundError, ProviderNotInitializeError
 from controllers.console.setup import setup_required
@@ -211,7 +212,7 @@ class AppTemplateApi(Resource):
 
         templates = demo_model_templates.get(interface_language)
         if not templates:
-            templates = demo_model_templates.get('en-US')
+            templates = demo_model_templates.get(languages[0])
 
         return {'data': templates}
 

+ 1 - 1
api/controllers/console/app/site.py

@@ -7,7 +7,7 @@ from extensions.ext_database import db
 from fields.app_fields import app_site_fields
 from flask_login import current_user
 from flask_restful import Resource, marshal_with, reqparse
-from libs.helper import supported_language
+from constants.languages import supported_language
 from libs.login import login_required
 from models.model import Site
 from werkzeug.exceptions import Forbidden, NotFound

+ 2 - 1
api/controllers/console/auth/activate.py

@@ -6,7 +6,8 @@ from controllers.console import api
 from controllers.console.error import AlreadyActivateError
 from extensions.ext_database import db
 from flask_restful import Resource, reqparse
-from libs.helper import email, str_len, supported_language, timezone
+from libs.helper import email, str_len, timezone
+from constants.languages import supported_language
 from libs.password import hash_password, valid_password
 from models.account import AccountStatus, Tenant
 from services.account_service import RegisterService

+ 5 - 4
api/controllers/console/auth/oauth.py

@@ -3,6 +3,7 @@ from datetime import datetime
 from typing import Optional
 
 import requests
+from constants.languages import languages
 from extensions.ext_database import db
 from flask import current_app, redirect, request
 from flask_restful import Resource
@@ -106,11 +107,11 @@ def _generate_account(provider: str, user_info: OAuthUserInfo):
         )
 
         # Set interface language
-        preferred_lang = request.accept_languages.best_match(['zh', 'en'])
-        if preferred_lang == 'zh':
-            interface_language = 'zh-Hans'
+        preferred_lang = request.accept_languages.best_match(languages)
+        if preferred_lang and preferred_lang in languages:
+            interface_language = preferred_lang
         else:
-            interface_language = 'en-US'
+            interface_language = languages[0]
         account.interface_language = interface_language
         db.session.commit()
 

+ 2 - 1
api/controllers/console/explore/recommended_app.py

@@ -9,6 +9,7 @@ from libs.login import login_required
 from models.model import App, InstalledApp, RecommendedApp
 from services.account_service import TenantService
 from sqlalchemy import and_
+from constants.languages import languages
 
 app_fields = {
     'id': fields.String,
@@ -44,7 +45,7 @@ class RecommendedAppListApi(Resource):
     @account_initialization_required
     @marshal_with(recommended_app_list_fields)
     def get(self):
-        language_prefix = current_user.interface_language if current_user.interface_language else 'en-US'
+        language_prefix = current_user.interface_language if current_user.interface_language else languages[0]
 
         recommended_apps = db.session.query(RecommendedApp).filter(
             RecommendedApp.is_listed == True,

+ 2 - 1
api/controllers/console/workspace/account.py

@@ -11,7 +11,8 @@ from extensions.ext_database import db
 from flask import current_app, request
 from flask_login import current_user
 from flask_restful import Resource, fields, marshal_with, reqparse
-from libs.helper import TimestampField, supported_language, timezone
+from libs.helper import TimestampField, timezone
+from constants.languages import supported_language
 from libs.login import login_required
 from models.account import AccountIntegrate, InvitationCode
 from services.account_service import AccountService

+ 2 - 0
api/core/tools/provider/builtin/chart/chart.yaml

@@ -4,8 +4,10 @@ identity:
   label:
     en_US: ChartGenerator
     zh_Hans: 图表生成
+    pt_BR: Gerador de gráficos
   description:
     en_US: Chart Generator is a tool for generating statistical charts like bar chart, line chart, pie chart, etc.
     zh_Hans: 图表生成是一个用于生成可视化图表的工具,你可以通过它来生成柱状图、折线图、饼图等各类图表
+    pt_BR: O Gerador de gráficos é uma ferramenta para gerar gráficos estatísticos como gráfico de barras, gráfico de linhas, gráfico de pizza, etc.
   icon: icon.png
 credentails_for_provider:

+ 6 - 0
api/core/tools/provider/builtin/chart/tools/bar.yaml

@@ -4,11 +4,13 @@ identity:
   label:
     en_US: Bar Chart
     zh_Hans: 柱状图
+    pt_BR: Gráfico de barras
   icon: icon.svg
 description:
   human:
     en_US: Bar chart
     zh_Hans: 柱状图
+    pt_BR: Gráfico de barras
   llm: generate a bar chart with input data
 parameters:
   - name: data
@@ -17,9 +19,11 @@ parameters:
     label:
       en_US: data
       zh_Hans: 数据
+      pt_BR: dados
     human_description:
       en_US: data for generating bar chart
       zh_Hans: 用于生成柱状图的数据
+      pt_BR: dados para gerar gráfico de barras
     llm_description: data for generating bar chart, data should be a string contains a list of numbers like "1;2;3;4;5"
     form: llm
   - name: x_axis
@@ -28,8 +32,10 @@ parameters:
     label:
       en_US: X Axis
       zh_Hans: x 轴
+      pt_BR: Eixo X
     human_description:
       en_US: X axis for bar chart
       zh_Hans: 柱状图的 x 轴
+      pt_BR: Eixo X para gráfico de barras
     llm_description: x axis for bar chart, x axis should be a string contains a list of texts like "a;b;c;1;2" in order to match the data
     form: llm

+ 6 - 0
api/core/tools/provider/builtin/chart/tools/line.yaml

@@ -4,11 +4,13 @@ identity:
   label:
     en_US: Linear Chart
     zh_Hans: 线性图表
+    pt_BR: Gráfico linear
   icon: icon.svg
 description:
   human:
     en_US: linear chart
     zh_Hans: 线性图表
+    pt_BR: Gráfico linear
   llm: generate a linear chart with input data
 parameters:
   - name: data
@@ -17,9 +19,11 @@ parameters:
     label:
       en_US: data
       zh_Hans: 数据
+      pt_BR: dados
     human_description:
       en_US: data for generating linear chart
       zh_Hans: 用于生成线性图表的数据
+      pt_BR: dados para gerar gráfico linear
     llm_description: data for generating linear chart, data should be a string contains a list of numbers like "1;2;3;4;5"
     form: llm
   - name: x_axis
@@ -28,8 +32,10 @@ parameters:
     label:
       en_US: X Axis
       zh_Hans: x 轴
+      pt_BR: Eixo X
     human_description:
       en_US: X axis for linear chart
       zh_Hans: 线性图表的 x 轴
+      pt_BR: Eixo X para gráfico linear
     llm_description: x axis for linear chart, x axis should be a string contains a list of texts like "a;b;c;1;2" in order to match the data
     form: llm

+ 6 - 0
api/core/tools/provider/builtin/chart/tools/pie.yaml

@@ -4,11 +4,13 @@ identity:
   label:
     en_US: Pie Chart
     zh_Hans: 饼图
+    pt_BR: Gráfico de pizza
   icon: icon.svg
 description:
   human:
     en_US: Pie chart
     zh_Hans: 饼图
+    pt_BR: Gráfico de pizza
   llm: generate a pie chart with input data
 parameters:
   - name: data
@@ -17,9 +19,11 @@ parameters:
     label:
       en_US: data
       zh_Hans: 数据
+      pt_BR: dados
     human_description:
       en_US: data for generating pie chart
       zh_Hans: 用于生成饼图的数据
+      pt_BR: dados para gerar gráfico de pizza
     llm_description: data for generating pie chart, data should be a string contains a list of numbers like "1;2;3;4;5"
     form: llm
   - name: categories
@@ -28,8 +32,10 @@ parameters:
     label:
       en_US: Categories
       zh_Hans: 分类
+      pt_BR: Categorias
     human_description:
       en_US: Categories for pie chart
       zh_Hans: 饼图的分类
+      pt_BR: Categorias para gráfico de pizza
     llm_description: categories for pie chart, categories should be a string contains a list of texts like "a;b;c;1;2" in order to match the data, each category should be split by ";"
     form: llm

+ 11 - 0
api/core/tools/provider/builtin/dalle/dalle.yaml

@@ -4,9 +4,11 @@ identity:
   label:
     en_US: DALL-E
     zh_Hans: DALL-E 绘画
+    pt_BR: DALL-E
   description:
     en_US: DALL-E art
     zh_Hans: DALL-E 绘画
+    pt_BR: DALL-E art
   icon: icon.png
 credentails_for_provider:
   openai_api_key:
@@ -15,33 +17,42 @@ credentails_for_provider:
     label:
       en_US: OpenAI API key
       zh_Hans: OpenAI API key
+      pt_BR: OpenAI API key
     help:
       en_US: Please input your OpenAI API key
       zh_Hans: 请输入你的 OpenAI API key
+      pt_BR: Please input your OpenAI API key
     placeholder:
       en_US: Please input your OpenAI API key
       zh_Hans: 请输入你的 OpenAI API key
+      pt_BR: Please input your OpenAI API key
   openai_organizaion_id:
     type: text-input
     required: false
     label:
       en_US: OpenAI organization ID
       zh_Hans: OpenAI organization ID
+      pt_BR: OpenAI organization ID
     help:
       en_US: Please input your OpenAI organization ID
       zh_Hans: 请输入你的 OpenAI organization ID
+      pt_BR: Please input your OpenAI organization ID
     placeholder:
       en_US: Please input your OpenAI organization ID
       zh_Hans: 请输入你的 OpenAI organization ID
+      pt_BR: Please input your OpenAI organization ID
   openai_base_url:
     type: text-input
     required: false
     label:
       en_US: OpenAI base URL
       zh_Hans: OpenAI base URL
+      pt_BR: OpenAI base URL
     help:
       en_US: Please input your OpenAI base URL
       zh_Hans: 请输入你的 OpenAI base URL
+      pt_BR: Please input your OpenAI base URL
     placeholder:
       en_US: Please input your OpenAI base URL
       zh_Hans: 请输入你的 OpenAI base URL
+      pt_BR: Please input your OpenAI base URL

+ 11 - 0
api/core/tools/provider/builtin/dalle/tools/dalle2.yaml

@@ -7,10 +7,12 @@ identity:
   description:
     en_US: DALL-E 2 is a powerful drawing tool that can draw the image you want based on your prompt
     zh_Hans: DALL-E 2 是一个强大的绘画工具,它可以根据您的提示词绘制出您想要的图像
+    pt_BR: DALL-E 2 is a powerful drawing tool that can draw the image you want based on your prompt
 description:
   human:
     en_US: DALL-E is a text to image tool
     zh_Hans: DALL-E 是一个文本到图像的工具
+    pt_BR: DALL-E is a text to image tool
   llm: DALL-E is a tool used to generate images from text
 parameters:
   - name: prompt
@@ -19,9 +21,11 @@ parameters:
     label:
       en_US: Prompt
       zh_Hans: 提示词
+      pt_BR: Prompt
     human_description:
       en_US: Image prompt, you can check the official documentation of DallE 2
       zh_Hans: 图像提示词,您可以查看DallE 2 的官方文档
+      pt_BR: Image prompt, you can check the official documentation of DallE 2
     llm_description: Image prompt of DallE 2, you should describe the image you want to generate as a list of words as possible as detailed
     form: llm
   - name: size
@@ -30,23 +34,28 @@ parameters:
     human_description:
       en_US: used for selecting the image size
       zh_Hans: 用于选择图像大小
+      pt_BR: used for selecting the image size
     label:
       en_US: Image size
       zh_Hans: 图像大小
+      pt_BR: Image size
     form: form
     options:
       - value: small
         label:
           en_US: Small(256x256)
           zh_Hans: 小(256x256)
+          pt_BR: Small(256x256)
       - value: medium
         label:
           en_US: Medium(512x512)
           zh_Hans: 中(512x512)
+          pt_BR: Medium(512x512)
       - value: large
         label:
           en_US: Large(1024x1024)
           zh_Hans: 大(1024x1024)
+          pt_BR: Large(1024x1024)
     default: large
   - name: n
     type: number
@@ -54,9 +63,11 @@ parameters:
     human_description:
       en_US: used for selecting the number of images
       zh_Hans: 用于选择图像数量
+      pt_BR: used for selecting the number of images
     label:
       en_US: Number of images
       zh_Hans: 图像数量
+      pt_BR: Number of images
     form: form
     default: 1
     min: 1

+ 20 - 0
api/core/tools/provider/builtin/dalle/tools/dalle3.yaml

@@ -4,13 +4,16 @@ identity:
   label:
     en_US: DALL-E 3
     zh_Hans: DALL-E 3 绘画
+    pt_BR: DALL-E 3
   description:
     en_US: DALL-E 3 is a powerful drawing tool that can draw the image you want based on your prompt, compared to DallE 2, DallE 3 has stronger drawing ability, but it will consume more resources
     zh_Hans: DALL-E 3 是一个强大的绘画工具,它可以根据您的提示词绘制出您想要的图像,相比于DallE 2, DallE 3拥有更强的绘画能力,但会消耗更多的资源
+    pt_BR: DALL-E 3 is a powerful drawing tool that can draw the image you want based on your prompt, compared to DallE 2, DallE 3 has stronger drawing ability, but it will consume more resources
 description:
   human:
     en_US: DALL-E is a text to image tool
     zh_Hans: DALL-E 是一个文本到图像的工具
+    pt_BR: DALL-E is a text to image tool
   llm: DALL-E is a tool used to generate images from text
 parameters:
   - name: prompt
@@ -19,9 +22,11 @@ parameters:
     label:
       en_US: Prompt
       zh_Hans: 提示词
+      pt_BR: Prompt
     human_description:
       en_US: Image prompt, you can check the official documentation of DallE 3
       zh_Hans: 图像提示词,您可以查看DallE 3 的官方文档
+      pt_BR: Image prompt, you can check the official documentation of DallE 3
     llm_description: Image prompt of DallE 3, you should describe the image you want to generate as a list of words as possible as detailed
     form: llm
   - name: size
@@ -30,23 +35,28 @@ parameters:
     human_description:
       en_US: selecting the image size
       zh_Hans: 选择图像大小
+      pt_BR: selecting the image size
     label:
       en_US: Image size
       zh_Hans: 图像大小
+      pt_BR: Image size
     form: form
     options:
       - value: square
         label:
           en_US: Squre(1024x1024)
           zh_Hans: 方(1024x1024)
+          pt_BR: Squre(1024x1024)
       - value: vertical
         label:
           en_US: Vertical(1024x1792)
           zh_Hans: 竖屏(1024x1792)
+          pt_BR: Vertical(1024x1792)
       - value: horizontal
         label:
           en_US: Horizontal(1792x1024)
           zh_Hans: 横屏(1792x1024)
+          pt_BR: Horizontal(1792x1024)
     default: square
   - name: n
     type: number
@@ -54,9 +64,11 @@ parameters:
     human_description:
       en_US: selecting the number of images
       zh_Hans: 选择图像数量
+      pt_BR: selecting the number of images
     label:
       en_US: Number of images
       zh_Hans: 图像数量
+      pt_BR: Number of images
     form: form
     min: 1
     max: 1
@@ -67,19 +79,23 @@ parameters:
     human_description:
       en_US: selecting the image quality
       zh_Hans: 选择图像质量
+      pt_BR: selecting the image quality
     label:
       en_US: Image quality
       zh_Hans: 图像质量
+      pt_BR: Image quality
     form: form
     options:
       - value: standard
         label:
           en_US: Standard
           zh_Hans: 标准
+          pt_BR: Standard
       - value: hd
         label:
           en_US: HD
           zh_Hans: 高清
+          pt_BR: HD
     default: standard
   - name: style
     type: select
@@ -87,17 +103,21 @@ parameters:
     human_description:
       en_US: selecting the image style
       zh_Hans: 选择图像风格
+      pt_BR: selecting the image style
     label:
       en_US: Image style
       zh_Hans: 图像风格
+      pt_BR: Image style
     form: form
     options:
       - value: vivid
         label:
           en_US: Vivid
           zh_Hans: 生动
+          pt_BR: Vivid
       - value: natural
         label:
           en_US: Natural
           zh_Hans: 自然
+          pt_BR: Natural
     default: vivid

+ 5 - 0
api/core/tools/provider/builtin/google/google.yaml

@@ -4,9 +4,11 @@ identity:
   label:
     en_US: Google
     zh_Hans: Google
+    pt_BR: Google
   description:
     en_US: Google
     zh_Hans: GoogleSearch
+    pt_BR: Google
   icon: icon.svg
 credentails_for_provider:
   serpapi_api_key:
@@ -15,10 +17,13 @@ credentails_for_provider:
     label:
       en_US: SerpApi API key
       zh_Hans: SerpApi API key
+      pt_BR: SerpApi API key
     placeholder:
       en_US: Please input your SerpApi API key
       zh_Hans: 请输入你的 SerpApi API key
+      pt_BR: Please input your SerpApi API key
     help:
       en_US: Get your SerpApi API key from SerpApi
       zh_Hans: 从 SerpApi 获取您的 SerpApi API key
+      pt_BR: Get your SerpApi API key from SerpApi
     url: https://serpapi.com/manage-api-key

+ 8 - 0
api/core/tools/provider/builtin/google/tools/google_search.yaml

@@ -4,10 +4,12 @@ identity:
   label:
     en_US: GoogleSearch
     zh_Hans: 谷歌搜索
+    pt_BR: GoogleSearch
 description:
   human:
     en_US: A tool for performing a Google SERP search and extracting snippets and webpages.Input should be a search query.
     zh_Hans: 一个用于执行 Google SERP 搜索并提取片段和网页的工具。输入应该是一个搜索查询。
+    pt_BR: A tool for performing a Google SERP search and extracting snippets and webpages.Input should be a search query.
   llm: A tool for performing a Google SERP search and extracting snippets and webpages.Input should be a search query.
 parameters:
   - name: query
@@ -16,9 +18,11 @@ parameters:
     label:
       en_US: Query string
       zh_Hans: 查询语句
+      pt_BR: Query string
     human_description:
       en_US: used for searching
       zh_Hans: 用于搜索网页内容
+      pt_BR: used for searching
     llm_description: key words for searching
     form: llm
   - name: result_type
@@ -29,15 +33,19 @@ parameters:
         label:
           en_US: text
           zh_Hans: 文本
+          pt_BR: texto
       - value: link
         label:
           en_US: link
           zh_Hans: 链接
+          pt_BR: link
     default: link
     label:
       en_US: Result type
       zh_Hans: 结果类型
+      pt_BR: Result type
     human_description:
       en_US: used for selecting the result type, text or link
       zh_Hans: 用于选择结果类型,使用文本还是链接进行展示
+      pt_BR: used for selecting the result type, text or link
     form: form

+ 6 - 0
api/core/tools/provider/builtin/stablediffusion/stablediffusion.yaml

@@ -4,9 +4,11 @@ identity:
   label:
     en_US: Stable Diffusion
     zh_Hans: Stable Diffusion
+    pt_BR: Stable Diffusion
   description:
     en_US: Stable Diffusion is a tool for generating images which can be deployed locally.
     zh_Hans: Stable Diffusion 是一个可以在本地部署的图片生成的工具。
+    pt_BR: Stable Diffusion is a tool for generating images which can be deployed locally.
   icon: icon.png
 credentails_for_provider:
   base_url:
@@ -15,15 +17,19 @@ credentails_for_provider:
     label:
       en_US: Base URL
       zh_Hans: StableDiffusion服务器的Base URL
+      pt_BR: Base URL
     placeholder:
       en_US: Please input your StableDiffusion server's Base URL
       zh_Hans: 请输入你的 StableDiffusion 服务器的 Base URL
+      pt_BR: Please input your StableDiffusion server's Base URL
   model:
     type: text-input
     required: true
     label:
       en_US: Model
       zh_Hans: 模型
+      pt_BR: Model
     placeholder:
       en_US: Please input your model
       zh_Hans: 请输入你的模型名称
+      pt_BR: Please input your model

+ 14 - 0
api/core/tools/provider/builtin/stablediffusion/tools/stable_diffusion.yaml

@@ -4,10 +4,12 @@ identity:
   label:
     en_US: Stable Diffusion WebUI
     zh_Hans: Stable Diffusion WebUI
+    pt_BR: Stable Diffusion WebUI
 description:
   human:
     en_US: A tool for generating images which can be deployed locally, you can use stable-diffusion-webui to deploy it.
     zh_Hans: 一个可以在本地部署的图片生成的工具,您可以使用 stable-diffusion-webui 来部署它。
+    pt_BR: A tool for generating images which can be deployed locally, you can use stable-diffusion-webui to deploy it.
   llm: draw the image you want based on your prompt.
 parameters:
   - name: prompt
@@ -16,9 +18,11 @@ parameters:
     label:
       en_US: Prompt
       zh_Hans: 提示词
+      pt_BR: Prompt
     human_description:
       en_US: Image prompt, you can check the official documentation of Stable Diffusion
       zh_Hans: 图像提示词,您可以查看 Stable Diffusion 的官方文档
+      pt_BR: Image prompt, you can check the official documentation of Stable Diffusion
     llm_description: Image prompt of Stable Diffusion, you should describe the image you want to generate as a list of words as possible as detailed, the prompt must be written in English.
     form: llm
   - name: lora
@@ -27,9 +31,11 @@ parameters:
     label:
       en_US: Lora
       zh_Hans: Lora
+      pt_BR: Lora
     human_description:
       en_US: Lora
       zh_Hans: Lora
+      pt_BR: Lora
     form: form
   - name: steps
     type: number
@@ -37,9 +43,11 @@ parameters:
     label:
       en_US: Steps
       zh_Hans: Steps
+      pt_BR: Steps
     human_description:
       en_US: Steps
       zh_Hans: Steps
+      pt_BR: Steps
     form: form
     default: 10
   - name: width
@@ -48,9 +56,11 @@ parameters:
     label:
       en_US: Width
       zh_Hans: Width
+      pt_BR: Width
     human_description:
       en_US: Width
       zh_Hans: Width
+      pt_BR: Width
     form: form
     default: 1024
   - name: height
@@ -59,9 +69,11 @@ parameters:
     label:
       en_US: Height
       zh_Hans: Height
+      pt_BR: Height
     human_description:
       en_US: Height
       zh_Hans: Height
+      pt_BR: Height
     form: form
     default: 1024
   - name: negative_prompt
@@ -70,8 +82,10 @@ parameters:
     label:
       en_US: Negative prompt
       zh_Hans: Negative prompt
+      pt_BR: Negative prompt
     human_description:
       en_US: Negative prompt
       zh_Hans: Negative prompt
+      pt_BR: Negative prompt
     form: form
     default: bad art, ugly, deformed, watermark, duplicated, discontinuous lines

+ 2 - 0
api/core/tools/provider/builtin/time/time.yaml

@@ -4,8 +4,10 @@ identity:
   label:
     en_US: CurrentTime
     zh_Hans: 时间
+    pt_BR: CurrentTime
   description:
     en_US: A tool for getting the current time.
     zh_Hans: 一个用于获取当前时间的工具。
+    pt_BR: A tool for getting the current time.
   icon: icon.svg
 credentails_for_provider:

+ 2 - 0
api/core/tools/provider/builtin/time/tools/current_time.yaml

@@ -4,9 +4,11 @@ identity:
   label:
     en_US: Current Time
     zh_Hans: 获取当前时间
+    pt_BR: Current Time
 description:
   human:
     en_US: A tool for getting the current time.
     zh_Hans: 一个用于获取当前时间的工具。
+    pt_BR: A tool for getting the current time.
   llm: A tool for getting the current time.
 parameters:

+ 6 - 0
api/core/tools/provider/builtin/vectorizer/tools/vectorizer.yaml

@@ -4,10 +4,12 @@ identity:
   label:
     en_US: Vectorizer.AI
     zh_Hans: Vectorizer.AI
+    pt_BR: Vectorizer.AI
 description:
   human:
     en_US: Convert your PNG and JPG images to SVG vectors quickly and easily. Fully automatically. Using AI.
     zh_Hans: 一个将 PNG 和 JPG 图像快速轻松地转换为 SVG 矢量图的工具。
+    pt_BR: Convert your PNG and JPG images to SVG vectors quickly and easily. Fully automatically. Using AI.
   llm: A tool for converting images to SVG vectors. you should input the image id as the input of this tool. the image id can be got from parameters.
 parameters:
   - name: mode
@@ -18,15 +20,19 @@ parameters:
         label:
           en_US: production
           zh_Hans: 生产模式
+          pt_BR: production
       - value: test
         label:
           en_US: test
           zh_Hans: 测试模式
+          pt_BR: test
     default: test
     label:
       en_US: Mode
       zh_Hans: 模式
+      pt_BR: Mode
     human_description:
       en_US: It is free to integrate with and test out the API in test mode, no subscription required.
       zh_Hans: 在测试模式下,可以免费测试API。
+      pt_BR: It is free to integrate with and test out the API in test mode, no subscription required.
     form: form

+ 8 - 0
api/core/tools/provider/builtin/vectorizer/vectorizer.yaml

@@ -4,9 +4,11 @@ identity:
   label:
     en_US: Vectorizer.AI
     zh_Hans: Vectorizer.AI
+    pt_BR: Vectorizer.AI
   description:
     en_US: Convert your PNG and JPG images to SVG vectors quickly and easily. Fully automatically. Using AI.
     zh_Hans: 一个将 PNG 和 JPG 图像快速轻松地转换为 SVG 矢量图的工具。
+    pt_BR: Convert your PNG and JPG images to SVG vectors quickly and easily. Fully automatically. Using AI.
   icon: icon.png
 credentails_for_provider:
   api_key_name:
@@ -15,12 +17,15 @@ credentails_for_provider:
     label:
       en_US: Vectorizer.AI API Key name
       zh_Hans: Vectorizer.AI API Key name
+      pt_BR: Vectorizer.AI API Key name
     placeholder:
       en_US: Please input your Vectorizer.AI ApiKey name
       zh_Hans: 请输入你的 Vectorizer.AI ApiKey name
+      pt_BR: Please input your Vectorizer.AI ApiKey name
     help:
       en_US: Get your Vectorizer.AI API Key from Vectorizer.AI.
       zh_Hans: 从 Vectorizer.AI 获取您的 Vectorizer.AI API Key。
+      pt_BR: Get your Vectorizer.AI API Key from Vectorizer.AI.
     url: https://vectorizer.ai/api
   api_key_value:
     type: secret-input
@@ -28,9 +33,12 @@ credentails_for_provider:
     label:
       en_US: Vectorizer.AI API Key
       zh_Hans: Vectorizer.AI API Key
+      pt_BR: Vectorizer.AI API Key
     placeholder:
       en_US: Please input your Vectorizer.AI ApiKey
       zh_Hans: 请输入你的 Vectorizer.AI ApiKey
+      pt_BR: Please input your Vectorizer.AI ApiKey
     help:
       en_US: Get your Vectorizer.AI API Key from Vectorizer.AI.
       zh_Hans: 从 Vectorizer.AI 获取您的 Vectorizer.AI API Key。
+      pt_BR: Get your Vectorizer.AI API Key from Vectorizer.AI.

+ 6 - 0
api/core/tools/provider/builtin/webscraper/tools/webscraper.yaml

@@ -4,10 +4,12 @@ identity:
   label:
     en_US: Web Scraper
     zh_Hans: 网页爬虫
+    pt_BR: Web Scraper
 description:
   human:
     en_US: A tool for scraping webpages.
     zh_Hans: 一个用于爬取网页的工具。
+    pt_BR: A tool for scraping webpages.
   llm: A tool for scraping webpages. Input should be a URL.
 parameters:
   - name: url
@@ -16,9 +18,11 @@ parameters:
     label:
       en_US: URL
       zh_Hans: 网页链接
+      pt_BR: URL
     human_description:
       en_US: used for linking to webpages
       zh_Hans: 用于链接到网页
+      pt_BR: used for linking to webpages
     llm_description: url for scraping
     form: llm
   - name: user_agent
@@ -27,8 +31,10 @@ parameters:
     label:
       en_US: User Agent
       zh_Hans: User Agent
+      pt_BR: User Agent
     human_description:
       en_US: used for identifying the browser.
       zh_Hans: 用于识别浏览器。
+      pt_BR: used for identifying the browser.
     form: form
     default: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.1000.0 Safari/537.36

+ 2 - 0
api/core/tools/provider/builtin/webscraper/webscraper.yaml

@@ -4,8 +4,10 @@ identity:
   label:
     en_US: WebScraper
     zh_Hans: 网页抓取
+    pt_BR: WebScraper
   description:
     en_US: Web Scrapper tool kit is used to scrape web
     zh_Hans: 一个用于抓取网页的工具。
+    pt_BR: Web Scrapper tool kit is used to scrape web
   icon: icon.svg
 credentails_for_provider:

+ 4 - 0
api/core/tools/provider/builtin/wikipedia/tools/wikipedia_search.yaml

@@ -4,11 +4,13 @@ identity:
   label:
     en_US: WikipediaSearch
     zh_Hans: 维基百科搜索
+    pt_BR: WikipediaSearch
   icon: icon.svg
 description:
   human:
     en_US: A tool for performing a Wikipedia search and extracting snippets and webpages.
     zh_Hans: 一个用于执行维基百科搜索并提取片段和网页的工具。
+    pt_BR: A tool for performing a Wikipedia search and extracting snippets and webpages.
   llm: A tool for performing a Wikipedia search and extracting snippets and webpages. Input should be a search query.
 parameters:
   - name: query
@@ -17,8 +19,10 @@ parameters:
     label:
       en_US: Query string
       zh_Hans: 查询语句
+      pt_BR: Query string
     human_description:
       en_US: key words for searching
       zh_Hans: 查询关键词
+      pt_BR: key words for searching
     llm_description: key words for searching
     form: llm

+ 2 - 0
api/core/tools/provider/builtin/wikipedia/wikipedia.yaml

@@ -4,8 +4,10 @@ identity:
   label:
     en_US: Wikipedia
     zh_Hans: 维基百科
+    pt_BR: Wikipedia
   description:
     en_US: Wikipedia is a free online encyclopedia, created and edited by volunteers around the world.
     zh_Hans: 维基百科是一个由全世界的志愿者创建和编辑的免费在线百科全书。
+    pt_BR: Wikipedia is a free online encyclopedia, created and edited by volunteers around the world.
   icon: icon.svg
 credentails_for_provider:

+ 4 - 0
api/core/tools/provider/builtin/wolframalpha/tools/wolframalpha.yaml

@@ -4,10 +4,12 @@ identity:
   label:
     en_US: WolframAlpha
     zh_Hans: WolframAlpha
+    pt_BR: WolframAlpha
 description:
   human:
     en_US: WolframAlpha is a powerful computational knowledge engine.
     zh_Hans: WolframAlpha 是一个强大的计算知识引擎。
+    pt_BR: WolframAlpha is a powerful computational knowledge engine.
   llm: WolframAlpha is a powerful computational knowledge engine. one single query can get the answer of a question.
 parameters:
   - name: query
@@ -16,8 +18,10 @@ parameters:
     label:
       en_US: Query string
       zh_Hans: 计算语句
+      pt_BR: Query string
     human_description:
       en_US: used for calculating
       zh_Hans: 用于计算最终结果
+      pt_BR: used for calculating
     llm_description: a single query for calculating
     form: llm

+ 5 - 0
api/core/tools/provider/builtin/wolframalpha/wolframalpha.yaml

@@ -4,9 +4,11 @@ identity:
   label:
     en_US: WolframAlpha
     zh_Hans: WolframAlpha
+    pt_BR: WolframAlpha
   description:
     en_US: WolframAlpha is a powerful computational knowledge engine.
     zh_Hans: WolframAlpha 是一个强大的计算知识引擎。
+    pt_BR: WolframAlpha is a powerful computational knowledge engine.
   icon: icon.svg
 credentails_for_provider:
   appid:
@@ -15,10 +17,13 @@ credentails_for_provider:
     label:
       en_US: WolframAlpha AppID
       zh_Hans: WolframAlpha AppID
+      pt_BR: WolframAlpha AppID
     placeholder:
       en_US: Please input your WolframAlpha AppID
       zh_Hans: 请输入你的 WolframAlpha AppID
+      pt_BR: Please input your WolframAlpha AppID
     help:
       en_US: Get your WolframAlpha AppID from WolframAlpha, please use "full results" api access.
       zh_Hans: 从 WolframAlpha 获取您的 WolframAlpha AppID,请使用 "full results" API。
+      pt_BR: Get your WolframAlpha AppID from WolframAlpha, please use "full results" api access.
     url: https://products.wolframalpha.com/api

+ 8 - 0
api/core/tools/provider/builtin/yahoo/tools/analytics.yaml

@@ -4,11 +4,13 @@ identity:
   label:
     en_US: Analytics
     zh_Hans: 分析
+    pt_BR: Análises
   icon: icon.svg
 description:
   human:
     en_US: A tool for get analytics about a ticker from Yahoo Finance.
     zh_Hans: 一个用于从雅虎财经获取分析数据的工具。
+    pt_BR: Uma ferramenta para obter análises sobre um ticker do Yahoo Finance.
   llm: A tool for get analytics from Yahoo Finance. Input should be the ticker symbol like AAPL.
 parameters:
   - name: symbol
@@ -17,9 +19,11 @@ parameters:
     label:
       en_US: Ticker symbol
       zh_Hans: 股票代码
+      pt_BR: Símbolo do ticker
     human_description:
       en_US: The ticker symbol of the company you want to analyze.
       zh_Hans: 你想要搜索的公司的股票代码。
+      pt_BR: O símbolo do ticker da empresa que você deseja analisar.
     llm_description: The ticker symbol of the company you want to analyze.
     form: llm
   - name: start_date
@@ -28,9 +32,11 @@ parameters:
     label:
       en_US: Start date
       zh_Hans: 开始日期
+      pt_BR: Data de início
     human_description:
       en_US: The start date of the analytics.
       zh_Hans: 分析的开始日期。
+      pt_BR: A data de início das análises.
     llm_description: The start date of the analytics, the format of the date must be YYYY-MM-DD like 2020-01-01.
     form: llm
   - name: end_date
@@ -39,8 +45,10 @@ parameters:
     label:
       en_US: End date
       zh_Hans: 结束日期
+      pt_BR: Data de término
     human_description:
       en_US: The end date of the analytics.
       zh_Hans: 分析的结束日期。
+      pt_BR: A data de término das análises.
     llm_description: The end date of the analytics, the format of the date must be YYYY-MM-DD like 2024-01-01.
     form: llm

+ 4 - 0
api/core/tools/provider/builtin/yahoo/tools/news.yaml

@@ -4,11 +4,13 @@ identity:
   label:
     en_US: News
     zh_Hans: 新闻
+    pt_BR: Notícias
   icon: icon.svg
 description:
   human:
     en_US: A tool for get news about a ticker from Yahoo Finance.
     zh_Hans: 一个用于从雅虎财经获取新闻的工具。
+    pt_BR: Uma ferramenta para obter notícias sobre um ticker da Yahoo Finance.
   llm: A tool for get news from Yahoo Finance. Input should be the ticker symbol like AAPL.
 parameters:
   - name: symbol
@@ -17,8 +19,10 @@ parameters:
     label:
       en_US: Ticker symbol
       zh_Hans: 股票代码
+      pt_BR: Símbolo do ticker
     human_description:
       en_US: The ticker symbol of the company you want to search.
       zh_Hans: 你想要搜索的公司的股票代码。
+      pt_BR: O símbolo do ticker da empresa que você deseja pesquisar.
     llm_description: The ticker symbol of the company you want to search.
     form: llm

+ 4 - 0
api/core/tools/provider/builtin/yahoo/tools/ticker.yaml

@@ -4,11 +4,13 @@ identity:
   label:
     en_US: Ticker
     zh_Hans: 股票信息
+    pt_BR: Ticker
   icon: icon.svg
 description:
   human:
     en_US: A tool for search ticker information from Yahoo Finance.
     zh_Hans: 一个用于从雅虎财经搜索股票信息的工具。
+    pt_BR: Uma ferramenta para buscar informações de ticker do Yahoo Finance.
   llm: A tool for search ticker information from Yahoo Finance. Input should be the ticker symbol like AAPL.
 parameters:
   - name: symbol
@@ -17,8 +19,10 @@ parameters:
     label:
       en_US: Ticker symbol
       zh_Hans: 股票代码
+      pt_BR: Símbolo do ticker
     human_description:
       en_US: The ticker symbol of the company you want to search.
       zh_Hans: 你想要搜索的公司的股票代码。
+      pt_BR: O símbolo do ticker da empresa que você deseja pesquisar.
     llm_description: The ticker symbol of the company you want to search.
     form: llm

+ 2 - 0
api/core/tools/provider/builtin/yahoo/yahoo.yaml

@@ -4,8 +4,10 @@ identity:
   label:
     en_US: YahooFinance
     zh_Hans: 雅虎财经
+    pt_BR: YahooFinance
   description:
     en_US: Finance, and Yahoo! get the latest news, stock quotes, and interactive chart with Yahoo!
     zh_Hans: 雅虎财经,获取并整理出最新的新闻、股票报价等一切你想要的财经信息。
+    pt_BR: Finance, and Yahoo! get the latest news, stock quotes, and interactive chart with Yahoo!
   icon: icon.png
 credentails_for_provider:

+ 8 - 0
api/core/tools/provider/builtin/youtube/tools/videos.yaml

@@ -4,11 +4,13 @@ identity:
   label:
     en_US: Video statistics
     zh_Hans: 视频统计
+    pt_BR: Estatísticas de vídeo
   icon: icon.svg
 description:
   human:
     en_US: A tool for get statistics about a channel's videos.
     zh_Hans: 一个用于获取油管频道视频统计数据的工具。
+    pt_BR: Uma ferramenta para obter estatísticas sobre os vídeos de um canal.
   llm: A tool for get statistics about a channel's videos. Input should be the name of the channel like PewDiePie.
 parameters:
   - name: channel
@@ -17,9 +19,11 @@ parameters:
     label:
       en_US: Channel name
       zh_Hans: 频道名
+      pt_BR: Nome do canal
     human_description:
       en_US: The name of the channel you want to search.
       zh_Hans: 你想要搜索的油管频道名。
+      pt_BR: O nome do canal que você deseja pesquisar.
     llm_description: The name of the channel you want to search.
     form: llm
   - name: start_date
@@ -28,9 +32,11 @@ parameters:
     label:
       en_US: Start date
       zh_Hans: 开始日期
+      pt_BR: Data de início
     human_description:
       en_US: The start date of the analytics.
       zh_Hans: 分析的开始日期。
+      pt_BR: A data de início da análise.
     llm_description: The start date of the analytics, the format of the date must be YYYY-MM-DD like 2020-01-01.
     form: llm
   - name: end_date
@@ -39,8 +45,10 @@ parameters:
     label:
       en_US: End date
       zh_Hans: 结束日期
+      pt_BR: Data de término
     human_description:
       en_US: The end date of the analytics.
       zh_Hans: 分析的结束日期。
+      pt_BR: A data de término da análise.
     llm_description: The end date of the analytics, the format of the date must be YYYY-MM-DD like 2024-01-01.
     form: llm

+ 5 - 0
api/core/tools/provider/builtin/youtube/youtube.yaml

@@ -4,9 +4,11 @@ identity:
   label:
     en_US: Youtube
     zh_Hans: Youtube
+    pt_BR: Youtube
   description:
     en_US: Youtube
     zh_Hans: Youtube(油管)是全球最大的视频分享网站,用户可以在上面上传、观看和分享视频。
+    pt_BR: Youtube é o maior site de compartilhamento de vídeos do mundo, onde os usuários podem fazer upload, assistir e compartilhar vídeos.
   icon: icon.png
 credentails_for_provider:
   google_api_key:
@@ -15,10 +17,13 @@ credentails_for_provider:
     label:
       en_US: Google API key
       zh_Hans: Google API key
+      pt_BR: Chave da API do Google
     placeholder:
       en_US: Please input your Google API key
       zh_Hans: 请输入你的 Google API key
+      pt_BR: Insira sua chave da API do Google
     help:
       en_US: Get your Google API key from Google
       zh_Hans: 从 Google 获取您的 Google API key
+      pt_BR: Obtenha sua chave da API do Google no Google
     url: https://console.developers.google.com/apis/credentials

+ 0 - 10
api/libs/helper.py

@@ -113,16 +113,6 @@ def _get_float(value):
     except (TypeError, ValueError):
         raise ValueError('{0} is not a valid float'.format(value))
 
-
-def supported_language(lang):
-    if lang in ['en-US', 'zh-Hans']:
-        return lang
-
-    error = ('{lang} is not a valid language.'
-             .format(lang=lang))
-    raise ValueError(error)
-
-
 def timezone(timezone_string):
     if timezone_string and timezone_string in available_timezones():
         return timezone_string

+ 5 - 6
api/services/account_service.py

@@ -8,6 +8,7 @@ from datetime import datetime, timedelta
 from hashlib import sha256
 from typing import Any, Dict, Optional
 
+from constants.languages import languages, language_timezone_mapping
 from events.tenant_event import tenant_was_created
 from extensions.ext_redis import redis_client
 from flask import current_app, session
@@ -138,7 +139,7 @@ class AccountService:
 
     @staticmethod
     def create_account(email: str, name: str, password: str = None,
-                       interface_language: str = 'en-US', interface_theme: str = 'light',
+                       interface_language: str = languages[0], interface_theme: str = 'light',
                        timezone: str = 'America/New_York', ) -> Account:
         """create account"""
         account = Account()
@@ -159,11 +160,9 @@ class AccountService:
 
         account.interface_language = interface_language
         account.interface_theme = interface_theme
-
-        if interface_language == 'zh-Hans':
-            account.timezone = 'Asia/Shanghai'
-        else:
-            account.timezone = timezone
+        
+        # Set timezone based on language
+        account.timezone = language_timezone_mapping.get(interface_language, 'UTC') 
 
         db.session.add(account)
         db.session.commit()

+ 2 - 1
api/tasks/mail_invite_member_task.py

@@ -5,7 +5,7 @@ import click
 from celery import shared_task
 from extensions.ext_mail import mail
 from flask import current_app, render_template
-
+from constants.languages import languages
 
 @shared_task(queue='mail')
 def send_invite_member_mail_task(language: str, to: str, token: str, inviter_name: str, workspace_name: str):
@@ -26,6 +26,7 @@ def send_invite_member_mail_task(language: str, to: str, token: str, inviter_nam
                              fg='green'))
     start_at = time.perf_counter()
 
+    # TODO send invite member mail using different languages
     try:
         url = f'{current_app.config.get("CONSOLE_WEB_URL")}/activate?token={token}'
         if language == 'zh-Hans':

+ 0 - 6
web/app/(commonLayout)/apps/page.tsx

@@ -14,12 +14,6 @@ const AppList = async () => {
       <footer className='px-12 py-6 grow-0 shrink-0'>
         <h3 className='text-xl font-semibold leading-tight text-gradient'>{t('join')}</h3>
         <p className='mt-1 text-sm font-normal leading-tight text-gray-700'>{t('communityIntro')}</p>
-        {/* <p className='mt-3 text-sm'> */}
-        {/*  <a className='inline-flex items-center gap-1 link' target='_blank' href={`https://docs.dify.ai${locale === 'en' ? '' : '/v/zh-hans'}/community/product-roadmap`}> */}
-        {/*    {t('roadmap')} */}
-        {/*    <span className={style.linkIcon} /> */}
-        {/*  </a> */}
-        {/* </p> */}
         <div className='flex items-center gap-2 mt-3'>
           <a className={style.socialMediaLink} target='_blank' href='https://github.com/langgenius/dify'><span className={classNames(style.socialMediaIcon, style.githubIcon)} /></a>
           <a className={style.socialMediaLink} target='_blank' href='https://discord.gg/FngNHpbcY7'><span className={classNames(style.socialMediaIcon, style.discordIcon)} /></a>

+ 2 - 2
web/app/activate/activateForm.tsx

@@ -12,7 +12,7 @@ import Button from '@/app/components/base/button'
 
 import { SimpleSelect } from '@/app/components/base/select'
 import { timezones } from '@/utils/timezone'
-import { languageMaps, languages } from '@/utils/language'
+import { LanguagesSupported, languages } from '@/utils/language'
 import { activateMember, invitationCheck } from '@/service/common'
 import Toast from '@/app/components/base/toast'
 import Loading from '@/app/components/base/loading'
@@ -45,7 +45,7 @@ const ActivateForm = () => {
   const [timezone, setTimezone] = useState('Asia/Shanghai')
   const [language, setLanguage] = useState('en-US')
   const [showSuccess, setShowSuccess] = useState(false)
-  const defaultLanguage = useCallback(() => (window.navigator.language.startsWith('zh') ? languageMaps['zh-Hans'] : languageMaps.en) || languageMaps.en, [])
+  const defaultLanguage = useCallback(() => (window.navigator.language.startsWith('zh') ? LanguagesSupported[1] : LanguagesSupported[0]) || LanguagesSupported[0], [])
 
   const showErrorMessage = useCallback((message: string) => {
     Toast.notify({

+ 3 - 6
web/app/components/app/overview/settings/index.tsx

@@ -13,6 +13,8 @@ import type { AppDetailResponse } from '@/models/app'
 import type { Language } from '@/types/app'
 import EmojiPicker from '@/app/components/base/emoji-picker'
 
+import { languages } from '@/utils/language'
+
 export type ISettingsModalProps = {
   appInfo: AppDetailResponse
   isShow: boolean
@@ -32,11 +34,6 @@ export type ConfigParams = {
   icon_background: string
 }
 
-const LANGUAGE_MAP: Record<Language, string> = {
-  'en-US': 'English(United States)',
-  'zh-Hans': '简体中文',
-}
-
 const prefixSettings = 'appOverview.overview.appInfo.settings'
 
 const SettingsModal: FC<ISettingsModalProps> = ({
@@ -125,7 +122,7 @@ const SettingsModal: FC<ISettingsModalProps> = ({
         />
         <div className={`mt-6 mb-2 font-medium ${s.settingTitle} text-gray-900 `}>{t(`${prefixSettings}.language`)}</div>
         <SimpleSelect
-          items={Object.keys(LANGUAGE_MAP).map(lang => ({ name: LANGUAGE_MAP[lang as Language], value: lang }))}
+          items={languages}
           defaultValue={language}
           onSelect={item => setLanguage(item.value as Language)}
         />

+ 0 - 2
web/app/components/base/auto-height-textarea/index.tsx

@@ -25,12 +25,10 @@ const AutoHeightTextarea = forwardRef(
 
     const doFocus = () => {
       if (ref.current) {
-        // console.log('focus')
         ref.current.setSelectionRange(value.length, value.length)
         ref.current.focus()
         return true
       }
-      // console.log(autoFocus, 'not focus')
       return false
     }
 

+ 1 - 9
web/app/components/base/select/locale.tsx

@@ -3,14 +3,6 @@ import { Menu, Transition } from '@headlessui/react'
 import { Fragment } from 'react'
 import { GlobeAltIcon } from '@heroicons/react/24/outline'
 
-export const LOCALES = [
-  { value: 'en', name: 'EN' },
-  { value: 'zh-Hans', name: '简体中文' },
-]
-export const RFC_LOCALES = [
-  { value: 'en-US', name: 'EN' },
-  { value: 'zh-Hans', name: '简体中文' },
-]
 type ISelectProps = {
   items: Array<{ value: string; name: string }>
   value?: string
@@ -47,7 +39,7 @@ export default function Select({
           leaveFrom="transform opacity-100 scale-100"
           leaveTo="transform opacity-0 scale-95"
         >
-          <Menu.Items className="absolute right-0 mt-2 w-[120px] origin-top-right divide-y divide-gray-100 rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
+          <Menu.Items className="absolute right-0 mt-2 w-[200px] origin-top-right divide-y divide-gray-100 rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none z-10">
             <div className="px-1 py-1 ">
               {items.map((item) => {
                 return <Menu.Item key={item.value}>

+ 3 - 3
web/app/components/header/account-setting/language-page/index.tsx

@@ -10,7 +10,7 @@ import { updateUserProfile } from '@/service/common'
 import { ToastContext } from '@/app/components/base/toast'
 import I18n from '@/context/i18n'
 import { timezones } from '@/utils/timezone'
-import { languageMaps, languages } from '@/utils/language'
+import { languages } from '@/utils/language'
 
 const titleClassName = `
   mb-2 text-sm font-medium text-gray-900
@@ -28,7 +28,7 @@ export default function LanguagePage() {
     if (type === 'language') {
       url = '/account/interface-language'
       bodyKey = 'interface_language'
-      setLocaleOnClient(item.value === 'en-US' ? 'en' : 'zh-Hans')
+      setLocaleOnClient(item.value.toString())
     }
     if (type === 'timezone') {
       url = '/account/timezone'
@@ -52,7 +52,7 @@ export default function LanguagePage() {
       <div className='mb-8'>
         <div className={titleClassName}>{t('common.language.displayLanguage')}</div>
         <SimpleSelect
-          defaultValue={languageMaps[locale] || userProfile.interface_language}
+          defaultValue={locale || userProfile.interface_language}
           items={languages}
           onSelect={item => handleSelect('language', item)}
           disabled={editing}

+ 1 - 0
web/app/components/header/account-setting/model-provider-page/declarations.ts

@@ -3,6 +3,7 @@ export type FormValue = Record<string, any>
 export type TypeWithI18N<T = string> = {
   'en_US': T
   'zh_Hans': T
+  [key: string]: T
 }
 
 export enum FormTypeEnum {

+ 2 - 3
web/app/components/header/account-setting/model-provider-page/hooks.ts

@@ -16,7 +16,7 @@ import {
   ConfigurateMethodEnum,
   ModelTypeEnum,
 } from './declarations'
-import { languageMaps } from './utils'
+import { getModelRuntimeSupported } from '@/utils/language'
 import I18n from '@/context/i18n'
 import {
   fetchDefaultModal,
@@ -59,8 +59,7 @@ export const useSystemDefaultModelAndModelList: UseDefaultModelAndModelList = (
 
 export const useLanguage = () => {
   const { locale } = useContext(I18n)
-
-  return languageMaps[locale]
+  return getModelRuntimeSupported(locale)
 }
 
 export const useProviderCrenditialsFormSchemasValue = (

+ 0 - 8
web/app/components/header/account-setting/model-provider-page/utils.ts

@@ -15,14 +15,6 @@ import {
   validateModelProvider,
 } from '@/service/common'
 
-export const languageMaps = {
-  'en': 'en_US',
-  'zh-Hans': 'zh_Hans',
-} as {
-  'en': 'en_US'
-  'zh-Hans': 'zh_Hans'
-}
-
 export const MODEL_PROVIDER_QUOTA_GET_FREE = ['minimax', 'spark', 'zhipuai']
 export const MODEL_PROVIDER_QUOTA_GET_PAID = ['anthropic', 'openai', 'azure_openai']
 

+ 6 - 13
web/app/components/header/maintenance-notice.tsx

@@ -2,17 +2,7 @@ import { useState } from 'react'
 import { useContext } from 'use-context-selector'
 import I18n from '@/context/i18n'
 import { X } from '@/app/components/base/icons/src/vender/line/general'
-
-const NOTICE_I18N = {
-  title: {
-    'en': 'Important Notice',
-    'zh-Hans': '重要公告',
-  },
-  desc: {
-    'en': 'Our system will be unavailable from 19:00 to 24:00 UTC on August 28 for an upgrade. For questions, kindly contact our support team (support@dify.ai). We value your patience.',
-    'zh-Hans': '为了有效提升数据检索能力及稳定性,Dify 将于 2023 年 8 月 29 日 03:00 至 08:00 期间进行服务升级,届时 Dify 云端版及应用将无法访问。感谢您的耐心与支持。',
-  },
-}
+import { NOTICE_I18N } from '@/utils/language'
 
 const MaintenanceNotice = () => {
   const { locale } = useContext(I18n)
@@ -23,13 +13,16 @@ const MaintenanceNotice = () => {
     setShowNotice(false)
   }
 
+  const titleByLocale: { [key: string]: string } = NOTICE_I18N.title
+  const descByLocale: { [key: string]: string } = NOTICE_I18N.desc
+
   if (!showNotice)
     return null
 
   return (
     <div className='shrink-0 flex items-center px-4 h-[38px] bg-[#FFFAEB] border-b border-[0.5px] border-b-[#FEF0C7] z-20'>
-      <div className='shrink-0 flex items-center mr-2 px-2 h-[22px] bg-[#F79009] text-white text-[11px] font-medium rounded-xl'>{NOTICE_I18N.title[locale]}</div>
-      <div className='grow text-xs font-medium text-gray-700'>{NOTICE_I18N.desc[locale]}</div>
+      <div className='shrink-0 flex items-center mr-2 px-2 h-[22px] bg-[#F79009] text-white text-[11px] font-medium rounded-xl'>{titleByLocale[locale]}</div>
+      <div className='grow text-xs font-medium text-gray-700'>{descByLocale[locale]}</div>
       <X className='shrink-0 w-4 h-4 text-gray-500 cursor-pointer' onClick={handleCloseNotice} />
     </div>
   )

+ 1 - 1
web/app/components/tools/index.tsx

@@ -138,7 +138,7 @@ const Tools: FC<Props> = ({
           {isInToolsPage && (
             <Button className='mt-6 flex items-center !h-8 pl-4' type='primary' onClick={handleCreateToolCollection}>
               <Plus className='w-4 h-4 mr-1' />
-              <div className='leading-[18px] text-[13px] font-medium'>{t('tools.createCustomTool')}</div>
+              <div className='leading-[18px] text-[13px] font-medium truncate'>{t('tools.createCustomTool')}</div>
             </Button>
           )}
 

+ 3 - 2
web/app/signin/_header.tsx

@@ -1,7 +1,8 @@
 'use client'
 import React from 'react'
 import { useContext } from 'use-context-selector'
-import Select, { LOCALES } from '@/app/components/base/select/locale'
+import Select from '@/app/components/base/select/locale'
+import { languages } from '@/utils/language'
 import { type Locale } from '@/i18n'
 import I18n from '@/context/i18n'
 import LogoSite from '@/app/components/base/logo/logo-site'
@@ -16,7 +17,7 @@ const Header = () => {
     <LogoSite />
     <Select
       value={locale}
-      items={LOCALES}
+      items={languages}
       onChange={(value) => {
         setLocaleOnClient(value as Locale)
       }}

+ 2 - 2
web/app/signin/oneMoreStep.tsx

@@ -10,7 +10,7 @@ import Tooltip from '@/app/components/base/tooltip/index'
 
 import { SimpleSelect } from '@/app/components/base/select'
 import { timezones } from '@/utils/timezone'
-import { languageMaps, languages } from '@/utils/language'
+import { LanguagesSupported, languages } from '@/utils/language'
 import { oneMoreStep } from '@/service/common'
 import Toast from '@/app/components/base/toast'
 import I18n from '@/context/i18n'
@@ -121,7 +121,7 @@ const OneMoreStep = () => {
             </label>
             <div className="relative mt-1 rounded-md shadow-sm">
               <SimpleSelect
-                defaultValue={languageMaps.en}
+                defaultValue={LanguagesSupported[0]}
                 items={languages}
                 onSelect={(item) => {
                   dispatch({ type: 'interface_language', value: item.value })

+ 48 - 2
web/i18n/i18next-config.ts

@@ -3,47 +3,67 @@ import i18n from 'i18next'
 import { initReactI18next } from 'react-i18next'
 import commonEn from './lang/common.en'
 import commonZh from './lang/common.zh'
+import commonPt from './lang/common.pt' // Portuguese import
 import loginEn from './lang/login.en'
 import loginZh from './lang/login.zh'
+import loginPt from './lang/login.pt' // Portuguese import
 import registerEn from './lang/register.en'
 import registerZh from './lang/register.zh'
+import registerPt from './lang/register.pt' // Portuguese import
 import layoutEn from './lang/layout.en'
 import layoutZh from './lang/layout.zh'
+import layoutPt from './lang/layout.pt' // Portuguese import
 import appEn from './lang/app.en'
 import appZh from './lang/app.zh'
+import appPt from './lang/app.pt' // Portuguese import
 import appOverviewEn from './lang/app-overview.en'
 import appOverviewZh from './lang/app-overview.zh'
+import appOverviewPt from './lang/app-overview.pt' // Portuguese import
 import appDebugEn from './lang/app-debug.en'
 import appDebugZh from './lang/app-debug.zh'
+import appDebugPt from './lang/app-debug.pt' // Portuguese import
 import appApiEn from './lang/app-api.en'
 import appApiZh from './lang/app-api.zh'
+import appApiPt from './lang/app-api.pt' // Portuguese import
 import appLogEn from './lang/app-log.en'
 import appLogZh from './lang/app-log.zh'
+import appLogPt from './lang/app-log.pt' // Portuguese import
 import appAnnotationEn from './lang/app-annotation.en'
 import appAnnotationZh from './lang/app-annotation.zh'
+import appAnnotationPt from './lang/app-annotation.pt' // Portuguese import
 import shareEn from './lang/share-app.en'
 import shareZh from './lang/share-app.zh'
+import sharePt from './lang/share-app.pt' // Portuguese import
 import datasetEn from './lang/dataset.en'
 import datasetZh from './lang/dataset.zh'
+import datasetPt from './lang/dataset.pt' // Portuguese import
 import datasetDocumentsEn from './lang/dataset-documents.en'
 import datasetDocumentsZh from './lang/dataset-documents.zh'
+import datasetDocumentsPt from './lang/dataset-documents.pt' // Portuguese import
 import datasetHitTestingEn from './lang/dataset-hit-testing.en'
 import datasetHitTestingZh from './lang/dataset-hit-testing.zh'
+import datasetHitTestingPt from './lang/dataset-hit-testing.pt' // Portuguese import
 import datasetSettingsEn from './lang/dataset-settings.en'
 import datasetSettingsZh from './lang/dataset-settings.zh'
+import datasetSettingsPt from './lang/dataset-settings.pt' // Portuguese import
 import datasetCreationEn from './lang/dataset-creation.en'
 import datasetCreationZh from './lang/dataset-creation.zh'
+import datasetCreationPt from './lang/dataset-creation.pt' // Portuguese import
 import exploreEn from './lang/explore.en'
 import exploreZh from './lang/explore.zh'
+import explorePt from './lang/explore.pt' // Portuguese import
 import billingEn from './lang/billing.en'
 import billingZh from './lang/billing.zh'
+import billingPt from './lang/billing.pt' // Portuguese import
 import customEn from './lang/custom.en'
 import customZh from './lang/custom.zh'
+import customPt from './lang/custom.pt' // Portuguese import
 import toolsEn from './lang/tools.en'
 import toolsZh from './lang/tools.zh'
+import toolsPt from './lang/tools.pt' // Portuguese import
 
 const resources = {
-  'en': {
+  'en-US': {
     translation: {
       common: commonEn,
       layout: layoutEn, // page layout
@@ -98,6 +118,32 @@ const resources = {
       tools: toolsZh,
     },
   },
+  'pt-BR': {
+    translation: {
+      common: commonPt,
+      layout: layoutPt,
+      login: loginPt,
+      register: registerPt,
+      // app
+      app: appPt,
+      appOverview: appOverviewPt,
+      appDebug: appDebugPt,
+      appApi: appApiPt,
+      appLog: appLogPt,
+      appAnnotation: appAnnotationPt,
+      // share
+      share: sharePt,
+      dataset: datasetPt,
+      datasetDocuments: datasetDocumentsPt,
+      datasetHitTesting: datasetHitTestingPt,
+      datasetSettings: datasetSettingsPt,
+      datasetCreation: datasetCreationPt,
+      explore: explorePt,
+      billing: billingPt,
+      custom: customPt,
+      tools: toolsPt,
+    },
+  },
 }
 
 i18n.use(initReactI18next)
@@ -105,7 +151,7 @@ i18n.use(initReactI18next)
   // for all options read: https://www.i18next.com/overview/configuration-options
   .init({
     lng: undefined,
-    fallbackLng: 'en',
+    fallbackLng: 'en-US',
     // debug: true,
     resources,
   })

+ 3 - 1
web/i18n/index.ts

@@ -1,6 +1,8 @@
+import { LanguagesSupported } from '@/utils/language'
+
 export const i18n = {
   defaultLocale: 'en',
-  locales: ['en', 'zh-Hans'],
+  locales: LanguagesSupported,
 } as const
 
 export type Locale = typeof i18n['locales'][number]

+ 87 - 0
web/i18n/lang/app-annotation.pt.ts

@@ -0,0 +1,87 @@
+const translation = {
+  title: 'Anotações',
+  name: 'Resposta de Anotação',
+  editBy: 'Resposta editada por {{author}}',
+  noData: {
+    title: 'Sem anotações',
+    description: 'Você pode editar anotações no depuração do aplicativo ou importar anotações em massa aqui para obter uma resposta de alta qualidade.',
+  },
+  table: {
+    header: {
+      question: 'pergunta',
+      answer: 'resposta',
+      createdAt: 'criado em',
+      hits: 'acessos',
+      actions: 'ações',
+      addAnnotation: 'Adicionar Anotação',
+      bulkImport: 'Importação em Massa',
+      bulkExport: 'Exportação em Massa',
+      clearAll: 'Limpar Todas as Anotações',
+    },
+  },
+  editModal: {
+    title: 'Editar Resposta de Anotação',
+    queryName: 'Consulta do Usuário',
+    answerName: 'Bot Contador de Histórias',
+    yourAnswer: 'Sua Resposta',
+    answerPlaceholder: 'Digite sua resposta aqui',
+    yourQuery: 'Sua Consulta',
+    queryPlaceholder: 'Digite sua consulta aqui',
+    removeThisCache: 'Remover esta Anotação',
+    createdAt: 'Criado em',
+  },
+  addModal: {
+    title: 'Adicionar Resposta de Anotação',
+    queryName: 'Pergunta',
+    answerName: 'Resposta',
+    answerPlaceholder: 'Digite a resposta aqui',
+    queryPlaceholder: 'Digite a pergunta aqui',
+    createNext: 'Adicionar outra resposta anotada',
+  },
+  batchModal: {
+    title: 'Importação em Massa',
+    csvUploadTitle: 'Arraste e solte seu arquivo CSV aqui, ou ',
+    browse: 'navegue',
+    tip: 'O arquivo CSV deve seguir a seguinte estrutura:',
+    question: 'pergunta',
+    answer: 'resposta',
+    contentTitle: 'conteúdo do fragmento',
+    content: 'conteúdo',
+    template: 'Baixe o modelo aqui',
+    cancel: 'Cancelar',
+    run: 'Executar em Lote',
+    runError: 'Falha na execução em lote',
+    processing: 'Processando em lote',
+    completed: 'Importação concluída',
+    error: 'Erro na importação',
+    ok: 'OK',
+  },
+  errorMessage: {
+    answerRequired: 'A resposta é obrigatória',
+    queryRequired: 'A pergunta é obrigatória',
+  },
+  viewModal: {
+    annotatedResponse: 'Resposta de Anotação',
+    hitHistory: 'Histórico de Acessos',
+    hit: 'Acesso',
+    hits: 'Acessos',
+    noHitHistory: 'Nenhum histórico de acesso',
+  },
+  hitHistoryTable: {
+    query: 'Consulta',
+    match: 'Correspondência',
+    response: 'Resposta',
+    source: 'Origem',
+    score: 'Pontuação',
+    time: 'Tempo',
+  },
+  initSetup: {
+    title: 'Configuração Inicial da Resposta de Anotação',
+    configTitle: 'Configuração da Resposta de Anotação',
+    confirmBtn: 'Salvar e Habilitar',
+    configConfirmBtn: 'Salvar',
+  },
+  embeddingModelSwitchTip: 'Modelo de vetorização de texto de anotação, a troca de modelos será refeita, resultando em custos adicionais.',
+}
+
+export default translation

+ 79 - 0
web/i18n/lang/app-api.pt.ts

@@ -0,0 +1,79 @@
+const translation = {
+  apiServer: 'Servidor da API',
+  apiKey: 'Chave da API',
+  status: 'Status',
+  disabled: 'Desativado',
+  ok: 'Em Serviço',
+  copy: 'Copiar',
+  copied: 'Copiado',
+  merMaind: {
+    rerender: 'Refazer Rerender',
+  },
+  never: 'Nunca',
+  apiKeyModal: {
+    apiSecretKey: 'Chave Secreta da API',
+    apiSecretKeyTips: 'Para evitar abuso da API, proteja sua Chave da API. Evite usá-la como texto simples no código front-end. :)',
+    createNewSecretKey: 'Criar nova Chave Secreta',
+    secretKey: 'Chave Secreta',
+    created: 'CRIADA',
+    lastUsed: 'ÚLTIMO USO',
+    generateTips: 'Mantenha esta chave em um local seguro e acessível.',
+  },
+  actionMsg: {
+    deleteConfirmTitle: 'Excluir esta chave secreta?',
+    deleteConfirmTips: 'Esta ação não pode ser desfeita.',
+    ok: 'OK',
+  },
+  completionMode: {
+    title: 'Completar App API',
+    info: 'Para geração de texto de alta qualidade, como artigos, resumos e traduções, use a API de mensagens de conclusão com entrada do usuário. A geração de texto depende dos parâmetros do modelo e dos modelos de prompt definidos no Dify Prompt Engineering.',
+    createCompletionApi: 'Criar Mensagem de Conclusão',
+    createCompletionApiTip: 'Crie uma Mensagem de Conclusão para suportar o modo pergunta e resposta.',
+    inputsTips: '(Opcional) Forneça campos de entrada do usuário como pares chave-valor, correspondendo a variáveis no Prompt Eng. A chave é o nome da variável, o valor é o valor do parâmetro. Se o tipo do campo for Select, o Valor enviado deve ser uma das opções predefinidas.',
+    queryTips: 'Conteúdo de texto de entrada do usuário.',
+    blocking: 'Tipo de bloqueio, aguardando a conclusão da execução e retornando os resultados. (As solicitações podem ser interrompidas se o processo for longo)',
+    streaming: 'Retorno de streaming. Implementação de retorno de streaming com base em SSE (Server-Sent Events).',
+    messageFeedbackApi: 'Feedback de mensagem (curtir)',
+    messageFeedbackApiTip: 'Avalie as mensagens recebidas em nome dos usuários finais com curtidas ou descurtidas. Esses dados são visíveis na página de Logs e Anotações e são usados para ajustes futuros no modelo.',
+    messageIDTip: 'ID da mensagem',
+    ratingTip: 'curtir ou descurtir, null desfaz',
+    parametersApi: 'Obter informações de parâmetros do aplicativo',
+    parametersApiTip: 'Recupere os parâmetros de entrada configurados, incluindo nomes de variáveis, nomes de campos, tipos e valores padrão. Geralmente usado para exibir esses campos em um formulário ou preencher valores padrão após o carregamento do cliente.',
+  },
+  chatMode: {
+    title: 'Chat App API',
+    info: 'Para aplicativos de conversação versáteis usando um formato de pergunta e resposta, chame a API de mensagens de chat para iniciar o diálogo. Mantenha conversas em andamento passando o conversation_id retornado. Os parâmetros de resposta e modelos dependem das configurações do Dify Prompt Eng.',
+    createChatApi: 'Criar mensagem de chat',
+    createChatApiTip: 'Crie uma nova mensagem de conversa ou continue um diálogo existente.',
+    inputsTips: '(Opcional) Forneça campos de entrada do usuário como pares chave-valor, correspondendo a variáveis no Prompt Eng. A chave é o nome da variável, o valor é o valor do parâmetro. Se o tipo do campo for Select, o Valor enviado deve ser uma das opções predefinidas.',
+    queryTips: 'Conteúdo de entrada/pergunta do usuário',
+    blocking: 'Tipo de bloqueio, aguardando a conclusão da execução e retornando os resultados. (As solicitações podem ser interrompidas se o processo for longo)',
+    streaming: 'Retorno de streaming. Implementação de retorno de streaming com base em SSE (Server-Sent Events).',
+    conversationIdTip: '(Opcional) ID da conversa: deixe vazio para a primeira conversa; passe conversation_id do contexto para continuar o diálogo.',
+    messageFeedbackApi: 'Feedback do usuário final da mensagem, curtir',
+    messageFeedbackApiTip: 'Avalie as mensagens recebidas em nome dos usuários finais com curtidas ou descurtidas. Esses dados são visíveis na página de Logs e Anotações e são usados para ajustes futuros no modelo.',
+    messageIDTip: 'ID da mensagem',
+    ratingTip: 'curtir ou descurtir, null desfaz',
+    chatMsgHistoryApi: 'Obter histórico de mensagens de chat',
+    chatMsgHistoryApiTip: 'A primeira página retorna as últimas `limit` mensagens, em ordem reversa.',
+    chatMsgHistoryConversationIdTip: 'ID da conversa',
+    chatMsgHistoryFirstId: 'ID do primeiro registro de chat na página atual. O padrão é nenhum.',
+    chatMsgHistoryLimit: 'Quantos chats são retornados em uma solicitação',
+    conversationsListApi: 'Obter lista de conversas',
+    conversationsListApiTip: 'Obtém a lista de sessões do usuário atual. Por padrão, as últimas 20 sessões são retornadas.',
+    conversationsListFirstIdTip: 'O ID do último registro na página atual, padrão nenhum.',
+    conversationsListLimitTip: 'Quantos chats são retornados em uma solicitação',
+    conversationRenamingApi: 'Renomear conversa',
+    conversationRenamingApiTip: 'Renomeie conversas; o nome é exibido nas interfaces de cliente com várias sessões.',
+    conversationRenamingNameTip: 'Novo nome',
+    parametersApi: 'Obter informações de parâmetros do aplicativo',
+    parametersApiTip: 'Recupere os parâmetros de entrada configurados, incluindo nomes de variáveis, nomes de campos, tipos e valores padrão. Geralmente usado para exibir esses campos em um formulário ou preencher valores padrão após o carregamento do cliente.',
+  },
+  develop: {
+    requestBody: 'Corpo da Solicitação',
+    pathParams: 'Parâmetros de Caminho',
+    query: 'Consulta',
+  },
+}
+
+export default translation

+ 388 - 0
web/i18n/lang/app-debug.pt.ts

@@ -0,0 +1,388 @@
+const translation = {
+  pageTitle: {
+    line1: 'PROMPT',
+    line2: 'Engenharia',
+  },
+  orchestrate: 'Orquestrar',
+  promptMode: {
+    simple: 'Mudar para o Modo Especialista para editar todo o PROMPT',
+    advanced: 'Modo Especialista',
+    switchBack: 'Voltar',
+    advancedWarning: {
+      title: 'Você mudou para o Modo Especialista e, uma vez que você modifique o PROMPT, NÃO poderá retornar ao modo básico.',
+      description: 'No Modo Especialista, você pode editar todo o PROMPT.',
+      learnMore: 'Saiba mais',
+      ok: 'OK',
+    },
+    operation: {
+      addMessage: 'Adicionar Mensagem',
+    },
+    contextMissing: 'Componente de contexto ausente, a eficácia do prompt pode não ser boa.',
+  },
+  operation: {
+    applyConfig: 'Publicar',
+    resetConfig: 'Redefinir',
+    debugConfig: 'Depurar',
+    addFeature: 'Adicionar Recurso',
+    automatic: 'Automático',
+    stopResponding: 'Parar de responder',
+    agree: 'gostar',
+    disagree: 'não gostar',
+    cancelAgree: 'Cancelar gostar',
+    cancelDisagree: 'Cancelar não gostar',
+    userAction: 'Usuário ',
+  },
+  notSetAPIKey: {
+    title: 'A chave do provedor LLM não foi definida',
+    trailFinished: 'Trilha finalizada',
+    description: 'A chave do provedor LLM não foi definida e precisa ser definida antes da depuração.',
+    settingBtn: 'Ir para configurações',
+  },
+  trailUseGPT4Info: {
+    title: 'Não suporta gpt-4 agora',
+    description: 'Use gpt-4, por favor defina a chave da API.',
+  },
+  feature: {
+    groupChat: {
+      title: 'Melhoria do Chat',
+      description: 'Adicione configurações pré-conversa para aplicativos que podem melhorar a experiência do usuário.',
+    },
+    groupExperience: {
+      title: 'Melhoria da Experiência',
+    },
+    conversationOpener: {
+      title: 'Remodeladores de Conversa',
+      description: 'Em um aplicativo de chat, a primeira frase que a IA fala ativamente para o usuário geralmente é usada como uma saudação.',
+    },
+    suggestedQuestionsAfterAnswer: {
+      title: 'Perguntas de Acompanhamento',
+      description: 'Configurar sugestões de próximas perguntas pode proporcionar um melhor chat aos usuários.',
+      resDes: '3 sugestões para a próxima pergunta do usuário.',
+      tryToAsk: 'Tente perguntar',
+    },
+    moreLikeThis: {
+      title: 'Mais como isso',
+      description: 'Gere vários textos de uma vez e, em seguida, edite e continue a gerar',
+      generateNumTip: 'Número de vezes geradas',
+      tip: 'Usar esse recurso incorrerá em sobrecarga adicional de tokens',
+    },
+    speechToText: {
+      title: 'Fala para Texto',
+      description: 'Uma vez ativado, você pode usar entrada de voz.',
+      resDes: 'Entrada de voz está ativada',
+    },
+    citation: {
+      title: 'Citações e Atribuições',
+      description: 'Uma vez ativado, mostra o documento de origem e a seção atribuída do conteúdo gerado.',
+      resDes: 'Citações e Atribuições estão ativadas',
+    },
+    annotation: {
+      title: 'Resposta de Anotação',
+      description: 'Você pode adicionar manualmente uma resposta de alta qualidade ao cache para correspondência prioritária com perguntas semelhantes do usuário.',
+      resDes: 'Resposta de Anotação está ativada',
+      scoreThreshold: {
+        title: 'Limiar de Pontuação',
+        description: 'Usado para definir o limiar de similaridade para resposta de anotação.',
+        easyMatch: 'Correspondência Fácil',
+        accurateMatch: 'Correspondência Precisa',
+      },
+      matchVariable: {
+        title: 'Variável de Correspondência',
+        choosePlaceholder: 'Escolha a variável de correspondência',
+      },
+      cacheManagement: 'Anotações',
+      cached: 'Anotado',
+      remove: 'Remover',
+      removeConfirm: 'Excluir esta anotação?',
+      add: 'Adicionar anotação',
+      edit: 'Editar anotação',
+    },
+    dataSet: {
+      title: 'Contexto',
+      noData: 'Você pode importar Conhecimento como contexto',
+      words: 'Palavras',
+      textBlocks: 'Blocos de Texto',
+      selectTitle: 'Selecionar Conhecimento de referência',
+      selected: 'Conhecimento selecionado',
+      noDataSet: 'Nenhum Conhecimento encontrado',
+      toCreate: 'Ir para criar',
+      notSupportSelectMulti: 'Atualmente, suporta apenas um Conhecimento',
+      queryVariable: {
+        title: 'Variável de Consulta',
+        tip: 'Essa variável será usada como entrada de consulta para recuperação de contexto, obtendo informações de contexto relacionadas à entrada dessa variável.',
+        choosePlaceholder: 'Escolha a variável de consulta',
+        noVar: 'Nenhuma variável',
+        noVarTip: 'por favor, crie uma variável na seção Variáveis',
+        unableToQueryDataSet: 'Não é possível consultar o Conhecimento',
+        unableToQueryDataSetTip: 'Não é possível consultar o Conhecimento com sucesso, por favor escolha uma variável de consulta de contexto na seção de contexto.',
+        ok: 'OK',
+        contextVarNotEmpty: 'variável de consulta de contexto não pode estar vazia',
+        deleteContextVarTitle: 'Excluir variável "{{varName}}"?',
+        deleteContextVarTip: 'Esta variável foi definida como uma variável de consulta de contexto e removê-la afetará o uso normal do Conhecimento. Se você ainda precisa excluí-la, por favor, selecione-a novamente na seção de contexto.',
+      },
+    },
+    tools: {
+      title: 'Tools',
+      tips: 'Tools provide a standard API call method, taking user input or variables as request parameters for querying external data as context.',
+      toolsInUse: '{{count}} tools in use',
+      modal: {
+        title: 'Tool',
+        toolType: {
+          title: 'Tool Type',
+          placeholder: 'Por favor, selecione o tipo de ferramenta',
+        },
+        name: {
+          title: 'Nome',
+          placeholder: 'Por favor, insira o nome',
+        },
+        variableName: {
+          title: 'Nome da Variável',
+          placeholder: 'Por favor, insira o nome da variável',
+        },
+      },
+    },
+    conversationHistory: {
+      title: 'Histórico da Conversa',
+      description: 'Defina os nomes dos prefixos para os papéis da conversa',
+      tip: 'O Histórico da Conversa não está habilitado, por favor adicione <histories> na solicitação acima.',
+      learnMore: 'Saiba mais',
+      editModal: {
+        title: 'Editar Nomes dos Papéis da Conversa',
+        userPrefix: 'Prefixo do Usuário',
+        assistantPrefix: 'Prefixo do Assistente',
+      },
+    },
+    toolbox: {
+      title: 'CAIXA DE FERRAMENTAS',
+    },
+    moderation: {
+      title: 'Moderação de Conteúdo',
+      description: 'Proteja a saída do modelo usando a API de moderação ou mantendo uma lista de palavras sensíveis.',
+      allEnabled: 'Conteúdo de ENTRADA/SAÍDA Habilitado',
+      inputEnabled: 'Conteúdo de ENTRADA Habilitado',
+      outputEnabled: 'Conteúdo de SAÍDA Habilitado',
+      modal: {
+        title: 'Configurações de Moderação de Conteúdo',
+        provider: {
+          title: 'Provedor',
+          openai: 'Moderação OpenAI',
+          openaiTip: {
+            prefix: 'A Moderação OpenAI requer uma chave de API da OpenAI configurada em ',
+            suffix: '.',
+          },
+          keywords: 'Palavras-chave',
+        },
+        keywords: {
+          tip: 'Uma por linha, separadas por quebras de linha. Até 100 caracteres por linha.',
+          placeholder: 'Uma por linha, separadas por quebras de linha',
+          line: 'Linha',
+        },
+        content: {
+          input: 'Moderar Conteúdo de ENTRADA',
+          output: 'Moderar Conteúdo de SAÍDA',
+          preset: 'Respostas pré-definidas',
+          placeholder: 'Insira o conteúdo das respostas pré-definidas aqui',
+          condition: 'Moderar Conteúdo de ENTRADA e SAÍDA habilitado pelo menos uma',
+          fromApi: 'As respostas pré-definidas são retornadas pela API',
+          errorMessage: 'As respostas pré-definidas não podem estar vazias',
+          supportMarkdown: 'Suporte a Markdown',
+        },
+        openaiNotConfig: {
+          before: 'A Moderação OpenAI requer uma chave de API da OpenAI configurada em ',
+          after: '',
+        },
+      },
+    },
+  },
+  automatic: {
+    title: 'Orquestração Automatizada de Aplicativos',
+    description: 'Descreva o seu cenário, o Dify irá orquestrar um aplicativo para você.',
+    intendedAudience: 'Qual é o público-alvo?',
+    intendedAudiencePlaceHolder: 'ex: Estudante',
+    solveProblem: 'Quais problemas eles esperam que a IA possa resolver para eles?',
+    solveProblemPlaceHolder: 'ex: Avaliar o desempenho acadêmico',
+    generate: 'Gerar',
+    audiencesRequired: 'Públicos-alvo necessários',
+    problemRequired: 'Problema necessário',
+    resTitle: 'Orquestramos o seguinte aplicativo para você.',
+    apply: 'Aplicar esta orquestração',
+    noData: 'Descreva o seu caso de uso à esquerda, a visualização da orquestração será exibida aqui.',
+    loading: 'Orquestrando o aplicativo para você...',
+    overwriteTitle: 'Substituir configuração existente?',
+    overwriteMessage: 'Aplicar esta orquestração irá substituir a configuração existente.',
+  },
+  resetConfig: {
+    title: 'Confirmar redefinição?',
+    message:
+      'A redefinição descarta as alterações, restaurando a última configuração publicada.',
+  },
+  errorMessage: {
+    nameOfKeyRequired: 'nome da chave: {{key}} obrigatório',
+    valueOfVarRequired: 'valor de {{key}} não pode estar vazio',
+    queryRequired: 'Texto da solicitação é obrigatório.',
+    waitForResponse:
+      'Aguarde a resposta à mensagem anterior ser concluída.',
+    waitForBatchResponse:
+      'Aguarde a resposta à tarefa em lote ser concluída.',
+    notSelectModel: 'Por favor, escolha um modelo',
+    waitForImgUpload: 'Aguarde o upload da imagem',
+  },
+  chatSubTitle: 'Instruções',
+  completionSubTitle: 'Prefixo da Solicitação',
+  promptTip:
+    'As solicitações guiam as respostas da IA com instruções e restrições. Insira variáveis como {{input}}. Este prompt não será visível para os usuários.',
+  formattingChangedTitle: 'Formatação alterada',
+  formattingChangedText:
+    'Modificar a formatação redefinirá a área de depuração, você tem certeza?',
+  variableTitle: 'Variáveis',
+  variableTip:
+    'Os usuários preenchem as variáveis em um formulário, substituindo automaticamente as variáveis na solicitação.',
+  notSetVar: 'As variáveis permitem que os usuários introduzam palavras de solicitação ou observações iniciais ao preencher formulários. Você pode tentar digitar "{{input}}" nas palavras de solicitação.',
+  autoAddVar: 'Variáveis indefinidas referenciadas na pré-solicitação, você deseja adicioná-las no formulário de entrada do usuário?',
+  variableTable: {
+    key: 'Chave da Variável',
+    name: 'Nome do Campo de Entrada do Usuário',
+    optional: 'Opcional',
+    type: 'Tipo de Entrada',
+    action: 'Ações',
+    typeString: 'Texto',
+    typeSelect: 'Selecionar',
+  },
+  varKeyError: {
+    canNoBeEmpty: 'A chave da variável não pode estar vazia',
+    tooLong: 'A chave da variável: {{key}} é muito longa. Não pode ter mais de 30 caracteres',
+    notValid: 'A chave da variável: {{key}} é inválida. Pode conter apenas letras, números e sublinhados',
+    notStartWithNumber: 'A chave da variável: {{key}} não pode começar com um número',
+    keyAlreadyExists: 'A chave da variável: :{{key}} já existe',
+  },
+  otherError: {
+    promptNoBeEmpty: 'A solicitação não pode estar vazia',
+    historyNoBeEmpty: 'O histórico da conversa deve ser definido na solicitação',
+    queryNoBeEmpty: 'A consulta deve ser definida na solicitação',
+  },
+  variableConig: {
+    modalTitle: 'Configurações do Campo',
+    description: 'Configuração para a variável {{varName}}',
+    fieldType: 'Tipo de Campo',
+    string: 'Texto Curto',
+    paragraph: 'Parágrafo',
+    select: 'Selecionar',
+    notSet: 'Não definido, tente digitar {{input}} na solicitação',
+    stringTitle: 'Opções da Caixa de Texto do Formulário',
+    maxLength: 'Comprimento Máximo',
+    options: 'Opções',
+    addOption: 'Adicionar opção',
+    apiBasedVar: 'Variável Baseada em API',
+  },
+  vision: {
+    name: 'Visão',
+    description: 'Habilitar a Visão permite que o modelo receba imagens e responda perguntas sobre elas.',
+    settings: 'Configurações',
+    visionSettings: {
+      title: 'Configurações de Visão',
+      resolution: 'Resolução',
+      resolutionTooltip: `Baixa resolução permitirá que o modelo receba uma versão de baixa resolução de 512 x 512 da imagem e represente a imagem com um orçamento de 65 tokens. Isso permite que a API retorne respostas mais rápidas e consuma menos tokens de entrada para casos de uso que não exigem alta precisão.
+                \n
+                Alta resolução permitirá que o modelo veja a imagem de baixa resolução e crie recortes detalhados das imagens de entrada como quadrados de 512px com base no tamanho da imagem de entrada. Cada um dos recortes detalhados usa o dobro do orçamento de tokens, totalizando 129 tokens.`,
+      high: 'Alta',
+      low: 'Baixa',
+      uploadMethod: 'Método de Upload',
+      both: 'Ambos',
+      localUpload: 'Upload Local',
+      url: 'URL',
+      uploadLimit: 'Limite de Upload',
+    },
+  },
+  openingStatement: {
+    title: 'Abertura da Conversa',
+    add: 'Adicionar',
+    writeOpner: 'Escrever abertura',
+    placeholder: 'Escreva sua mensagem de abertura aqui, você pode usar variáveis, tente digitar {{variável}}.',
+    openingQuestion: 'Perguntas de Abertura',
+    noDataPlaceHolder:
+      'Iniciar a conversa com o usuário pode ajudar a IA a estabelecer uma conexão mais próxima com eles em aplicativos de conversação.',
+    varTip: 'Você pode usar variáveis, tente digitar {{variável}}',
+    tooShort: 'São necessárias pelo menos 20 palavras de prompt inicial para gerar observações de abertura para a conversa.',
+    notIncludeKey: 'O prompt inicial não inclui a variável: {{key}}. Por favor, adicione-a ao prompt inicial.',
+  },
+  modelConfig: {
+    model: 'Modelo',
+    setTone: 'Definir tom das respostas',
+    title: 'Modelo e Parâmetros',
+    modeType: {
+      chat: 'Chat',
+      completion: 'Completar',
+    },
+  },
+  inputs: {
+    title: 'Depuração e Visualização',
+    noPrompt: 'Tente escrever algum prompt na entrada de pré-prompt',
+    userInputField: 'Campo de Entrada do Usuário',
+    noVar: 'Preencha o valor da variável, que será substituída automaticamente na palavra de solicitação sempre que uma nova sessão for iniciada.',
+    chatVarTip:
+      'Preencha o valor da variável, que será substituída automaticamente na palavra de solicitação sempre que uma nova sessão for iniciada',
+    completionVarTip:
+      'Preencha o valor da variável, que será substituída automaticamente nas palavras de solicitação sempre que uma pergunta for enviada.',
+    previewTitle: 'Visualização do Prompt',
+    queryTitle: 'Conteúdo da Consulta',
+    queryPlaceholder: 'Por favor, insira o texto da solicitação.',
+    run: 'EXECUTAR',
+  },
+  result: 'Texto de Saída',
+  datasetConfig: {
+    settingTitle: 'Configurações de Recuperação',
+    retrieveOneWay: {
+      title: 'Recuperação N-para-1',
+      description: 'Com base na intenção do usuário e nas descrições do Conhecimento, o Agente seleciona autonomamente o melhor Conhecimento para consulta. Melhor para aplicativos com Conhecimento distinto e limitado.',
+    },
+    retrieveMultiWay: {
+      title: 'Recuperação Multi-caminho',
+      description: 'Com base na intenção do usuário, consulta todos os Conhecimentos, recupera texto relevante de várias fontes e seleciona os melhores resultados que correspondem à consulta do usuário após a reclassificação. É necessária a configuração da API do modelo de reclassificação.',
+    },
+    rerankModelRequired: 'Modelo de reclassificação é necessário',
+    params: 'Parâmetros',
+    top_k: 'Top K',
+    top_kTip: 'Usado para filtrar os trechos mais semelhantes às perguntas do usuário. O sistema também ajustará dinamicamente o valor de Top K, de acordo com max_tokens do modelo selecionado.',
+    score_threshold: 'Limiar de Pontuação',
+    score_thresholdTip: 'Usado para definir o limiar de similaridade para filtragem de trechos.',
+    retrieveChangeTip: 'Modificar o modo de índice e o modo de recuperação pode afetar os aplicativos associados a este Conhecimento.',
+  },
+  assistantType: {
+    name: 'Tipo de Assistente',
+    chatAssistant: {
+      name: 'Assistente Básico',
+      description: 'Construa um assistente baseado em chat usando um Modelo de Linguagem Grande',
+    },
+    agentAssistant: {
+      name: 'Assistente de Agente',
+      description: 'Construa um Agente inteligente que pode escolher autonomamente ferramentas para concluir as tarefas',
+    },
+  },
+  agent: {
+    agentMode: 'Modo do Agente',
+    agentModeDes: 'Defina o tipo de modo de inferência para o agente',
+    agentModeType: {
+      ReACT: 'ReAct',
+      functionCall: 'Chamada de Função',
+    },
+    setting: {
+      name: 'Configurações do Agente',
+      description: 'As configurações do Assistente de Agente permitem definir o modo do agente e recursos avançados como prompts incorporados, disponíveis apenas no tipo de Agente.',
+      maximumIterations: {
+        name: 'Número Máximo de Iterações',
+        description: 'Limite o número de iterações que um assistente de agente pode executar',
+      },
+    },
+    buildInPrompt: 'Prompt Incorporado',
+    firstPrompt: 'Primeiro Prompt',
+    nextIteration: 'Próxima Iteração',
+    promptPlaceholder: 'Escreva seu prompt aqui',
+    tools: {
+      name: 'Ferramentas',
+      description: 'O uso de ferramentas pode ampliar as capacidades do LLM, como pesquisar na internet ou realizar cálculos científicos',
+      enabled: 'Habilitado',
+    },
+  },
+}
+
+export default translation

+ 69 - 0
web/i18n/lang/app-log.pt.ts

@@ -0,0 +1,69 @@
+const translation = {
+  title: 'Registros',
+  description: 'Os registros registram o status de execução do aplicativo, incluindo as entradas do usuário e as respostas da IA.',
+  dateTimeFormat: 'MM/DD/YYYY hh:mm A',
+  table: {
+    header: {
+      time: 'Tempo',
+      endUser: 'Usuário Final',
+      input: 'Entrada',
+      output: 'Saída',
+      summary: 'Título',
+      messageCount: 'Contagem de Mensagens',
+      userRate: 'Taxa de Usuário',
+      adminRate: 'Taxa de Op.',
+    },
+    pagination: {
+      previous: 'Anterior',
+      next: 'Próximo',
+    },
+    empty: {
+      noChat: 'Nenhuma conversa ainda',
+      noOutput: 'Nenhuma saída',
+      element: {
+        title: 'Tem alguém aí?',
+        content: 'Observe e anote as interações entre usuários finais e aplicativos de IA aqui para melhorar continuamente a precisão da IA. Você pode tentar <shareLink>compartilhar</shareLink> ou <testLink>testar</testLink> o aplicativo da Web você mesmo e depois voltar para esta página.',
+      },
+    },
+  },
+  detail: {
+    time: 'Tempo',
+    conversationId: 'ID da Conversa',
+    promptTemplate: 'Modelo de Prompt',
+    promptTemplateBeforeChat: 'Modelo de Prompt Antes da Conversa · Como Mensagem do Sistema',
+    annotationTip: 'Melhorias Marcadas por {{user}}',
+    timeConsuming: '',
+    second: 's',
+    tokenCost: 'Tokens gastos',
+    loading: 'carregando',
+    operation: {
+      like: 'curtir',
+      dislike: 'não curtir',
+      addAnnotation: 'Adicionar Melhoria',
+      editAnnotation: 'Editar Melhoria',
+      annotationPlaceholder: 'Digite a resposta esperada que você deseja que a IA responda, que pode ser usada para ajustar o modelo e melhorar continuamente a qualidade da geração de texto no futuro.',
+    },
+    variables: 'Variáveis',
+    uploadImages: 'Imagens Enviadas',
+  },
+  filter: {
+    period: {
+      today: 'Hoje',
+      last7days: 'Últimos 7 Dias',
+      last4weeks: 'Últimas 4 semanas',
+      last3months: 'Últimos 3 meses',
+      last12months: 'Últimos 12 meses',
+      monthToDate: 'Mês até a data',
+      quarterToDate: 'Trimestre até a data',
+      yearToDate: 'Ano até a data',
+      allTime: 'Todo o tempo',
+    },
+    annotation: {
+      all: 'Todos',
+      annotated: 'Melhorias Anotadas ({{count}} itens)',
+      not_annotated: 'Não Anotadas',
+    },
+  },
+}
+
+export default translation

+ 139 - 0
web/i18n/lang/app-overview.pt.ts

@@ -0,0 +1,139 @@
+const translation = {
+  welcome: {
+    firstStepTip: 'Para começar,',
+    enterKeyTip: 'insira sua chave de API do OpenAI abaixo',
+    getKeyTip: 'Obtenha sua chave de API no painel do OpenAI',
+    placeholder: 'Sua chave de API do OpenAI (por exemplo, sk-xxxx)',
+  },
+  apiKeyInfo: {
+    cloud: {
+      trial: {
+        title: 'Você está usando a cota de teste do {{providerName}}.',
+        description: 'A cota de teste é fornecida para uso de teste. Antes que as chamadas da cota de teste se esgotem, configure seu próprio provedor de modelo ou compre uma cota adicional.',
+      },
+      exhausted: {
+        title: 'Sua cota de teste foi esgotada, configure sua chave de API.',
+        description: 'Sua cota de teste foi esgotada. Configure seu próprio provedor de modelo ou compre uma cota adicional.',
+      },
+    },
+    selfHost: {
+      title: {
+        row1: 'Para começar,',
+        row2: 'configure seu próprio provedor de modelo primeiro.',
+      },
+    },
+    callTimes: 'Número de chamadas',
+    usedToken: 'Tokens usados',
+    setAPIBtn: 'Ir para configurar provedor de modelo',
+    tryCloud: 'Ou experimente a versão em nuvem do Dify com cota gratuita',
+  },
+  overview: {
+    title: 'Visão geral',
+    appInfo: {
+      explanation: 'Aplicativo Web de IA pronto para uso',
+      accessibleAddress: 'URL pública',
+      preview: 'Visualização',
+      regenerate: 'Regenerar',
+      preUseReminder: 'Ative o aplicativo da Web antes de continuar.',
+      settings: {
+        entry: 'Configurações',
+        title: 'Configurações do aplicativo da Web',
+        webName: 'Nome do aplicativo da Web',
+        webDesc: 'Descrição do aplicativo da Web',
+        webDescTip: 'Este texto será exibido no lado do cliente, fornecendo orientações básicas sobre como usar o aplicativo',
+        webDescPlaceholder: 'Insira a descrição do aplicativo da Web',
+        language: 'Idioma',
+        more: {
+          entry: 'Mostrar mais configurações',
+          copyright: 'Direitos autorais',
+          copyRightPlaceholder: 'Insira o nome do autor ou organização',
+          privacyPolicy: 'Política de Privacidade',
+          privacyPolicyPlaceholder: 'Insira o link da política de privacidade',
+          privacyPolicyTip: 'Ajuda os visitantes a entender os dados que o aplicativo coleta, consulte a <privacyPolicyLink>Política de Privacidade</privacyPolicyLink> do Dify.',
+        },
+      },
+      embedded: {
+        entry: 'Embutido',
+        title: 'Incorporar no site',
+        explanation: 'Escolha a maneira de incorporar o aplicativo de bate-papo ao seu site',
+        iframe: 'Para adicionar o aplicativo de bate-papo em qualquer lugar do seu site, adicione este iframe ao seu código HTML.',
+        scripts: 'Para adicionar um aplicativo de bate-papo na parte inferior direita do seu site, adicione este código ao seu HTML.',
+        chromePlugin: 'Instalar Extensão do Chatbot Dify para o Chrome',
+        copied: 'Copiado',
+        copy: 'Copiar',
+      },
+      qrcode: {
+        title: 'Código QR para compartilhar',
+        scan: 'Digitalizar para compartilhar o aplicativo',
+        download: 'Baixar código QR',
+      },
+      customize: {
+        way: 'maneira',
+        entry: 'Personalizar',
+        title: 'Personalizar aplicativo Web de IA',
+        explanation: 'Você pode personalizar a interface do usuário do aplicativo Web para se adequar ao seu cenário e necessidades de estilo.',
+        way1: {
+          name: 'Fork do código do cliente, modifique-o e implante no Vercel (recomendado)',
+          step1: 'Fork do código do cliente e modifique-o',
+          step1Tip: 'Clique aqui para fazer um fork do código-fonte em sua conta do GitHub e modificar o código',
+          step1Operation: 'Dify-WebClient',
+          step2: 'Implantar no Vercel',
+          step2Tip: 'Clique aqui para importar o repositório no Vercel e implantar',
+          step2Operation: 'Importar repositório',
+          step3: 'Configurar variáveis de ambiente',
+          step3Tip: 'Adicione as seguintes variáveis de ambiente no Vercel',
+        },
+        way2: {
+          name: 'Escrever código do lado do cliente para chamar a API e implantá-lo em um servidor',
+          operation: 'Documentação',
+        },
+      },
+    },
+    apiInfo: {
+      title: 'API do serviço de backend',
+      explanation: 'Integração fácil em seu aplicativo',
+      accessibleAddress: 'Endpoint da API de serviço',
+      doc: 'Referência da API',
+    },
+    status: {
+      running: 'Em serviço',
+      disable: 'Desativar',
+    },
+  },
+  analysis: {
+    title: 'Análise',
+    ms: 'ms',
+    tokenPS: 'Token/s',
+    totalMessages: {
+      title: 'Total de mensagens',
+      explanation: 'Contagem diária de interações de IA; engenharia de prompt/depuração excluída.',
+    },
+    activeUsers: {
+      title: 'Usuários ativos',
+      explanation: 'Usuários únicos envolvidos em perguntas e respostas com IA; engenharia de prompt/depuração excluída.',
+    },
+    tokenUsage: {
+      title: 'Uso de tokens',
+      explanation: 'Reflete o uso diário de tokens do modelo de linguagem para o aplicativo, útil para fins de controle de custos.',
+      consumed: 'Consumidos',
+    },
+    avgSessionInteractions: {
+      title: 'Média de interações por sessão',
+      explanation: 'Contagem contínua de comunicação usuário-IA; para aplicativos baseados em conversas.',
+    },
+    userSatisfactionRate: {
+      title: 'Taxa de satisfação do usuário',
+      explanation: 'O número de curtidas por 1.000 mensagens. Isso indica a proporção de respostas com as quais os usuários estão altamente satisfeitos.',
+    },
+    avgResponseTime: {
+      title: 'Tempo médio de resposta',
+      explanation: 'Tempo (ms) para o processamento/resposta da IA; para aplicativos baseados em texto.',
+    },
+    tps: {
+      title: 'Velocidade de saída de tokens',
+      explanation: 'Mede o desempenho do LLM. Conta a velocidade de saída de tokens do LLM desde o início da solicitação até a conclusão da saída.',
+    },
+  },
+}
+
+export default translation

+ 49 - 0
web/i18n/lang/app.pt.ts

@@ -0,0 +1,49 @@
+const translation = {
+  title: 'Aplicativos',
+  createApp: 'Criar novo aplicativo',
+  modes: {
+    completion: 'Gerador de Texto',
+    chat: 'Aplicativo de Chat',
+  },
+  createFromConfigFile: 'Criar aplicativo a partir do arquivo de configuração',
+  deleteAppConfirmTitle: 'Excluir este aplicativo?',
+  deleteAppConfirmContent:
+    'A exclusão do aplicativo é irreversível. Os usuários não poderão mais acessar seu aplicativo e todas as configurações de prompt e logs serão excluídas permanentemente.',
+  appDeleted: 'Aplicativo excluído',
+  appDeleteFailed: 'Falha ao excluir o aplicativo',
+  join: 'Participe da comunidade',
+  communityIntro:
+    'Discuta com membros da equipe, colaboradores e desenvolvedores em diferentes canais.',
+  roadmap: 'Veja nosso roteiro',
+  appNamePlaceholder: 'Por favor, digite o nome do aplicativo',
+  newApp: {
+    startToCreate: 'Vamos começar com o seu novo aplicativo',
+    captionName: 'Dê um nome ao seu aplicativo',
+    captionAppType: 'Que tipo de aplicativo você deseja?',
+    previewDemo: 'Visualizar demonstração',
+    chatApp: 'Aplicativo de Chat',
+    chatAppIntro:
+      'Quero construir um aplicativo baseado em chat. Este aplicativo usa um formato de pergunta e resposta, permitindo várias rodadas de conversa contínua.',
+    completeApp: 'Gerador de Texto',
+    completeAppIntro:
+      'Quero criar um aplicativo que gera texto de alta qualidade com base em prompts, como geração de artigos, resumos, traduções e muito mais.',
+    showTemplates: 'Quero escolher um modelo',
+    hideTemplates: 'Voltar para seleção de modo',
+    Create: 'Criar',
+    Cancel: 'Cancelar',
+    nameNotEmpty: 'O nome não pode estar vazio',
+    appTemplateNotSelected: 'Por favor, selecione um modelo',
+    appTypeRequired: 'Por favor, selecione um tipo de aplicativo',
+    appCreated: 'Aplicativo criado',
+    appCreateFailed: 'Falha ao criar o aplicativo',
+  },
+  editApp: {
+    startToEdit: 'Editar Aplicativo',
+  },
+  emoji: {
+    ok: 'OK',
+    cancel: 'Cancelar',
+  },
+}
+
+export default translation

+ 109 - 0
web/i18n/lang/billing.pt.ts

@@ -0,0 +1,109 @@
+const translation = {
+  currentPlan: 'Current Plan',
+  upgradeBtn: {
+    plain: 'Upgrade Plan',
+    encourage: 'Upgrade Now',
+    encourageShort: 'Upgrade',
+  },
+  viewBilling: 'View billing information',
+  buyPermissionDeniedTip: 'Please contact your enterprise administrator to subscribe',
+  plansCommon: {
+    title: 'Choose a plan that’s right for you',
+    yearlyTip: 'Get 2 months for free by subscribing yearly!',
+    mostPopular: 'Most Popular',
+    planRange: {
+      monthly: 'Monthly',
+      yearly: 'Yearly',
+    },
+    month: 'month',
+    year: 'year',
+    save: 'Save ',
+    free: 'Free',
+    currentPlan: 'Current Plan',
+    contractOwner: 'Contact your workspace owner',
+    startForFree: 'Start for free',
+    getStartedWith: 'Get started with ',
+    contactSales: 'Contact Sales',
+    talkToSales: 'Talk to Sales',
+    modelProviders: 'Model Providers',
+    teamMembers: 'Team Members',
+    buildApps: 'Build Apps',
+    vectorSpace: 'Vector Space',
+    vectorSpaceBillingTooltip: 'Each 1MB can store about 1.2million characters of vectorized data(estimated using OpenAI Embeddings, varies across models).',
+    vectorSpaceTooltip: 'Vector Space is the long-term memory system required for LLMs  to comprehend your data.',
+    documentProcessingPriority: 'Document Processing Priority',
+    documentProcessingPriorityTip: 'For higher document processing priority, please upgrade your plan.',
+    documentProcessingPriorityUpgrade: 'Process more data with higher accuracy at faster speeds.',
+    priority: {
+      'standard': 'Standard',
+      'priority': 'Priority',
+      'top-priority': 'Top Priority',
+    },
+    logsHistory: 'Logs history',
+    days: 'days',
+    unlimited: 'Unlimited',
+    support: 'Support',
+    supportItems: {
+      communityForums: 'Community forums',
+      emailSupport: 'Email support',
+      priorityEmail: 'Priority email & chat support',
+      logoChange: 'Logo change',
+      SSOAuthentication: 'SSO authentication',
+      personalizedSupport: 'Personalized support',
+      dedicatedAPISupport: 'Dedicated API support',
+      customIntegration: 'Custom integration and support',
+      ragAPIRequest: 'RAG API Requests',
+      agentModel: 'Agent Model',
+    },
+    comingSoon: 'Coming soon',
+    member: 'Member',
+    memberAfter: 'Member',
+    messageRequest: {
+      title: 'Message Requests',
+      unit: 'per day',
+      tooltip: 'Includes all messages from your apps, whether via APIs or web sessions. (Not LLM resource usage)',
+    },
+    annotatedResponse: {
+      title: 'Annotation Quota Limits',
+      tooltip: 'Manual editing and annotation of responses provides customizable high-quality question-answering abilities for apps. (Applicable only in chat apps)',
+    },
+    ragAPIRequestTooltip: 'Refers to the number of API calls invoking only the knowledge base processing capabilities of Dify.',
+  },
+  plans: {
+    sandbox: {
+      name: 'Sandbox',
+      description: '200 times GPT free trial',
+      includesTitle: 'Includes:',
+    },
+    professional: {
+      name: 'Professional',
+      description: 'For individuals and small teams to unlock more power affordably.',
+      includesTitle: 'Everything in free plan, plus:',
+    },
+    team: {
+      name: 'Team',
+      description: 'Collaborate without limits and enjoy top-tier performance.',
+      includesTitle: 'Everything in Professional plan, plus:',
+    },
+    enterprise: {
+      name: 'Enterprise',
+      description: 'Get full capabilities and support for large-scale mission-critical systems.',
+      includesTitle: 'Everything in Team plan, plus:',
+    },
+  },
+  vectorSpace: {
+    fullTip: 'Vector Space is full.',
+    fullSolution: 'Upgrade your plan to get more space.',
+  },
+  apps: {
+    fullTipLine1: 'Upgrade your plan to',
+    fullTipLine2: 'build more apps.',
+  },
+  annotatedResponse: {
+    fullTipLine1: 'Upgrade your plan to',
+    fullTipLine2: 'annotate more conversations.',
+    quotaTitle: 'Annotation Reply Quota',
+  },
+}
+
+export default translation

+ 313 - 0
web/i18n/lang/common.pt.ts

@@ -0,0 +1,313 @@
+const translation = {
+  you: '(Você)',
+  integrations: {
+    connected: 'Conectado',
+    google: 'Google',
+    googleAccount: 'Entrar com conta do Google',
+    github: 'GitHub',
+    githubAccount: 'Entrar com conta do GitHub',
+    connect: 'Conectar',
+  },
+  language: {
+    displayLanguage: 'Idioma de exibição',
+    timezone: 'Fuso horário',
+  },
+  provider: {
+    apiKey: 'Chave da API',
+    enterYourKey: 'Insira sua chave da API aqui',
+    invalidKey: 'Chave da API inválida',
+    validatedError: 'Falha na validação: ',
+    validating: 'Validando chave...',
+    saveFailed: 'Falha ao salvar a chave da API',
+    apiKeyExceedBill: 'Esta chave da API não possui cota disponível, por favor leia',
+    addKey: 'Adicionar chave',
+    comingSoon: 'Em breve',
+    editKey: 'Editar',
+    invalidApiKey: 'Chave da API inválida',
+    azure: {
+      apiBase: 'Base da API',
+      apiBasePlaceholder: 'A URL base da API do seu ponto de extremidade Azure OpenAI.',
+      apiKey: 'Chave da API',
+      apiKeyPlaceholder: 'Insira sua chave da API aqui',
+      helpTip: 'Aprenda sobre o Serviço Azure OpenAI',
+    },
+    openaiHosted: {
+      openaiHosted: 'OpenAI Hospedado',
+      onTrial: 'EM TESTE',
+      exhausted: 'COTA ESGOTADA',
+      desc: 'O serviço de hospedagem OpenAI fornecido pela Dify permite que você use modelos como o GPT-3.5. Antes que sua cota de teste seja esgotada, você precisa configurar outros provedores de modelo.',
+      callTimes: 'Número de chamadas',
+      usedUp: 'Cota de teste esgotada. Adicione seu próprio provedor de modelo.',
+      useYourModel: 'Atualmente usando seu próprio provedor de modelo.',
+      close: 'Fechar',
+    },
+    anthropicHosted: {
+      anthropicHosted: 'Anthropic Claude',
+      onTrial: 'EM TESTE',
+      exhausted: 'COTA ESGOTADA',
+      desc: 'Modelo poderoso, que se destaca em uma ampla gama de tarefas, desde diálogos sofisticados e geração de conteúdo criativo até instruções detalhadas.',
+      callTimes: 'Número de chamadas',
+      usedUp: 'Cota de teste esgotada. Adicione seu próprio provedor de modelo.',
+      useYourModel: 'Atualmente usando seu próprio provedor de modelo.',
+      close: 'Fechar',
+    },
+    anthropic: {
+      using: 'A capacidade de incorporação está usando',
+      enableTip: 'Para habilitar o modelo Anthropic, você precisa se vincular ao OpenAI ou ao Azure OpenAI Service primeiro.',
+      notEnabled: 'Não habilitado',
+      keyFrom: 'Obtenha sua chave da API do Anthropic',
+    },
+    encrypted: {
+      front: 'Sua CHAVE DA API será criptografada e armazenada usando',
+      back: ' tecnologia.',
+    },
+  },
+  modelProvider: {
+    notConfigured: 'O modelo do sistema ainda não foi totalmente configurado e algumas funções podem não estar disponíveis.',
+    systemModelSettings: 'Configurações do modelo do sistema',
+    systemModelSettingsLink: 'Por que é necessário configurar um modelo do sistema?',
+    selectModel: 'Selecione seu modelo',
+    setupModelFirst: 'Por favor, configure seu modelo primeiro',
+    systemReasoningModel: {
+      key: 'Modelo de raciocínio do sistema',
+      tip: 'Defina o modelo de inferência padrão a ser usado para criar aplicativos, bem como recursos como geração de nome de diálogo e sugestão de próxima pergunta também usarão o modelo de inferência padrão.',
+    },
+    embeddingModel: {
+      key: 'Modelo de incorporação',
+      tip: 'Defina o modelo padrão para o processamento de incorporação de documentos do Conhecimento, tanto a recuperação quanto a importação do Conhecimento usam este modelo de Incorporação para o processamento de vetorização. A troca causará inconsistência na dimensão do vetor entre o Conhecimento importado e a pergunta, resultando em falha na recuperação. Para evitar falhas na recuperação, não altere este modelo indiscriminadamente.',
+      required: 'O modelo de incorporação é obrigatório',
+    },
+    speechToTextModel: {
+      key: 'Modelo de fala para texto',
+      tip: 'Defina o modelo padrão para entrada de fala para texto em conversa.',
+    },
+    rerankModel: {
+      key: 'Modelo de reclassificação',
+      tip: 'O modelo de reclassificação reordenará a lista de documentos candidatos com base na correspondência semântica com a consulta do usuário, melhorando os resultados da classificação semântica',
+    },
+    quota: 'Cota',
+    searchModel: 'Modelo de pesquisa',
+    noModelFound: 'Nenhum modelo encontrado para {{model}}',
+    models: 'Modelos',
+    showMoreModelProvider: 'Mostrar mais provedor de modelo',
+    selector: {
+      tip: 'Este modelo foi removido. Adicione um modelo ou selecione outro modelo.',
+      emptyTip: 'Nenhum modelo disponível',
+      emptySetting: 'Por favor, vá para as configurações para configurar',
+      rerankTip: 'Por favor, configure o modelo de reclassificação',
+    },
+    card: {
+      quota: 'COTA',
+      onTrial: 'Em Teste',
+      paid: 'Pago',
+      quotaExhausted: 'Cota esgotada',
+      callTimes: 'Número de chamadas',
+      tokens: 'Tokens',
+      buyQuota: 'Comprar Cota',
+      priorityUse: 'Uso prioritário',
+      removeKey: 'Remover Chave da API',
+      tip: 'A cota paga terá prioridade. A cota de teste será usada após a cota paga ser esgotada.',
+    },
+    item: {
+      deleteDesc: '{{modelName}} está sendo usado como modelos de raciocínio do sistema. Algumas funções não estarão disponíveis após a remoção. Por favor, confirme.',
+      freeQuota: 'COTA GRATUITA',
+    },
+    addApiKey: 'Adicionar sua chave da API',
+    invalidApiKey: 'Chave da API inválida',
+    encrypted: {
+      front: 'Sua CHAVE DA API será criptografada e armazenada usando',
+      back: ' tecnologia.',
+    },
+    freeQuota: {
+      howToEarn: 'Como ganhar',
+    },
+    addMoreModelProvider: 'ADICIONAR MAIS PROVEDOR DE MODELO',
+    addModel: 'Adicionar Modelo',
+    modelsNum: '{{num}} Modelos',
+    showModels: 'Mostrar Modelos',
+    showModelsNum: 'Mostrar {{num}} Modelos',
+    collapse: 'Recolher',
+    config: 'Configurar',
+    modelAndParameters: 'Modelo e Parâmetros',
+    model: 'Modelo',
+    featureSupported: '{{feature}} suportado',
+    callTimes: 'Número de chamadas',
+    buyQuota: 'Comprar Cota',
+    getFreeTokens: 'Obter Tokens gratuitos',
+    priorityUsing: 'Uso prioritário',
+    deprecated: 'Descontinuado',
+    confirmDelete: 'confirmar exclusão?',
+    quotaTip: 'Tokens gratuitos disponíveis restantes',
+  },
+  dataSource: {
+    add: 'Adicionar uma fonte de dados',
+    connect: 'Conectar',
+    notion: {
+      title: 'Notion',
+      description: 'Usando o Notion como fonte de dados para o Conhecimento.',
+      connectedWorkspace: 'Espaço de trabalho conectado',
+      addWorkspace: 'Adicionar espaço de trabalho',
+      connected: 'Conectado',
+      disconnected: 'Desconectado',
+      changeAuthorizedPages: 'Alterar páginas autorizadas',
+      pagesAuthorized: 'Páginas autorizadas',
+      sync: 'Sincronizar',
+      remove: 'Remover',
+      selector: {
+        pageSelected: 'Páginas Selecionadas',
+        searchPages: 'Pesquisar páginas...',
+        noSearchResult: 'Nenhum resultado de pesquisa',
+        addPages: 'Adicionar páginas',
+        preview: 'PRÉ-VISUALIZAÇÃO',
+      },
+    },
+  },
+  plugin: {
+    serpapi: {
+      apiKey: 'Chave da API',
+      apiKeyPlaceholder: 'Insira sua chave da API',
+      keyFrom: 'Obtenha sua chave SerpAPI na página da conta SerpAPI',
+    },
+  },
+  apiBasedExtension: {
+    title: 'As extensões de API fornecem gerenciamento centralizado de API, simplificando a configuração para uso fácil em aplicativos da Dify.',
+    link: 'Saiba como desenvolver sua própria Extensão de API.',
+    linkUrl: 'https://docs.dify.ai/advanced/api_based_extension',
+    add: 'Adicionar Extensão de API',
+    selector: {
+      title: 'Extensão de API',
+      placeholder: 'Por favor, selecione a extensão de API',
+      manage: 'Gerenciar Extensão de API',
+    },
+    modal: {
+      title: 'Adicionar Extensão de API',
+      editTitle: 'Editar Extensão de API',
+      name: {
+        title: 'Nome',
+        placeholder: 'Por favor, insira o nome',
+      },
+      apiEndpoint: {
+        title: 'Endpoint da API',
+        placeholder: 'Por favor, insira o endpoint da API',
+      },
+      apiKey: {
+        title: 'Chave da API',
+        placeholder: 'Por favor, insira a chave da API',
+        lengthError: 'O comprimento da chave da API não pode ser inferior a 5 caracteres',
+      },
+    },
+  },
+  about: {
+    changeLog: 'Registro de alterações',
+    updateNow: 'Atualizar agora',
+    nowAvailable: 'Dify {{version}} está disponível agora.',
+    latestAvailable: 'Dify {{version}} é a versão mais recente disponível.',
+  },
+  appMenus: {
+    overview: 'Visão geral',
+    promptEng: 'Orquestrar',
+    apiAccess: 'Acesso à API',
+    logAndAnn: 'Logs e Anúncios',
+  },
+  environment: {
+    testing: 'TESTE',
+    development: 'DESENVOLVIMENTO',
+  },
+  appModes: {
+    completionApp: 'Gerador de Texto',
+    chatApp: 'Aplicativo de Chat',
+  },
+  datasetMenus: {
+    documents: 'Documentos',
+    hitTesting: 'Teste de Recuperação',
+    settings: 'Configurações',
+    emptyTip: 'O Conhecimento não foi associado, por favor, vá para o aplicativo ou plug-in para completar a associação.',
+    viewDoc: 'Ver documentação',
+    relatedApp: 'aplicativos vinculados',
+  },
+  voiceInput: {
+    speaking: 'Fale agora...',
+    converting: 'Convertendo para texto...',
+    notAllow: 'microfone não autorizado',
+  },
+  modelName: {
+    'gpt-3.5-turbo': 'GPT-3.5-Turbo',
+    'gpt-3.5-turbo-16k': 'GPT-3.5-Turbo-16K',
+    'gpt-4': 'GPT-4',
+    'gpt-4-32k': 'GPT-4-32K',
+    'text-davinci-003': 'Text-Davinci-003',
+    'text-embedding-ada-002': 'Text-Embedding-Ada-002',
+    'whisper-1': 'Whisper-1',
+    'claude-instant-1': 'Claude-Instant',
+    'claude-2': 'Claude-2',
+  },
+  chat: {
+    renameConversation: 'Renomear Conversa',
+    conversationName: 'Nome da conversa',
+    conversationNamePlaceholder: 'Por favor, insira o nome da conversa',
+    conversationNameCanNotEmpty: 'Nome da conversa obrigatório',
+    citation: {
+      title: 'CITAÇÕES',
+      linkToDataset: 'Link para o Conhecimento',
+      characters: 'Personagens:',
+      hitCount: 'Contagem de recuperação:',
+      vectorHash: 'Hash do vetor:',
+      hitScore: 'Pontuação de recuperação:',
+    },
+  },
+  promptEditor: {
+    placeholder: 'Escreva sua palavra de estímulo aqui, digite \'{\' para inserir uma variável, digite \'/\' para inserir um bloco de conteúdo de estímulo',
+    context: {
+      item: {
+        title: 'Contexto',
+        desc: 'Inserir modelo de contexto',
+      },
+      modal: {
+        title: '{{num}} Conhecimento em Contexto',
+        add: 'Adicionar Contexto',
+        footer: 'Você pode gerenciar os contextos na seção de Contexto abaixo.',
+      },
+    },
+    history: {
+      item: {
+        title: 'Histórico da Conversa',
+        desc: 'Inserir modelo de mensagem histórica',
+      },
+      modal: {
+        title: 'EXEMPLO',
+        user: 'Olá',
+        assistant: 'Olá! Como posso ajudar hoje?',
+        edit: 'Editar Nomes de Função da Conversa',
+      },
+    },
+    variable: {
+      item: {
+        title: 'Variáveis e Ferramentas Externas',
+        desc: 'Inserir Variáveis e Ferramentas Externas',
+      },
+      modal: {
+        add: 'Nova variável',
+        addTool: 'Nova ferramenta',
+      },
+    },
+    query: {
+      item: {
+        title: 'Consulta',
+        desc: 'Inserir modelo de consulta do usuário',
+      },
+    },
+    existed: 'Já existe no estímulo',
+  },
+  imageUploader: {
+    uploadFromComputer: 'Enviar do computador',
+    uploadFromComputerReadError: 'Falha na leitura da imagem, por favor, tente novamente.',
+    uploadFromComputerUploadError: 'Falha no envio da imagem, por favor, envie novamente.',
+    uploadFromComputerLimit: 'As imagens enviadas não podem exceder {{size}} MB',
+    pasteImageLink: 'Colar link da imagem',
+    pasteImageLinkInputPlaceholder: 'Cole o link da imagem aqui',
+    pasteImageLinkInvalid: 'Link da imagem inválido',
+    imageUpload: 'Envio de Imagem',
+  },
+}
+
+export default translation

+ 30 - 0
web/i18n/lang/custom.pt.ts

@@ -0,0 +1,30 @@
+const translation = {
+  custom: 'Customization',
+  upgradeTip: {
+    prefix: 'Upgrade your plan to',
+    suffix: 'customize your brand.',
+  },
+  webapp: {
+    title: 'Customize WebApp brand',
+    removeBrand: 'Remove Powered by Dify',
+    changeLogo: 'Change Powered by Brand Image',
+    changeLogoTip: 'SVG or PNG format with a minimum size of 40x40px',
+  },
+  app: {
+    title: 'Customize app header brand',
+    changeLogoTip: 'SVG or PNG format with a minimum size of 80x80px',
+  },
+  upload: 'Upload',
+  uploading: 'Uploading',
+  uploadedFail: 'Image upload failed, please re-upload.',
+  change: 'Change',
+  apply: 'Apply',
+  restore: 'Restore Defaults',
+  customize: {
+    contactUs: ' contact us ',
+    prefix: 'To customize the brand logo within the app, please',
+    suffix: 'to upgrade to the Enterprise edition.',
+  },
+}
+
+export default translation

+ 126 - 0
web/i18n/lang/dataset-creation.pt.ts

@@ -0,0 +1,126 @@
+const translation = {
+  steps: {
+    header: {
+      creation: 'Criar Conhecimento',
+      update: 'Adicionar dados',
+    },
+    one: 'Escolher fonte de dados',
+    two: 'Pré-processamento e Limpeza de Texto',
+    three: 'Executar e finalizar',
+  },
+  error: {
+    unavailable: 'Este Conhecimento não está disponível',
+  },
+  stepOne: {
+    filePreview: 'Visualização do arquivo',
+    pagePreview: 'Visualização da página',
+    dataSourceType: {
+      file: 'Importar de arquivo de texto',
+      notion: 'Sincronizar do Notion',
+      web: 'Sincronizar de site',
+    },
+    uploader: {
+      title: 'Enviar arquivo de texto',
+      button: 'Arraste e solte o arquivo, ou',
+      browse: 'Navegar',
+      tip: 'Suporta {{supportTypes}}. Máximo de {{size}}MB cada.',
+      validation: {
+        typeError: 'Tipo de arquivo não suportado',
+        size: 'Arquivo muito grande. Máximo é {{size}}MB',
+        count: 'Vários arquivos não suportados',
+      },
+      cancel: 'Cancelar',
+      change: 'Alterar',
+      failed: 'Falha no envio',
+    },
+    notionSyncTitle: 'Notion não está conectado',
+    notionSyncTip: 'Para sincronizar com o Notion, a conexão com o Notion deve ser estabelecida primeiro.',
+    connect: 'Ir para conexão',
+    button: 'Próximo',
+    emptyDatasetCreation: 'Quero criar um Conhecimento vazio',
+    modal: {
+      title: 'Criar um Conhecimento vazio',
+      tip: 'Um Conhecimento vazio não conterá documentos e você poderá fazer upload de documentos a qualquer momento.',
+      input: 'Nome do Conhecimento',
+      placeholder: 'Por favor, insira',
+      nameNotEmpty: 'O nome não pode estar vazio',
+      nameLengthInvaild: 'O nome deve ter entre 1 e 40 caracteres',
+      cancelButton: 'Cancelar',
+      confirmButton: 'Criar',
+      failed: 'Falha na criação',
+    },
+  },
+  stepTwo: {
+    segmentation: 'Configurações de fragmentação',
+    auto: 'Automático',
+    autoDescription: 'Configura automaticamente as regras de fragmentação e pré-processamento. Usuários não familiarizados são recomendados a selecionar esta opção.',
+    custom: 'Personalizado',
+    customDescription: 'Personalize as regras de fragmentação, comprimento dos fragmentos e regras de pré-processamento, etc.',
+    separator: 'Identificador de segmento',
+    separatorPlaceholder: 'Por exemplo, nova linha (\\\\n) ou separador especial (como "***")',
+    maxLength: 'Comprimento máximo do fragmento',
+    rules: 'Regras de pré-processamento de texto',
+    removeExtraSpaces: 'Substituir espaços consecutivos, quebras de linha e tabulações',
+    removeUrlEmails: 'Excluir todos os URLs e endereços de e-mail',
+    removeStopwords: 'Remover palavras irrelevantes como "um", "uma", "o"',
+    preview: 'Confirmar e visualizar',
+    reset: 'Redefinir',
+    indexMode: 'Modo de índice',
+    qualified: 'Alta qualidade',
+    recommend: 'Recomendado',
+    qualifiedTip: 'Chama a interface de incorporação do sistema padrão para processamento, fornecendo maior precisão ao consultar.',
+    warning: 'Por favor, configure primeiro a chave da API do provedor do modelo.',
+    click: 'Ir para configurações',
+    economical: 'Econômico',
+    economicalTip: 'Use motores de vetor offline, índices de palavras-chave, etc. para reduzir a precisão sem gastar tokens',
+    QATitle: 'Fragmentação no formato de Perguntas e Respostas',
+    QATip: 'Habilitar esta opção consumirá mais tokens',
+    QALanguage: 'Fragmentar usando',
+    emstimateCost: 'Estimativa',
+    emstimateSegment: 'Fragmentos estimados',
+    segmentCount: 'fragmentos',
+    calculating: 'Calculando...',
+    fileSource: 'Pré-processar documentos',
+    notionSource: 'Pré-processar páginas',
+    other: 'e outros ',
+    fileUnit: ' arquivos',
+    notionUnit: ' páginas',
+    lastStep: 'Última etapa',
+    nextStep: 'Salvar e Processar',
+    save: 'Salvar e Processar',
+    cancel: 'Cancelar',
+    sideTipTitle: 'Por que fragmentar e pré-processar?',
+    sideTipP1: 'Ao processar dados de texto, fragmentar e limpar são duas etapas importantes de pré-processamento.',
+    sideTipP2: 'A fragmentação divide um texto longo em parágrafos para que os modelos possam entender melhor. Isso melhora a qualidade e relevância dos resultados do modelo.',
+    sideTipP3: 'A limpeza remove caracteres e formatos desnecessários, tornando o Conhecimento mais limpo e fácil de analisar.',
+    sideTipP4: 'Fragmentação e limpeza adequadas melhoram o desempenho do modelo, fornecendo resultados mais precisos e valiosos.',
+    previewTitle: 'Visualização',
+    previewTitleButton: 'Visualização',
+    previewButton: 'Alternar para visualização no formato de Perguntas e Respostas',
+    previewSwitchTipStart: 'A visualização atual do fragmento está no formato de texto, alternar para uma visualização no formato de Perguntas e Respostas irá',
+    previewSwitchTipEnd: ' consumir tokens adicionais',
+    characters: 'caracteres',
+    indexSettedTip: 'Para alterar o método de índice, por favor vá para as ',
+    retrivalSettedTip: 'Para alterar o método de índice, por favor vá para as ',
+    datasetSettingLink: 'configurações do Conhecimento.',
+  },
+  stepThree: {
+    creationTitle: '🎉 Conhecimento criado',
+    creationContent: 'Nomeamos automaticamente o Conhecimento, você pode modificá-lo a qualquer momento',
+    label: 'Nome do Conhecimento',
+    additionTitle: '🎉 Documento enviado',
+    additionP1: 'O documento foi enviado para o Conhecimento',
+    additionP2: ', você pode encontrá-lo na lista de documentos do Conhecimento.',
+    stop: 'Parar processamento',
+    resume: 'Continuar processamento',
+    navTo: 'Ir para documento',
+    sideTipTitle: 'O que fazer em seguida',
+    sideTipContent: 'Após a conclusão da indexação do documento, o Conhecimento pode ser integrado à aplicação como contexto. Você pode encontrar a configuração de contexto na página de orquestração de prompts. Você também pode criá-lo como um plugin de indexação ChatGPT independente para lançamento.',
+    modelTitle: 'Tem certeza de que deseja parar a incorporação?',
+    modelContent: 'Se você precisar continuar o processamento posteriormente, você continuará de onde parou.',
+    modelButtonConfirm: 'Confirmar',
+    modelButtonCancel: 'Cancelar',
+  },
+}
+
+export default translation

+ 349 - 0
web/i18n/lang/dataset-documents.pt.ts

@@ -0,0 +1,349 @@
+const translation = {
+  list: {
+    title: 'Documentos',
+    desc: 'Todos os arquivos do Knowledge são mostrados aqui, e todo o Knowledge pode ser vinculado a citações do Dify ou indexado por meio do plugin Chat.',
+    addFile: 'adicionar arquivo',
+    addPages: 'Adicionar Páginas',
+    table: {
+      header: {
+        fileName: 'NOME DO ARQUIVO',
+        words: 'PALAVRAS',
+        hitCount: 'CONTAGEM DE RECUPERAÇÃO',
+        uploadTime: 'HORA DO UPLOAD',
+        status: 'STATUS',
+        action: 'AÇÃO',
+      },
+    },
+    action: {
+      uploadFile: 'Enviar novo arquivo',
+      settings: 'Configurações de segmento',
+      addButton: 'Adicionar fragmento',
+      add: 'Adicionar um fragmento',
+      batchAdd: 'Adicionar em lote',
+      archive: 'Arquivar',
+      unarchive: 'Desarquivar',
+      delete: 'Excluir',
+      enableWarning: 'O arquivo arquivado não pode ser habilitado',
+      sync: 'Sincronizar',
+    },
+    index: {
+      enable: 'Habilitar',
+      disable: 'Desabilitar',
+      all: 'Todos',
+      enableTip: 'O arquivo pode ser indexado',
+      disableTip: 'O arquivo não pode ser indexado',
+    },
+    status: {
+      queuing: 'Em fila',
+      indexing: 'Indexando',
+      paused: 'Pausado',
+      error: 'Erro',
+      available: 'Disponível',
+      enabled: 'Habilitado',
+      disabled: 'Desabilitado',
+      archived: 'Arquivado',
+    },
+    empty: {
+      title: 'Ainda não há documentação',
+      upload: {
+        tip: 'Você pode enviar arquivos, sincronizar do site ou de aplicativos da web como Notion, GitHub, etc.',
+      },
+      sync: {
+        tip: 'O Dify baixará periodicamente arquivos do seu Notion e concluirá o processamento.',
+      },
+    },
+    delete: {
+      title: 'Tem certeza que deseja excluir?',
+      content: 'Se você precisar retomar o processamento posteriormente, continuará de onde parou',
+    },
+    batchModal: {
+      title: 'Adicionar fragmentos em lote',
+      csvUploadTitle: 'Arraste e solte seu arquivo CSV aqui ou ',
+      browse: 'navegar',
+      tip: 'O arquivo CSV deve seguir a seguinte estrutura:',
+      question: 'pergunta',
+      answer: 'resposta',
+      contentTitle: 'conteúdo do fragmento',
+      content: 'conteúdo',
+      template: 'Baixe o modelo aqui',
+      cancel: 'Cancelar',
+      run: 'Executar em lote',
+      runError: 'Falha ao executar em lote',
+      processing: 'Processando em lote',
+      completed: 'Importação concluída',
+      error: 'Erro na importação',
+      ok: 'OK',
+    },
+  },
+  metadata: {
+    title: 'Metadados',
+    desc: 'A rotulagem de metadados para documentos permite que a IA acesse-os de maneira oportuna e expõe a fonte de referências para os usuários.',
+    dateTimeFormat: 'D MMMM, YYYY hh:mm A',
+    docTypeSelectTitle: 'Selecione um tipo de documento',
+    docTypeChangeTitle: 'Alterar tipo de documento',
+    docTypeSelectWarning:
+      'Se o tipo de documento for alterado, os metadados preenchidos agora não serão mais preservados',
+    firstMetaAction: 'Vamos lá',
+    placeholder: {
+      add: 'Adicionar ',
+      select: 'Selecionar ',
+    },
+    source: {
+      upload_file: 'Enviar arquivo',
+      notion: 'Sincronizar do Notion',
+      github: 'Sincronizar do Github',
+    },
+    type: {
+      book: 'Livro',
+      webPage: 'Página da Web',
+      paper: 'Artigo',
+      socialMediaPost: 'Postagem em Mídias Sociais',
+      personalDocument: 'Documento Pessoal',
+      businessDocument: 'Documento Empresarial',
+      IMChat: 'Chat de IM',
+      wikipediaEntry: 'Entrada da Wikipedia',
+      notion: 'Sincronizar do Notion',
+      github: 'Sincronizar do Github',
+      technicalParameters: 'Parâmetros Técnicos',
+    },
+    field: {
+      processRule: {
+        processDoc: 'Processar Documento',
+        segmentRule: 'Regra de Fragmentação',
+        segmentLength: 'Comprimento dos Fragmentos',
+        processClean: 'Limpeza de Texto',
+      },
+      book: {
+        title: 'Título',
+        language: 'Idioma',
+        author: 'Autor',
+        publisher: 'Editora',
+        publicationDate: 'Data de Publicação',
+        ISBN: 'ISBN',
+        category: 'Categoria',
+      },
+      webPage: {
+        title: 'Título',
+        url: 'URL',
+        language: 'Idioma',
+        authorPublisher: 'Autor/Editor',
+        publishDate: 'Data de Publicação',
+        topicsKeywords: 'Tópicos/Palavras-chave',
+        description: 'Descrição',
+      },
+      paper: {
+        title: 'Título',
+        language: 'Idioma',
+        author: 'Autor',
+        publishDate: 'Data de Publicação',
+        journalConferenceName: 'Nome do Jornal/Conferência',
+        volumeIssuePage: 'Volume/Edição/Página',
+        DOI: 'DOI',
+        topicsKeywords: 'Tópicos/Palavras-chave',
+        abstract: 'Resumo',
+      },
+      socialMediaPost: {
+        platform: 'Plataforma',
+        authorUsername: 'Autor/Nome de Usuário',
+        publishDate: 'Data de Publicação',
+        postURL: 'URL da Postagem',
+        topicsTags: 'Tópicos/Tags',
+      },
+      personalDocument: {
+        title: 'Título',
+        author: 'Autor',
+        creationDate: 'Data de Criação',
+        lastModifiedDate: 'Data da Última Modificação',
+        documentType: 'Tipo de Documento',
+        tagsCategory: 'Tags/Categoria',
+      },
+      businessDocument: {
+        title: 'Título',
+        author: 'Autor',
+        creationDate: 'Data de Criação',
+        lastModifiedDate: 'Data da Última Modificação',
+        documentType: 'Tipo de Documento',
+        departmentTeam: 'Departamento/Equipe',
+      },
+      IMChat: {
+        chatPlatform: 'Plataforma de Chat',
+        chatPartiesGroupName: 'Partes/Grupo do Chat',
+        participants: 'Participantes',
+        startDate: 'Data de Início',
+        endDate: 'Data de Término',
+        topicsKeywords: 'Tópicos/Palavras-chave',
+        fileType: 'Tipo de Arquivo',
+      },
+      wikipediaEntry: {
+        title: 'Título',
+        language: 'Idioma',
+        webpageURL: 'URL da Página da Web',
+        editorContributor: 'Editor/Contribuidor',
+        lastEditDate: 'Data da Última Edição',
+        summaryIntroduction: 'Resumo/Introdução',
+      },
+      notion: {
+        title: 'Título',
+        language: 'Idioma',
+        author: 'Autor',
+        createdTime: 'Data de Criação',
+        lastModifiedTime: 'Data da Última Modificação',
+        url: 'URL',
+        tag: 'Tag',
+        description: 'Descrição',
+      },
+      github: {
+        repoName: 'Nome do Repositório',
+        repoDesc: 'Descrição do Repositório',
+        repoOwner: 'Proprietário do Repositório',
+        fileName: 'Nome do Arquivo',
+        filePath: 'Caminho do Arquivo',
+        programmingLang: 'Linguagem de Programação',
+        url: 'URL',
+        license: 'Licença',
+        lastCommitTime: 'Data do Último Commit',
+        lastCommitAuthor: 'Autor do Último Commit',
+      },
+      originInfo: {
+        originalFilename: 'Nome do arquivo original',
+        originalFileSize: 'Tamanho do arquivo original',
+        uploadDate: 'Data de envio',
+        lastUpdateDate: 'Data da última atualização',
+        source: 'Fonte',
+      },
+      technicalParameters: {
+        segmentSpecification: 'Especificação dos fragmentos',
+        segmentLength: 'Comprimento dos fragmentos',
+        avgParagraphLength: 'Comprimento médio do parágrafo',
+        paragraphs: 'Parágrafos',
+        hitCount: 'Contagem de recuperação',
+        embeddingTime: 'Tempo de incorporação',
+        embeddedSpend: 'Tempo gasto na incorporação',
+      },
+    },
+    languageMap: {
+      zh: 'Chinês',
+      en: 'Inglês',
+      es: 'Espanhol',
+      fr: 'Francês',
+      de: 'Alemão',
+      ja: 'Japonês',
+      ko: 'Coreano',
+      ru: 'Russo',
+      ar: 'Árabe',
+      pt: 'Português',
+      it: 'Italiano',
+      nl: 'Holandês',
+      pl: 'Polonês',
+      sv: 'Sueco',
+      tr: 'Turco',
+      he: 'Hebraico',
+      hi: 'Hindi',
+      da: 'Dinamarquês',
+      fi: 'Finlandês',
+      no: 'Norueguês',
+      hu: 'Húngaro',
+      el: 'Grego',
+      cs: 'Tcheco',
+      th: 'Tailandês',
+      id: 'Indonésio',
+    },
+    categoryMap: {
+      book: {
+        fiction: 'Ficção',
+        biography: 'Biografia',
+        history: 'História',
+        science: 'Ciência',
+        technology: 'Tecnologia',
+        education: 'Educação',
+        philosophy: 'Filosofia',
+        religion: 'Religião',
+        socialSciences: 'Ciências Sociais',
+        art: 'Arte',
+        travel: 'Viagem',
+        health: 'Saúde',
+        selfHelp: 'Autoajuda',
+        businessEconomics: 'Negócios/Economia',
+        cooking: 'Culinária',
+        childrenYoungAdults: 'Crianças/Jovens Adultos',
+        comicsGraphicNovels: 'Quadrinhos/Graphic Novels',
+        poetry: 'Poesia',
+        drama: 'Drama',
+        other: 'Outro',
+      },
+      personalDoc: {
+        notes: 'Notas',
+        blogDraft: 'Rascunho de Blog',
+        diary: 'Diário',
+        researchReport: 'Relatório de Pesquisa',
+        bookExcerpt: 'Trecho de Livro',
+        schedule: 'Agenda',
+        list: 'Lista',
+        projectOverview: 'Visão Geral do Projeto',
+        photoCollection: 'Coleção de Fotos',
+        creativeWriting: 'Escrita Criativa',
+        codeSnippet: 'Trecho de Código',
+        designDraft: 'Rascunho de Design',
+        personalResume: 'Currículo Pessoal',
+        other: 'Outro',
+      },
+      businessDoc: {
+        meetingMinutes: 'Minutos de Reunião',
+        researchReport: 'Relatório de Pesquisa',
+        proposal: 'Proposta',
+        employeeHandbook: 'Manual do Funcionário',
+        trainingMaterials: 'Materiais de Treinamento',
+        requirementsDocument: 'Documento de Requisitos',
+        designDocument: 'Documento de Design',
+        productSpecification: 'Especificação do Produto',
+        financialReport: 'Relatório Financeiro',
+        marketAnalysis: 'Análise de Mercado',
+        projectPlan: 'Plano de Projeto',
+        teamStructure: 'Estrutura da Equipe',
+        policiesProcedures: 'Políticas e Procedimentos',
+        contractsAgreements: 'Contratos e Acordos',
+        emailCorrespondence: 'Correspondência por E-mail',
+        other: 'Outro',
+      },
+    },
+  },
+  embedding: {
+    processing: 'Processando incorporação...',
+    paused: 'Incorporação pausada',
+    completed: 'Incorporação concluída',
+    error: 'Erro na incorporação',
+    docName: 'Pré-processamento do documento',
+    mode: 'Regra de segmentação',
+    segmentLength: 'Comprimento dos fragmentos',
+    textCleaning: 'Definição prévia e limpeza de texto',
+    segments: 'Parágrafos',
+    highQuality: 'Modo de alta qualidade',
+    economy: 'Modo econômico',
+    estimate: 'Consumo estimado',
+    stop: 'Parar processamento',
+    resume: 'Retomar processamento',
+    automatic: 'Automático',
+    custom: 'Personalizado',
+    previewTip: 'A visualização do parágrafo estará disponível após a incorporação ser concluída',
+  },
+  segment: {
+    paragraphs: 'Parágrafos',
+    keywords: 'Palavras-chave',
+    addKeyWord: 'Adicionar palavra-chave',
+    keywordError: 'O comprimento máximo da palavra-chave é 20',
+    characters: 'caracteres',
+    hitCount: 'Contagem de recuperação',
+    vectorHash: 'Hash do vetor: ',
+    questionPlaceholder: 'adicionar pergunta aqui',
+    questionEmpty: 'A pergunta não pode estar vazia',
+    answerPlaceholder: 'adicionar resposta aqui',
+    answerEmpty: 'A resposta não pode estar vazia',
+    contentPlaceholder: 'adicionar conteúdo aqui',
+    contentEmpty: 'O conteúdo não pode estar vazio',
+    newTextSegment: 'Novo fragmento de texto',
+    newQaSegment: 'Novo fragmento de P&R',
+    delete: 'Excluir este fragmento?',
+  },
+}
+
+export default translation

+ 28 - 0
web/i18n/lang/dataset-hit-testing.pt.ts

@@ -0,0 +1,28 @@
+const translation = {
+  title: 'Teste de Recuperação',
+  desc: 'Teste o efeito de recuperação do conhecimento com base no texto de consulta fornecido.',
+  dateTimeFormat: 'MM/DD/YYYY hh:mm A',
+  recents: 'Recentes',
+  table: {
+    header: {
+      source: 'Origem',
+      text: 'Texto',
+      time: 'Hora',
+    },
+  },
+  input: {
+    title: 'Texto de origem',
+    placeholder: 'Digite um texto, uma frase declarativa curta é recomendada.',
+    countWarning: 'Até 200 caracteres.',
+    indexWarning: 'Somente conhecimento de alta qualidade.',
+    testing: 'Testando',
+  },
+  hit: {
+    title: 'PARÁGRAFOS DE RECUPERAÇÃO',
+    emptyTip: 'Os resultados do teste de recuperação serão exibidos aqui',
+  },
+  noRecentTip: 'Nenhum resultado de consulta recente aqui',
+  viewChart: 'Ver GRÁFICO DE VETORES',
+}
+
+export default translation

+ 33 - 0
web/i18n/lang/dataset-settings.pt.ts

@@ -0,0 +1,33 @@
+const translation = {
+  title: 'Configurações do conhecimento',
+  desc: 'Aqui você pode modificar as propriedades e métodos de trabalho do conhecimento.',
+  form: {
+    name: 'Nome do conhecimento',
+    namePlaceholder: 'Por favor, insira o nome do conhecimento',
+    nameError: 'O nome não pode estar vazio',
+    desc: 'Descrição do conhecimento',
+    descInfo: 'Por favor, escreva uma descrição textual clara para delinear o conteúdo do conhecimento. Esta descrição será usada como base para a correspondência ao selecionar entre vários conhecimentos para inferência.',
+    descPlaceholder: 'Descreva o que está neste conhecimento. Uma descrição detalhada permite que a IA acesse o conteúdo do conhecimento de forma oportuna. Se estiver vazio, o Dify usará a estratégia de correspondência padrão.',
+    descWrite: 'Aprenda como escrever uma boa descrição do conhecimento.',
+    permissions: 'Permissões',
+    permissionsOnlyMe: 'Apenas eu',
+    permissionsAllMember: 'Todos os membros da equipe',
+    indexMethod: 'Método de indexação',
+    indexMethodHighQuality: 'Alta qualidade',
+    indexMethodHighQualityTip: 'Chame a interface de incorporação da OpenAI para processamento, fornecendo maior precisão quando os usuários consultam.',
+    indexMethodEconomy: 'Econômico',
+    indexMethodEconomyTip: 'Use motores de vetor offline, índices de palavras-chave, etc. para reduzir a precisão sem gastar tokens.',
+    embeddingModel: 'Modelo de incorporação',
+    embeddingModelTip: 'Altere o modelo incorporado, por favor, vá para ',
+    embeddingModelTipLink: 'Configurações',
+    retrievalSetting: {
+      title: 'Configuração de recuperação',
+      learnMore: 'Saiba mais',
+      description: ' sobre o método de recuperação.',
+      longDescription: ' sobre o método de recuperação, você pode alterar isso a qualquer momento nas configurações do conhecimento.',
+    },
+    save: 'Salvar',
+  },
+}
+
+export default translation

+ 45 - 0
web/i18n/lang/dataset.pt.ts

@@ -0,0 +1,45 @@
+const translation = {
+  documentCount: ' documentos',
+  wordCount: 'k palavras',
+  appCount: ' aplicativos vinculados',
+  createDataset: 'Criar Conhecimento',
+  createDatasetIntro: 'Importe seus próprios dados de texto ou escreva dados em tempo real via Webhook para aprimoramento de contexto LLM.',
+  deleteDatasetConfirmTitle: 'Excluir este Conhecimento?',
+  deleteDatasetConfirmContent:
+    'A exclusão do Conhecimento é irreversível. Os usuários não poderão mais acessar seu Conhecimento e todas as configurações e registros de prompt serão excluídos permanentemente.',
+  datasetDeleted: 'Conhecimento excluído',
+  datasetDeleteFailed: 'Falha ao excluir o Conhecimento',
+  didYouKnow: 'Você sabia?',
+  intro1: 'O Conhecimento pode ser integrado ao aplicativo Dify ',
+  intro2: 'como um contexto',
+  intro3: ',',
+  intro4: 'ou pode ser criado',
+  intro5: ' como um plug-in de índice ChatGPT independente para publicação',
+  unavailable: 'Indisponível',
+  unavailableTip: 'O modelo de incorporação não está disponível, o modelo de incorporação padrão precisa ser configurado',
+  datasets: 'CONHECIMENTO',
+  datasetsApi: 'API',
+  retrieval: {
+    semantic_search: {
+      title: 'Pesquisa Vetorial',
+      description: 'Gere incorporações de consulta e pesquise o trecho de texto mais semelhante à sua representação vetorial.',
+    },
+    full_text_search: {
+      title: 'Pesquisa de Texto Completo',
+      description: 'Indexe todos os termos no documento, permitindo que os usuários pesquisem qualquer termo e recuperem trechos de texto relevantes contendo esses termos.',
+    },
+    hybrid_search: {
+      title: 'Pesquisa Híbrida',
+      description: 'Execute pesquisas de texto completo e pesquisas vetoriais simultaneamente, reclassifique para selecionar a melhor correspondência para a consulta do usuário. A configuração da API do modelo de reclassificação é necessária.',
+      recommend: 'Recomendar',
+    },
+    invertedIndex: {
+      title: 'Índice Invertido',
+      description: 'O Índice Invertido é uma estrutura usada para recuperação eficiente. Organizado por termos, cada termo aponta para documentos ou páginas da web que o contêm.',
+    },
+    change: 'Alterar',
+    changeRetrievalMethod: 'Alterar método de recuperação',
+  },
+}
+
+export default translation

+ 80 - 0
web/i18n/lang/explore.pt.ts

@@ -0,0 +1,80 @@
+const translation = {
+  title: 'Minhas Aplicações',
+  sidebar: {
+    discovery: 'Descoberta',
+    chat: 'Chat',
+    workspace: 'Espaço de Trabalho',
+    action: {
+      pin: 'Fixar',
+      unpin: 'Desafixar',
+      rename: 'Renomear',
+      delete: 'Excluir',
+    },
+    delete: {
+      title: 'Excluir aplicativo',
+      content: 'Tem certeza de que deseja excluir este aplicativo?',
+    },
+  },
+  apps: {
+    title: 'Explorar Aplicações por Dify',
+    description: 'Use esses aplicativos modelo instantaneamente ou personalize seus próprios aplicativos com base nos modelos.',
+    allCategories: 'Todas as Categorias',
+  },
+  appCard: {
+    addToWorkspace: 'Adicionar ao Espaço de Trabalho',
+    customize: 'Personalizar',
+  },
+  appCustomize: {
+    title: 'Criar aplicativo a partir de {{name}}',
+    subTitle: 'Ícone e nome do aplicativo',
+    nameRequired: 'O nome do aplicativo é obrigatório',
+  },
+  category: {
+    Assistant: 'Assistente',
+    Writing: 'Escrita',
+    Translate: 'Traduzir',
+    Programming: 'Programação',
+    HR: 'RH',
+  },
+  universalChat: {
+    welcome: 'Iniciar chat com Dify',
+    welcomeDescribe: 'Seu companheiro de conversa de IA para assistência personalizada',
+    model: 'Modelo',
+    plugins: {
+      name: 'Plugins',
+      google_search: {
+        name: 'Pesquisa do Google',
+        more: {
+          left: 'Ative o plugin, ',
+          link: 'configure sua chave SerpAPI',
+          right: ' primeiro.',
+        },
+      },
+      web_reader: {
+        name: 'Leitor da Web',
+        description: 'Obtenha informações necessárias de qualquer link da web',
+      },
+      wikipedia: {
+        name: 'Wikipedia',
+      },
+    },
+    thought: {
+      show: 'Mostrar',
+      hide: 'Ocultar',
+      processOfThought: ' o processo de pensamento',
+      res: {
+        webReader: {
+          normal: 'Lendo {url}',
+          hasPageInfo: 'Lendo próxima página de {url}',
+        },
+        google: 'Pesquisando no Google {{query}}',
+        wikipedia: 'Pesquisando na Wikipedia {{query}}',
+        dataset: 'Recuperando Conhecimento {datasetName}',
+        date: 'Pesquisando data',
+      },
+    },
+    viewConfigDetailTip: 'Na conversa, não é possível alterar as configurações acima',
+  },
+}
+
+export default translation

+ 4 - 0
web/i18n/lang/layout.pt.ts

@@ -0,0 +1,4 @@
+const translation = {
+}
+
+export default translation

+ 57 - 0
web/i18n/lang/login.pt.ts

@@ -0,0 +1,57 @@
+const translation = {
+  pageTitle: 'Oi, vamos começar!👋',
+  welcome: 'Bem-vindo ao Dify, faça login para continuar.',
+  email: 'Endereço de e-mail',
+  emailPlaceholder: 'Seu e-mail',
+  password: 'Senha',
+  passwordPlaceholder: 'Sua senha',
+  name: 'Nome de usuário',
+  namePlaceholder: 'Seu nome de usuário',
+  forget: 'Esqueceu sua senha?',
+  signBtn: 'Entrar',
+  installBtn: 'Configuração',
+  setAdminAccount: 'Configurando uma conta de administrador',
+  setAdminAccountDesc: 'Privilégios máximos para a conta de administrador, que pode ser usada para criar aplicativos e gerenciar provedores LLM, etc.',
+  createAndSignIn: 'Criar e entrar',
+  oneMoreStep: 'Mais um passo',
+  createSample: 'Com base nessas informações, criaremos um aplicativo de exemplo para você',
+  invitationCode: 'Código de convite',
+  invitationCodePlaceholder: 'Seu código de convite',
+  interfaceLanguage: 'Idioma da interface',
+  timezone: 'Fuso horário',
+  go: 'Ir para o Dify',
+  sendUsMail: 'Envie-nos um e-mail com sua introdução e cuidaremos do pedido de convite.',
+  acceptPP: 'Li e aceito a política de privacidade',
+  reset: 'Execute o seguinte comando para redefinir sua senha',
+  withGitHub: 'Continuar com o GitHub',
+  withGoogle: 'Continuar com o Google',
+  rightTitle: 'Desbloqueie todo o potencial do LLM',
+  rightDesc: 'Crie aplicativos de IA visualmente cativantes, operáveis e aprimoráveis sem esforço.',
+  tos: 'Termos de Serviço',
+  pp: 'Política de Privacidade',
+  tosDesc: 'Ao se inscrever, você concorda com nossos',
+  donthave: 'Não tem?',
+  invalidInvitationCode: 'Código de convite inválido',
+  accountAlreadyInited: 'Conta já iniciada',
+  error: {
+    emailEmpty: 'O endereço de e-mail é obrigatório',
+    emailInValid: 'Digite um endereço de e-mail válido',
+    nameEmpty: 'O nome é obrigatório',
+    passwordEmpty: 'A senha é obrigatória',
+    passwordInvalid: 'A senha deve conter letras e números e ter um comprimento maior que 8',
+  },
+  license: {
+    tip: 'Antes de começar a usar a Edição Comunitária do Dify, leia a',
+    link: 'Licença de código aberto do GitHub',
+  },
+  join: 'Participar',
+  joinTipStart: 'Convidamos você a participar da',
+  joinTipEnd: 'equipe no Dify',
+  invalid: 'O link expirou',
+  explore: 'Explorar o Dify',
+  activatedTipStart: 'Você se juntou à equipe',
+  activatedTipEnd: '',
+  activated: 'Entrar agora',
+}
+
+export default translation

+ 4 - 0
web/i18n/lang/register.pt.ts

@@ -0,0 +1,4 @@
+const translation = {
+}
+
+export default translation

+ 74 - 0
web/i18n/lang/share-app.pt.ts

@@ -0,0 +1,74 @@
+const translation = {
+  common: {
+    welcome: 'Bem-vindo ao usar',
+    appUnavailable: 'O aplicativo não está disponível',
+    appUnkonwError: 'O aplicativo encontrou um erro desconhecido',
+  },
+  chat: {
+    newChat: 'Nova conversa',
+    pinnedTitle: 'Fixado',
+    unpinnedTitle: 'Conversas',
+    newChatDefaultName: 'Nova conversa',
+    resetChat: 'Redefinir conversa',
+    powerBy: 'Desenvolvido por',
+    prompt: 'Prompt',
+    privatePromptConfigTitle: 'Configurações da conversa',
+    publicPromptConfigTitle: 'Prompt inicial',
+    configStatusDes: 'Antes de começar, você pode modificar as configurações da conversa',
+    configDisabled:
+      'As configurações da sessão anterior foram usadas para esta sessão.',
+    startChat: 'Iniciar conversa',
+    privacyPolicyLeft:
+      'Por favor, leia a ',
+    privacyPolicyMiddle:
+      'política de privacidade',
+    privacyPolicyRight:
+      ' fornecida pelo desenvolvedor do aplicativo.',
+    deleteConversation: {
+      title: 'Excluir conversa',
+      content: 'Tem certeza de que deseja excluir esta conversa?',
+    },
+    tryToSolve: 'Tente resolver',
+    temporarySystemIssue: 'Desculpe, problema temporário do sistema.',
+  },
+  generation: {
+    tabs: {
+      create: 'Executar uma vez',
+      batch: 'Executar em lote',
+      saved: 'Salvo',
+    },
+    savedNoData: {
+      title: 'Você ainda não salvou um resultado!',
+      description: 'Comece a gerar conteúdo e encontre seus resultados salvos aqui.',
+      startCreateContent: 'Começar a criar conteúdo',
+    },
+    title: 'Completar com IA',
+    queryTitle: 'Consultar conteúdo',
+    completionResult: 'Resultado da conclusão',
+    queryPlaceholder: 'Escreva sua consulta...',
+    run: 'Executar',
+    copy: 'Copiar',
+    resultTitle: 'Completar com IA',
+    noData: 'A IA fornecerá o que você deseja aqui.',
+    csvUploadTitle: 'Arraste e solte seu arquivo CSV aqui ou ',
+    browse: 'navegue',
+    csvStructureTitle: 'O arquivo CSV deve seguir a seguinte estrutura:',
+    downloadTemplate: 'Baixe o modelo aqui',
+    field: 'Campo',
+    batchFailed: {
+      info: '{{num}} execuções falharam',
+      retry: 'Tentar novamente',
+      outputPlaceholder: 'Nenhum conteúdo de saída',
+    },
+    errorMsg: {
+      empty: 'Por favor, insira conteúdo no arquivo enviado.',
+      fileStructNotMatch: 'O arquivo CSV enviado não corresponde à estrutura.',
+      emptyLine: 'A linha {{rowIndex}} está vazia',
+      invalidLine: 'Linha {{rowIndex}}: o valor de {{varName}} não pode estar vazio',
+      moreThanMaxLengthLine: 'Linha {{rowIndex}}: o valor de {{varName}} não pode ter mais de {{maxLength}} caracteres',
+      atLeastOne: 'Por favor, insira pelo menos uma linha no arquivo enviado.',
+    },
+  },
+}
+
+export default translation

+ 102 - 0
web/i18n/lang/tools.pt.ts

@@ -0,0 +1,102 @@
+const translation = {
+  title: 'Ferramentas',
+  createCustomTool: 'Criar Ferramenta Personalizada',
+  type: {
+    all: 'Todas',
+    builtIn: 'Integradas',
+    custom: 'Personal...',
+  },
+  contribute: {
+    line1: 'Estou interessado em ',
+    line2: 'contribuir com ferramentas para o Dify.',
+    viewGuide: 'Ver o guia',
+  },
+  author: 'Por',
+  auth: {
+    unauthorized: 'Não autorizado',
+    authorized: 'Autorizado',
+    setup: 'Configurar autorização para usar',
+    setupModalTitle: 'Configurar Autorização',
+    setupModalTitleDescription: 'Após configurar as credenciais, todos os membros do espaço de trabalho podem usar essa ferramenta ao orquestrar aplicativos.',
+  },
+  includeToolNum: '{{num}} ferramentas incluídas',
+  addTool: 'Adicionar Ferramenta',
+  createTool: {
+    title: 'Criar Ferramenta Personalizada',
+    editAction: 'Configurar',
+    editTitle: 'Editar Ferramenta Personalizada',
+    name: 'Nome',
+    toolNamePlaceHolder: 'Digite o nome da ferramenta',
+    schema: 'Esquema',
+    schemaPlaceHolder: 'Digite seu esquema OpenAPI aqui',
+    viewSchemaSpec: 'Ver a Especificação OpenAPI-Swagger',
+    importFromUrl: 'Importar de URL',
+    importFromUrlPlaceHolder: 'https://...',
+    urlError: 'Digite uma URL válida',
+    examples: 'Exemplos',
+    exampleOptions: {
+      json: 'Clima (JSON)',
+      yaml: 'Pet Store (YAML)',
+      blankTemplate: 'Modelo em Branco',
+    },
+    availableTools: {
+      title: 'Ferramentas Disponíveis',
+      name: 'Nome',
+      description: 'Descrição',
+      method: 'Método',
+      path: 'Caminho',
+      action: 'Ações',
+      test: 'Testar',
+    },
+    authMethod: {
+      title: 'Método de Autorização',
+      type: 'Tipo de Autorização',
+      types: {
+        none: 'Nenhum',
+        api_key: 'Chave de API',
+      },
+      key: 'Chave',
+      value: 'Valor',
+    },
+    privacyPolicy: 'Política de Privacidade',
+    privacyPolicyPlaceholder: 'Digite a política de privacidade',
+  },
+  test: {
+    title: 'Testar',
+    parametersValue: 'Parâmetros e Valor',
+    parameters: 'Parâmetros',
+    value: 'Valor',
+    testResult: 'Resultados do Teste',
+    testResultPlaceholder: 'O resultado do teste será exibido aqui',
+  },
+  thought: {
+    using: 'Usando',
+    used: 'Usado',
+    requestTitle: 'Requisição para',
+    responseTitle: 'Resposta de',
+  },
+  setBuiltInTools: {
+    info: 'Informações',
+    setting: 'Configuração',
+    toolDescription: 'Descrição da Ferramenta',
+    parameters: 'parâmetros',
+    string: 'string',
+    number: 'número',
+    required: 'Obrigatório',
+    infoAndSetting: 'Informações e Configurações',
+  },
+  noCustomTool: {
+    title: 'Nenhuma ferramenta personalizada',
+    content: 'Você não possui ferramentas personalizadas. ',
+    createTool: 'Criar Ferramenta',
+  },
+  noSearchRes: {
+    title: 'Desculpe, sem resultados!',
+    content: 'Não encontramos nenhuma ferramenta que corresponda à sua pesquisa.',
+    reset: 'Redefinir Pesquisa',
+  },
+  builtInPromptTitle: 'Prompt',
+  toolRemoved: 'Ferramenta removida',
+}
+
+export default translation

+ 2 - 5
web/models/common.ts

@@ -1,3 +1,5 @@
+import type { I18nText } from '@/utils/language'
+
 export type CommonResponse = {
   result: 'success' | 'fail'
 }
@@ -204,11 +206,6 @@ export type ApiBasedExtension = {
   api_key?: string
 }
 
-export type I18nText = {
-  'en-US': string
-  'zh-Hans': string
-}
-
 export type CodeBasedExtensionForm = {
   type: string
   label: I18nText

+ 1 - 1
web/types/app.ts

@@ -1,5 +1,6 @@
 import type { AnnotationReplyConfig, ChatPromptConfig, CompletionPromptConfig, DatasetConfigs, PromptMode } from '@/models/debug'
 import type { CollectionType } from '@/app/components/tools/types'
+import type { LanguagesSupported } from '@/utils/language'
 export enum ProviderType {
   openai = 'openai',
   anthropic = 'anthropic',
@@ -213,7 +214,6 @@ export type ModelConfig = {
   files?: VisionFile[]
 }
 
-export const LanguagesSupported = ['zh-Hans', 'en-US'] as const
 export type Language = typeof LanguagesSupported[number]
 
 /**

+ 87 - 3
web/utils/language.ts

@@ -1,8 +1,10 @@
-type Item = {
+export type Item = {
   value: number | string
   name: string
 }
-export const languages: Item[] = [
+
+export const LanguagesSupported = ['en-US', 'zh-Hans', 'pt-BR', 'es-ES', 'fr-FR', 'de-DE', 'ja-JP', 'ko-KR', 'ru-RU', 'it-IT']
+export const languages = [
   {
     value: 'en-US',
     name: 'English(United States)',
@@ -11,9 +13,91 @@ export const languages: Item[] = [
     value: 'zh-Hans',
     name: '简体中文',
   },
+  {
+    value: 'pt-BR',
+    name: 'Português(Brasil)',
+  },
+  // {
+  //   value: 'es-ES',
+  //   name: 'Español(España)',
+  // },
+  // {
+  //   value: 'fr-FR',
+  //   name: 'Français(France)',
+  // },
+  // {
+  //   value: 'de-DE',
+  //   name: 'Deutsch(Deutschland)',
+  // },
+  // {
+  //   value: 'ja-JP',
+  //   name: '日本語(日本)',
+  // },
+  // {
+  //   value: 'ko-KR',
+  //   name: '한국어(대한민국)',
+  // },
+  // {
+  //   value: 'ru-RU',
+  //   name: 'Русский(Россия)',
+  // },
+  // {
+  //   value: 'it-IT',
+  //   name: 'Italiano(Italia)',
+  // },
 ]
 
+export const getModelRuntimeSupported = (locale: string) => {
+  if (locale === 'zh-Hans')
+    return locale.replace('-', '_')
+
+  return LanguagesSupported[0].replace('-', '_')
+}
 export const languageMaps = {
-  'en': 'en-US',
+  'en-US': 'en-US',
   'zh-Hans': 'zh-Hans',
+  'pt-BR': 'pt-BR',
+  'es-ES': 'es-ES',
+  'fr-FR': 'fr-FR',
+  'de-DE': 'de-DE',
+  'ja-JP': 'ja-JP',
+  'ko-KR': 'ko-KR',
+  'ru-RU': 'ru-RU',
+  'it-IT': 'it-IT',
+}
+
+export type I18nText = {
+  'en-US': string
+  'zh-Hans': string
+  'pt-BR': string
+  'es-ES': string
+  'fr-FR': string
+  'de-DE': string
+  'ja-JP': string
+  'ko-KR': string
+  'ru-RU': string
+  'it-IT': string
+}
+
+export const NOTICE_I18N = {
+  title: {
+    'en-US': 'Important Notice',
+    'zh-Hans': '重要公告',
+    'pt-BR': 'Aviso Importante',
+    'es-ES': 'Aviso Importante',
+    'fr-FR': 'Avis important',
+    'de-DE': 'Wichtiger Hinweis',
+    'ja-JP': '重要なお知らせ',
+    'ko-KR': '중요 공지',
+  },
+  desc: {
+    'en-US': 'Our system will be unavailable from 19:00 to 24:00 UTC on August 28 for an upgrade. For questions, kindly contact our support team (support@dify.ai). We value your patience.',
+    'zh-Hans': '为了有效提升数据检索能力及稳定性,Dify 将于 2023 年 8 月 29 日 03:00 至 08:00 期间进行服务升级,届时 Dify 云端版及应用将无法访问。感谢您的耐心与支持。',
+    'pt-BR': 'Our system will be unavailable from 19:00 to 24:00 UTC on August 28 for an upgrade. For questions, kindly contact our support team (support@dify.ai). We value your patience.',
+    'es-ES': 'Our system will be unavailable from 19:00 to 24:00 UTC on August 28 for an upgrade. For questions, kindly contact our support team (support@dify.ai). We value your patience.',
+    'fr-FR': 'Our system will be unavailable from 19:00 to 24:00 UTC on August 28 for an upgrade. For questions, kindly contact our support team (support@dify.ai). We value your patience.',
+    'de-DE': 'Our system will be unavailable from 19:00 to 24:00 UTC on August 28 for an upgrade. For questions, kindly contact our support team (support@dify.ai). We value your patience.',
+    'ja-JP': 'Our system will be unavailable from 19:00 to 24:00 UTC on August 28 for an upgrade. For questions, kindly contact our support team (support@dify.ai). We value your patience.',
+    'ko-KR': 'Our system will be unavailable from 19:00 to 24:00 UTC on August 28 for an upgrade. For questions, kindly contact our support team (support@dify.ai). We value your patience.',
+  },
 }