Merge branch 'main' of https://github.com/ConnectAI-E/ChatGPT-Next-Web into feature/jest
|
@ -66,4 +66,4 @@ ANTHROPIC_API_VERSION=
|
||||||
ANTHROPIC_URL=
|
ANTHROPIC_URL=
|
||||||
|
|
||||||
### (optional)
|
### (optional)
|
||||||
WHITE_WEBDEV_ENDPOINTS=
|
WHITE_WEBDAV_ENDPOINTS=
|
|
@ -1,4 +1,7 @@
|
||||||
{
|
{
|
||||||
"extends": "next/core-web-vitals",
|
"extends": "next/core-web-vitals",
|
||||||
"plugins": ["prettier"]
|
"plugins": ["prettier", "unused-imports"],
|
||||||
|
"rules": {
|
||||||
|
"unused-imports/no-unused-imports": "warn"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,7 @@ jobs:
|
||||||
run: npm install --global vercel@latest
|
run: npm install --global vercel@latest
|
||||||
|
|
||||||
- name: Cache dependencies
|
- name: Cache dependencies
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v4
|
||||||
id: cache-npm
|
id: cache-npm
|
||||||
with:
|
with:
|
||||||
path: ~/.npm
|
path: ~/.npm
|
||||||
|
|
39
README.md
|
@ -12,15 +12,18 @@ One-Click to get a well-designed cross-platform ChatGPT web UI, with GPT3, GPT4
|
||||||
|
|
||||||
一键免费部署你的跨平台私人 ChatGPT 应用, 支持 GPT3, GPT4 & Gemini Pro 模型。
|
一键免费部署你的跨平台私人 ChatGPT 应用, 支持 GPT3, GPT4 & Gemini Pro 模型。
|
||||||
|
|
||||||
|
[![Saas][Saas-image]][saas-url]
|
||||||
[![Web][Web-image]][web-url]
|
[![Web][Web-image]][web-url]
|
||||||
[![Windows][Windows-image]][download-url]
|
[![Windows][Windows-image]][download-url]
|
||||||
[![MacOS][MacOS-image]][download-url]
|
[![MacOS][MacOS-image]][download-url]
|
||||||
[![Linux][Linux-image]][download-url]
|
[![Linux][Linux-image]][download-url]
|
||||||
|
|
||||||
[Web App](https://app.nextchat.dev/) / [Desktop App](https://github.com/Yidadaa/ChatGPT-Next-Web/releases) / [Discord](https://discord.gg/YCkeafCafC) / [Enterprise Edition](#enterprise-edition) / [Twitter](https://twitter.com/NextChatDev)
|
[NextChatAI](https://nextchat.dev/chat?utm_source=readme) / [Web App](https://app.nextchat.dev) / [Desktop App](https://github.com/Yidadaa/ChatGPT-Next-Web/releases) / [Discord](https://discord.gg/YCkeafCafC) / [Enterprise Edition](#enterprise-edition) / [Twitter](https://twitter.com/NextChatDev)
|
||||||
|
|
||||||
[网页版](https://app.nextchat.dev/) / [客户端](https://github.com/Yidadaa/ChatGPT-Next-Web/releases) / [企业版](#%E4%BC%81%E4%B8%9A%E7%89%88) / [反馈](https://github.com/Yidadaa/ChatGPT-Next-Web/issues)
|
[NextChatAI](https://nextchat.dev/chat) / [网页版](https://app.nextchat.dev) / [客户端](https://github.com/Yidadaa/ChatGPT-Next-Web/releases) / [企业版](#%E4%BC%81%E4%B8%9A%E7%89%88) / [反馈](https://github.com/Yidadaa/ChatGPT-Next-Web/issues)
|
||||||
|
|
||||||
|
[saas-url]: https://nextchat.dev/chat?utm_source=readme
|
||||||
|
[saas-image]: https://img.shields.io/badge/NextChat-Saas-green?logo=microsoftedge
|
||||||
[web-url]: https://app.nextchat.dev/
|
[web-url]: https://app.nextchat.dev/
|
||||||
[download-url]: https://github.com/Yidadaa/ChatGPT-Next-Web/releases
|
[download-url]: https://github.com/Yidadaa/ChatGPT-Next-Web/releases
|
||||||
[Web-image]: https://img.shields.io/badge/Web-PWA-orange?logo=microsoftedge
|
[Web-image]: https://img.shields.io/badge/Web-PWA-orange?logo=microsoftedge
|
||||||
|
@ -30,6 +33,8 @@ One-Click to get a well-designed cross-platform ChatGPT web UI, with GPT3, GPT4
|
||||||
|
|
||||||
[<img src="https://vercel.com/button" alt="Deploy on Zeabur" height="30">](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FChatGPTNextWeb%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=nextchat&repository-name=NextChat) [<img src="https://zeabur.com/button.svg" alt="Deploy on Zeabur" height="30">](https://zeabur.com/templates/ZBUEFA) [<img src="https://gitpod.io/button/open-in-gitpod.svg" alt="Open in Gitpod" height="30">](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web)
|
[<img src="https://vercel.com/button" alt="Deploy on Zeabur" height="30">](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FChatGPTNextWeb%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=nextchat&repository-name=NextChat) [<img src="https://zeabur.com/button.svg" alt="Deploy on Zeabur" height="30">](https://zeabur.com/templates/ZBUEFA) [<img src="https://gitpod.io/button/open-in-gitpod.svg" alt="Open in Gitpod" height="30">](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web)
|
||||||
|
|
||||||
|
[<img src="https://github.com/user-attachments/assets/903482d4-3e87-4134-9af1-f2588fa90659" height="60" width="288" >](https://monica.im/?utm=nxcrp)
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
## Enterprise Edition
|
## Enterprise Edition
|
||||||
|
@ -89,12 +94,13 @@ For enterprise inquiries, please contact: **business@nextchat.dev**
|
||||||
- [x] Desktop App with tauri
|
- [x] Desktop App with tauri
|
||||||
- [x] Self-host Model: Fully compatible with [RWKV-Runner](https://github.com/josStorer/RWKV-Runner), as well as server deployment of [LocalAI](https://github.com/go-skynet/LocalAI): llama/gpt4all/rwkv/vicuna/koala/gpt4all-j/cerebras/falcon/dolly etc.
|
- [x] Self-host Model: Fully compatible with [RWKV-Runner](https://github.com/josStorer/RWKV-Runner), as well as server deployment of [LocalAI](https://github.com/go-skynet/LocalAI): llama/gpt4all/rwkv/vicuna/koala/gpt4all-j/cerebras/falcon/dolly etc.
|
||||||
- [x] Artifacts: Easily preview, copy and share generated content/webpages through a separate window [#5092](https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web/pull/5092)
|
- [x] Artifacts: Easily preview, copy and share generated content/webpages through a separate window [#5092](https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web/pull/5092)
|
||||||
- [x] Plugins: support artifacts, network search, calculator, any other apis etc. [#165](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/165)
|
- [x] Plugins: support network search, calculator, any other apis etc. [#165](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/165) [#5353](https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web/issues/5353)
|
||||||
- [x] artifacts
|
- [x] network search, calculator, any other apis etc. [#165](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/165) [#5353](https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web/issues/5353)
|
||||||
- [ ] network search, network search, calculator, any other apis etc. [#165](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/165)
|
- [ ] local knowledge base
|
||||||
|
|
||||||
## What's New
|
## What's New
|
||||||
|
|
||||||
|
- 🚀 v2.15.0 Now supports Plugins! Read this: [NextChat-Awesome-Plugins](https://github.com/ChatGPTNextWeb/NextChat-Awesome-Plugins)
|
||||||
- 🚀 v2.14.0 Now supports Artifacts & SD
|
- 🚀 v2.14.0 Now supports Artifacts & SD
|
||||||
- 🚀 v2.10.1 support Google Gemini Pro model.
|
- 🚀 v2.10.1 support Google Gemini Pro model.
|
||||||
- 🚀 v2.9.11 you can use azure endpoint now.
|
- 🚀 v2.9.11 you can use azure endpoint now.
|
||||||
|
@ -125,12 +131,13 @@ For enterprise inquiries, please contact: **business@nextchat.dev**
|
||||||
- [x] 使用 tauri 打包桌面应用
|
- [x] 使用 tauri 打包桌面应用
|
||||||
- [x] 支持自部署的大语言模型:开箱即用 [RWKV-Runner](https://github.com/josStorer/RWKV-Runner) ,服务端部署 [LocalAI 项目](https://github.com/go-skynet/LocalAI) llama / gpt4all / rwkv / vicuna / koala / gpt4all-j / cerebras / falcon / dolly 等等,或者使用 [api-for-open-llm](https://github.com/xusenlinzy/api-for-open-llm)
|
- [x] 支持自部署的大语言模型:开箱即用 [RWKV-Runner](https://github.com/josStorer/RWKV-Runner) ,服务端部署 [LocalAI 项目](https://github.com/go-skynet/LocalAI) llama / gpt4all / rwkv / vicuna / koala / gpt4all-j / cerebras / falcon / dolly 等等,或者使用 [api-for-open-llm](https://github.com/xusenlinzy/api-for-open-llm)
|
||||||
- [x] Artifacts: 通过独立窗口,轻松预览、复制和分享生成的内容/可交互网页 [#5092](https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web/pull/5092)
|
- [x] Artifacts: 通过独立窗口,轻松预览、复制和分享生成的内容/可交互网页 [#5092](https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web/pull/5092)
|
||||||
- [x] 插件机制,支持 artifacts,联网搜索、计算器、调用其他平台 api [#165](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/165)
|
- [x] 插件机制,支持`联网搜索`、`计算器`、调用其他平台 api [#165](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/165) [#5353](https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web/issues/5353)
|
||||||
- [x] artifacts
|
- [x] 支持联网搜索、计算器、调用其他平台 api [#165](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/165) [#5353](https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web/issues/5353)
|
||||||
- [ ] 支持联网搜索、计算器、调用其他平台 api [#165](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/165)
|
- [ ] 本地知识库
|
||||||
|
|
||||||
## 最新动态
|
## 最新动态
|
||||||
|
|
||||||
|
- 🚀 v2.15.0 现在支持插件功能了!了解更多:[NextChat-Awesome-Plugins](https://github.com/ChatGPTNextWeb/NextChat-Awesome-Plugins)
|
||||||
- 🚀 v2.14.0 现在支持 Artifacts & SD 了。
|
- 🚀 v2.14.0 现在支持 Artifacts & SD 了。
|
||||||
- 🚀 v2.10.1 现在支持 Gemini Pro 模型。
|
- 🚀 v2.10.1 现在支持 Gemini Pro 模型。
|
||||||
- 🚀 v2.9.11 现在可以使用自定义 Azure 服务了。
|
- 🚀 v2.9.11 现在可以使用自定义 Azure 服务了。
|
||||||
|
@ -168,7 +175,7 @@ We recommend that you follow the steps below to re-deploy:
|
||||||
|
|
||||||
### Enable Automatic Updates
|
### Enable Automatic Updates
|
||||||
|
|
||||||
> If you encounter a failure of Upstream Sync execution, please manually sync fork once.
|
> If you encounter a failure of Upstream Sync execution, please [manually update code](./README.md#manually-updating-code).
|
||||||
|
|
||||||
After forking the project, due to the limitations imposed by GitHub, you need to manually enable Workflows and Upstream Sync Action on the Actions page of the forked project. Once enabled, automatic updates will be scheduled every hour:
|
After forking the project, due to the limitations imposed by GitHub, you need to manually enable Workflows and Upstream Sync Action on the Actions page of the forked project. Once enabled, automatic updates will be scheduled every hour:
|
||||||
|
|
||||||
|
@ -280,6 +287,18 @@ Alibaba Cloud Api Key.
|
||||||
|
|
||||||
Alibaba Cloud Api Url.
|
Alibaba Cloud Api Url.
|
||||||
|
|
||||||
|
### `IFLYTEK_URL` (Optional)
|
||||||
|
|
||||||
|
iflytek Api Url.
|
||||||
|
|
||||||
|
### `IFLYTEK_API_KEY` (Optional)
|
||||||
|
|
||||||
|
iflytek Api Key.
|
||||||
|
|
||||||
|
### `IFLYTEK_API_SECRET` (Optional)
|
||||||
|
|
||||||
|
iflytek Api Secret.
|
||||||
|
|
||||||
### `HIDE_USER_API_KEY` (optional)
|
### `HIDE_USER_API_KEY` (optional)
|
||||||
|
|
||||||
> Default: Empty
|
> Default: Empty
|
||||||
|
@ -324,7 +343,7 @@ For ByteDance: use `modelName@bytedance=deploymentName` to customize model name
|
||||||
|
|
||||||
Change default model
|
Change default model
|
||||||
|
|
||||||
### `WHITE_WEBDEV_ENDPOINTS` (optional)
|
### `WHITE_WEBDAV_ENDPOINTS` (optional)
|
||||||
|
|
||||||
You can use this option if you want to increase the number of webdav service addresses you are allowed to access, as required by the format:
|
You can use this option if you want to increase the number of webdav service addresses you are allowed to access, as required by the format:
|
||||||
- Each address must be a complete endpoint
|
- Each address must be a complete endpoint
|
||||||
|
|
20
README_CN.md
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
一键免费部署你的私人 ChatGPT 网页应用,支持 GPT3, GPT4 & Gemini Pro 模型。
|
一键免费部署你的私人 ChatGPT 网页应用,支持 GPT3, GPT4 & Gemini Pro 模型。
|
||||||
|
|
||||||
[企业版](#%E4%BC%81%E4%B8%9A%E7%89%88) /[演示 Demo](https://chat-gpt-next-web.vercel.app/) / [反馈 Issues](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [加入 Discord](https://discord.gg/zrhvHCr79N)
|
[NextChatAI](https://nextchat.dev/chat?utm_source=readme) / [企业版](#%E4%BC%81%E4%B8%9A%E7%89%88) / [演示 Demo](https://chat-gpt-next-web.vercel.app/) / [反馈 Issues](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [加入 Discord](https://discord.gg/zrhvHCr79N)
|
||||||
|
|
||||||
[<img src="https://vercel.com/button" alt="Deploy on Zeabur" height="30">](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FChatGPTNextWeb%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=nextchat&repository-name=NextChat) [<img src="https://zeabur.com/button.svg" alt="Deploy on Zeabur" height="30">](https://zeabur.com/templates/ZBUEFA) [<img src="https://gitpod.io/button/open-in-gitpod.svg" alt="Open in Gitpod" height="30">](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web)
|
[<img src="https://vercel.com/button" alt="Deploy on Zeabur" height="30">](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FChatGPTNextWeb%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=nextchat&repository-name=NextChat) [<img src="https://zeabur.com/button.svg" alt="Deploy on Zeabur" height="30">](https://zeabur.com/templates/ZBUEFA) [<img src="https://gitpod.io/button/open-in-gitpod.svg" alt="Open in Gitpod" height="30">](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web)
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@
|
||||||
|
|
||||||
### 打开自动更新
|
### 打开自动更新
|
||||||
|
|
||||||
> 如果你遇到了 Upstream Sync 执行错误,请手动 Sync Fork 一次!
|
> 如果你遇到了 Upstream Sync 执行错误,请[手动 Sync Fork 一次](./README_CN.md#手动更新代码)!
|
||||||
|
|
||||||
当你 fork 项目之后,由于 Github 的限制,需要手动去你 fork 后的项目的 Actions 页面启用 Workflows,并启用 Upstream Sync Action,启用之后即可开启每小时定时自动更新:
|
当你 fork 项目之后,由于 Github 的限制,需要手动去你 fork 后的项目的 Actions 页面启用 Workflows,并启用 Upstream Sync Action,启用之后即可开启每小时定时自动更新:
|
||||||
|
|
||||||
|
@ -172,6 +172,20 @@ ByteDance Api Url.
|
||||||
|
|
||||||
阿里云(千问)Api Url.
|
阿里云(千问)Api Url.
|
||||||
|
|
||||||
|
### `IFLYTEK_URL` (可选)
|
||||||
|
|
||||||
|
讯飞星火Api Url.
|
||||||
|
|
||||||
|
### `IFLYTEK_API_KEY` (可选)
|
||||||
|
|
||||||
|
讯飞星火Api Key.
|
||||||
|
|
||||||
|
### `IFLYTEK_API_SECRET` (可选)
|
||||||
|
|
||||||
|
讯飞星火Api Secret.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### `HIDE_USER_API_KEY` (可选)
|
### `HIDE_USER_API_KEY` (可选)
|
||||||
|
|
||||||
如果你不想让用户自行填入 API Key,将此环境变量设置为 1 即可。
|
如果你不想让用户自行填入 API Key,将此环境变量设置为 1 即可。
|
||||||
|
@ -188,7 +202,7 @@ ByteDance Api Url.
|
||||||
|
|
||||||
如果你想禁用从链接解析预制设置,将此环境变量设置为 1 即可。
|
如果你想禁用从链接解析预制设置,将此环境变量设置为 1 即可。
|
||||||
|
|
||||||
### `WHITE_WEBDEV_ENDPOINTS` (可选)
|
### `WHITE_WEBDAV_ENDPOINTS` (可选)
|
||||||
|
|
||||||
如果你想增加允许访问的webdav服务地址,可以使用该选项,格式要求:
|
如果你想增加允许访问的webdav服务地址,可以使用该选项,格式要求:
|
||||||
- 每一个地址必须是一个完整的 endpoint
|
- 每一个地址必须是一个完整的 endpoint
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
ワンクリックで無料であなた専用の ChatGPT ウェブアプリをデプロイ。GPT3、GPT4 & Gemini Pro モデルをサポート。
|
ワンクリックで無料であなた専用の ChatGPT ウェブアプリをデプロイ。GPT3、GPT4 & Gemini Pro モデルをサポート。
|
||||||
|
|
||||||
[企業版](#企業版) / [デモ](https://chat-gpt-next-web.vercel.app/) / [フィードバック](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [Discordに参加](https://discord.gg/zrhvHCr79N)
|
[NextChatAI](https://nextchat.dev/chat?utm_source=readme) / [企業版](#企業版) / [デモ](https://chat-gpt-next-web.vercel.app/) / [フィードバック](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [Discordに参加](https://discord.gg/zrhvHCr79N)
|
||||||
|
|
||||||
[<img src="https://vercel.com/button" alt="Zeaburでデプロイ" height="30">](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FChatGPTNextWeb%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=nextchat&repository-name=NextChat) [<img src="https://zeabur.com/button.svg" alt="Zeaburでデプロイ" height="30">](https://zeabur.com/templates/ZBUEFA) [<img src="https://gitpod.io/button/open-in-gitpod.svg" alt="Gitpodで開く" height="30">](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web)
|
[<img src="https://vercel.com/button" alt="Zeaburでデプロイ" height="30">](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FChatGPTNextWeb%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=nextchat&repository-name=NextChat) [<img src="https://zeabur.com/button.svg" alt="Zeaburでデプロイ" height="30">](https://zeabur.com/templates/ZBUEFA) [<img src="https://gitpod.io/button/open-in-gitpod.svg" alt="Gitpodで開く" height="30">](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web)
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@
|
||||||
|
|
||||||
### 自動更新を開く
|
### 自動更新を開く
|
||||||
|
|
||||||
> Upstream Sync の実行エラーが発生した場合は、手動で Sync Fork してください!
|
> Upstream Sync の実行エラーが発生した場合は、[手動で Sync Fork](./README_JA.md#手動でコードを更新する) してください!
|
||||||
|
|
||||||
プロジェクトを fork した後、GitHub の制限により、fork 後のプロジェクトの Actions ページで Workflows を手動で有効にし、Upstream Sync Action を有効にする必要があります。有効化後、毎時の定期自動更新が可能になります:
|
プロジェクトを fork した後、GitHub の制限により、fork 後のプロジェクトの Actions ページで Workflows を手動で有効にし、Upstream Sync Action を有効にする必要があります。有効化後、毎時の定期自動更新が可能になります:
|
||||||
|
|
||||||
|
@ -193,7 +193,7 @@ ByteDance API の URL。
|
||||||
|
|
||||||
リンクからのプリセット設定解析を無効にしたい場合は、この環境変数を 1 に設定します。
|
リンクからのプリセット設定解析を無効にしたい場合は、この環境変数を 1 に設定します。
|
||||||
|
|
||||||
### `WHITE_WEBDEV_ENDPOINTS` (オプション)
|
### `WHITE_WEBDAV_ENDPOINTS` (オプション)
|
||||||
|
|
||||||
アクセス許可を与える WebDAV サービスのアドレスを追加したい場合、このオプションを使用します。フォーマット要件:
|
アクセス許可を与える WebDAV サービスのアドレスを追加したい場合、このオプションを使用します。フォーマット要件:
|
||||||
- 各アドレスは完全なエンドポイントでなければなりません。
|
- 各アドレスは完全なエンドポイントでなければなりません。
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { ApiPath } from "@/app/constant";
|
import { ApiPath } from "@/app/constant";
|
||||||
import { NextRequest, NextResponse } from "next/server";
|
import { NextRequest } from "next/server";
|
||||||
import { handle as openaiHandler } from "../../openai";
|
import { handle as openaiHandler } from "../../openai";
|
||||||
import { handle as azureHandler } from "../../azure";
|
import { handle as azureHandler } from "../../azure";
|
||||||
import { handle as googleHandler } from "../../google";
|
import { handle as googleHandler } from "../../google";
|
||||||
|
@ -9,6 +9,8 @@ import { handle as bytedanceHandler } from "../../bytedance";
|
||||||
import { handle as alibabaHandler } from "../../alibaba";
|
import { handle as alibabaHandler } from "../../alibaba";
|
||||||
import { handle as moonshotHandler } from "../../moonshot";
|
import { handle as moonshotHandler } from "../../moonshot";
|
||||||
import { handle as stabilityHandler } from "../../stability";
|
import { handle as stabilityHandler } from "../../stability";
|
||||||
|
import { handle as iflytekHandler } from "../../iflytek";
|
||||||
|
import { handle as proxyHandler } from "../../proxy";
|
||||||
|
|
||||||
async function handle(
|
async function handle(
|
||||||
req: NextRequest,
|
req: NextRequest,
|
||||||
|
@ -34,8 +36,12 @@ async function handle(
|
||||||
return moonshotHandler(req, { params });
|
return moonshotHandler(req, { params });
|
||||||
case ApiPath.Stability:
|
case ApiPath.Stability:
|
||||||
return stabilityHandler(req, { params });
|
return stabilityHandler(req, { params });
|
||||||
default:
|
case ApiPath.Iflytek:
|
||||||
|
return iflytekHandler(req, { params });
|
||||||
|
case ApiPath.OpenAI:
|
||||||
return openaiHandler(req, { params });
|
return openaiHandler(req, { params });
|
||||||
|
default:
|
||||||
|
return proxyHandler(req, { params });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { getServerSideConfig } from "@/app/config/server";
|
import { getServerSideConfig } from "@/app/config/server";
|
||||||
import {
|
import {
|
||||||
Alibaba,
|
|
||||||
ALIBABA_BASE_URL,
|
ALIBABA_BASE_URL,
|
||||||
ApiPath,
|
ApiPath,
|
||||||
ModelProvider,
|
ModelProvider,
|
||||||
|
@ -10,7 +9,6 @@ import { prettyObject } from "@/app/utils/format";
|
||||||
import { NextRequest, NextResponse } from "next/server";
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
import { auth } from "@/app/api/auth";
|
import { auth } from "@/app/api/auth";
|
||||||
import { isModelAvailableInServer } from "@/app/utils/model";
|
import { isModelAvailableInServer } from "@/app/utils/model";
|
||||||
import type { RequestPayload } from "@/app/client/platforms/openai";
|
|
||||||
|
|
||||||
const serverConfig = getServerSideConfig();
|
const serverConfig = getServerSideConfig();
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,6 @@ import {
|
||||||
ANTHROPIC_BASE_URL,
|
ANTHROPIC_BASE_URL,
|
||||||
Anthropic,
|
Anthropic,
|
||||||
ApiPath,
|
ApiPath,
|
||||||
DEFAULT_MODELS,
|
|
||||||
ServiceProvider,
|
ServiceProvider,
|
||||||
ModelProvider,
|
ModelProvider,
|
||||||
} from "@/app/constant";
|
} from "@/app/constant";
|
||||||
|
@ -98,6 +97,7 @@ async function request(req: NextRequest) {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
"Cache-Control": "no-store",
|
"Cache-Control": "no-store",
|
||||||
|
"anthropic-dangerous-direct-browser-access": "true",
|
||||||
[authHeaderName]: authValue,
|
[authHeaderName]: authValue,
|
||||||
"anthropic-version":
|
"anthropic-version":
|
||||||
req.headers.get("anthropic-version") ||
|
req.headers.get("anthropic-version") ||
|
||||||
|
|
|
@ -88,6 +88,10 @@ export function auth(req: NextRequest, modelProvider: ModelProvider) {
|
||||||
case ModelProvider.Moonshot:
|
case ModelProvider.Moonshot:
|
||||||
systemApiKey = serverConfig.moonshotApiKey;
|
systemApiKey = serverConfig.moonshotApiKey;
|
||||||
break;
|
break;
|
||||||
|
case ModelProvider.Iflytek:
|
||||||
|
systemApiKey =
|
||||||
|
serverConfig.iflytekApiKey + ":" + serverConfig.iflytekApiSecret;
|
||||||
|
break;
|
||||||
case ModelProvider.GPT:
|
case ModelProvider.GPT:
|
||||||
default:
|
default:
|
||||||
if (req.nextUrl.pathname.includes("azure/deployments")) {
|
if (req.nextUrl.pathname.includes("azure/deployments")) {
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { getServerSideConfig } from "@/app/config/server";
|
|
||||||
import { ModelProvider } from "@/app/constant";
|
import { ModelProvider } from "@/app/constant";
|
||||||
import { prettyObject } from "@/app/utils/format";
|
import { prettyObject } from "@/app/utils/format";
|
||||||
import { NextRequest, NextResponse } from "next/server";
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
|
|
|
@ -3,7 +3,6 @@ import {
|
||||||
BAIDU_BASE_URL,
|
BAIDU_BASE_URL,
|
||||||
ApiPath,
|
ApiPath,
|
||||||
ModelProvider,
|
ModelProvider,
|
||||||
BAIDU_OATUH_URL,
|
|
||||||
ServiceProvider,
|
ServiceProvider,
|
||||||
} from "@/app/constant";
|
} from "@/app/constant";
|
||||||
import { prettyObject } from "@/app/utils/format";
|
import { prettyObject } from "@/app/utils/format";
|
||||||
|
|
|
@ -1,11 +1,6 @@
|
||||||
import { NextRequest, NextResponse } from "next/server";
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
import { getServerSideConfig } from "../config/server";
|
import { getServerSideConfig } from "../config/server";
|
||||||
import {
|
import { OPENAI_BASE_URL, ServiceProvider } from "../constant";
|
||||||
DEFAULT_MODELS,
|
|
||||||
OPENAI_BASE_URL,
|
|
||||||
GEMINI_BASE_URL,
|
|
||||||
ServiceProvider,
|
|
||||||
} from "../constant";
|
|
||||||
import { isModelAvailableInServer } from "../utils/model";
|
import { isModelAvailableInServer } from "../utils/model";
|
||||||
import { cloudflareAIGatewayUrl } from "../utils/cloudflare";
|
import { cloudflareAIGatewayUrl } from "../utils/cloudflare";
|
||||||
|
|
||||||
|
@ -32,10 +27,7 @@ export async function requestOpenai(req: NextRequest) {
|
||||||
authHeaderName = "Authorization";
|
authHeaderName = "Authorization";
|
||||||
}
|
}
|
||||||
|
|
||||||
let path = `${req.nextUrl.pathname}${req.nextUrl.search}`.replaceAll(
|
let path = `${req.nextUrl.pathname}`.replaceAll("/api/openai/", "");
|
||||||
"/api/openai/",
|
|
||||||
"",
|
|
||||||
);
|
|
||||||
|
|
||||||
let baseUrl =
|
let baseUrl =
|
||||||
(isAzure ? serverConfig.azureUrl : serverConfig.baseUrl) || OPENAI_BASE_URL;
|
(isAzure ? serverConfig.azureUrl : serverConfig.baseUrl) || OPENAI_BASE_URL;
|
||||||
|
|
|
@ -1,12 +1,7 @@
|
||||||
import { NextRequest, NextResponse } from "next/server";
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
import { auth } from "./auth";
|
import { auth } from "./auth";
|
||||||
import { getServerSideConfig } from "@/app/config/server";
|
import { getServerSideConfig } from "@/app/config/server";
|
||||||
import {
|
import { ApiPath, GEMINI_BASE_URL, ModelProvider } from "@/app/constant";
|
||||||
ApiPath,
|
|
||||||
GEMINI_BASE_URL,
|
|
||||||
Google,
|
|
||||||
ModelProvider,
|
|
||||||
} from "@/app/constant";
|
|
||||||
import { prettyObject } from "@/app/utils/format";
|
import { prettyObject } from "@/app/utils/format";
|
||||||
|
|
||||||
const serverConfig = getServerSideConfig();
|
const serverConfig = getServerSideConfig();
|
||||||
|
@ -28,7 +23,8 @@ export async function handle(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const bearToken = req.headers.get("Authorization") ?? "";
|
const bearToken =
|
||||||
|
req.headers.get("x-goog-api-key") || req.headers.get("Authorization") || "";
|
||||||
const token = bearToken.trim().replaceAll("Bearer ", "").trim();
|
const token = bearToken.trim().replaceAll("Bearer ", "").trim();
|
||||||
|
|
||||||
const apiKey = token ? token : serverConfig.googleApiKey;
|
const apiKey = token ? token : serverConfig.googleApiKey;
|
||||||
|
@ -96,8 +92,8 @@ async function request(req: NextRequest, apiKey: string) {
|
||||||
},
|
},
|
||||||
10 * 60 * 1000,
|
10 * 60 * 1000,
|
||||||
);
|
);
|
||||||
const fetchUrl = `${baseUrl}${path}?key=${apiKey}${
|
const fetchUrl = `${baseUrl}${path}${
|
||||||
req?.nextUrl?.searchParams?.get("alt") === "sse" ? "&alt=sse" : ""
|
req?.nextUrl?.searchParams?.get("alt") === "sse" ? "?alt=sse" : ""
|
||||||
}`;
|
}`;
|
||||||
|
|
||||||
console.log("[Fetch Url] ", fetchUrl);
|
console.log("[Fetch Url] ", fetchUrl);
|
||||||
|
@ -105,6 +101,9 @@ async function request(req: NextRequest, apiKey: string) {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
"Cache-Control": "no-store",
|
"Cache-Control": "no-store",
|
||||||
|
"x-goog-api-key":
|
||||||
|
req.headers.get("x-goog-api-key") ||
|
||||||
|
(req.headers.get("Authorization") ?? "").replace("Bearer ", ""),
|
||||||
},
|
},
|
||||||
method: req.method,
|
method: req.method,
|
||||||
body: req.body,
|
body: req.body,
|
||||||
|
|
|
@ -0,0 +1,129 @@
|
||||||
|
import { getServerSideConfig } from "@/app/config/server";
|
||||||
|
import {
|
||||||
|
IFLYTEK_BASE_URL,
|
||||||
|
ApiPath,
|
||||||
|
ModelProvider,
|
||||||
|
ServiceProvider,
|
||||||
|
} from "@/app/constant";
|
||||||
|
import { prettyObject } from "@/app/utils/format";
|
||||||
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
|
import { auth } from "@/app/api/auth";
|
||||||
|
import { isModelAvailableInServer } from "@/app/utils/model";
|
||||||
|
// iflytek
|
||||||
|
|
||||||
|
const serverConfig = getServerSideConfig();
|
||||||
|
|
||||||
|
export async function handle(
|
||||||
|
req: NextRequest,
|
||||||
|
{ params }: { params: { path: string[] } },
|
||||||
|
) {
|
||||||
|
console.log("[Iflytek Route] params ", params);
|
||||||
|
|
||||||
|
if (req.method === "OPTIONS") {
|
||||||
|
return NextResponse.json({ body: "OK" }, { status: 200 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const authResult = auth(req, ModelProvider.Iflytek);
|
||||||
|
if (authResult.error) {
|
||||||
|
return NextResponse.json(authResult, {
|
||||||
|
status: 401,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await request(req);
|
||||||
|
return response;
|
||||||
|
} catch (e) {
|
||||||
|
console.error("[Iflytek] ", e);
|
||||||
|
return NextResponse.json(prettyObject(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function request(req: NextRequest) {
|
||||||
|
const controller = new AbortController();
|
||||||
|
|
||||||
|
// iflytek use base url or just remove the path
|
||||||
|
let path = `${req.nextUrl.pathname}`.replaceAll(ApiPath.Iflytek, "");
|
||||||
|
|
||||||
|
let baseUrl = serverConfig.iflytekUrl || IFLYTEK_BASE_URL;
|
||||||
|
|
||||||
|
if (!baseUrl.startsWith("http")) {
|
||||||
|
baseUrl = `https://${baseUrl}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (baseUrl.endsWith("/")) {
|
||||||
|
baseUrl = baseUrl.slice(0, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("[Proxy] ", path);
|
||||||
|
console.log("[Base Url]", baseUrl);
|
||||||
|
|
||||||
|
const timeoutId = setTimeout(
|
||||||
|
() => {
|
||||||
|
controller.abort();
|
||||||
|
},
|
||||||
|
10 * 60 * 1000,
|
||||||
|
);
|
||||||
|
|
||||||
|
const fetchUrl = `${baseUrl}${path}`;
|
||||||
|
const fetchOptions: RequestInit = {
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Authorization: req.headers.get("Authorization") ?? "",
|
||||||
|
},
|
||||||
|
method: req.method,
|
||||||
|
body: req.body,
|
||||||
|
redirect: "manual",
|
||||||
|
// @ts-ignore
|
||||||
|
duplex: "half",
|
||||||
|
signal: controller.signal,
|
||||||
|
};
|
||||||
|
|
||||||
|
// try to refuse some request to some models
|
||||||
|
if (serverConfig.customModels && req.body) {
|
||||||
|
try {
|
||||||
|
const clonedBody = await req.text();
|
||||||
|
fetchOptions.body = clonedBody;
|
||||||
|
|
||||||
|
const jsonBody = JSON.parse(clonedBody) as { model?: string };
|
||||||
|
|
||||||
|
// not undefined and is false
|
||||||
|
if (
|
||||||
|
isModelAvailableInServer(
|
||||||
|
serverConfig.customModels,
|
||||||
|
jsonBody?.model as string,
|
||||||
|
ServiceProvider.Iflytek as string,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{
|
||||||
|
error: true,
|
||||||
|
message: `you are not allowed to use ${jsonBody?.model} model`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
status: 403,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`[Iflytek] filter`, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const res = await fetch(fetchUrl, fetchOptions);
|
||||||
|
|
||||||
|
// to prevent browser prompt for credentials
|
||||||
|
const newHeaders = new Headers(res.headers);
|
||||||
|
newHeaders.delete("www-authenticate");
|
||||||
|
// to disable nginx buffering
|
||||||
|
newHeaders.set("X-Accel-Buffering", "no");
|
||||||
|
|
||||||
|
return new Response(res.body, {
|
||||||
|
status: res.status,
|
||||||
|
statusText: res.statusText,
|
||||||
|
headers: newHeaders,
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
import { getServerSideConfig } from "@/app/config/server";
|
import { getServerSideConfig } from "@/app/config/server";
|
||||||
import {
|
import {
|
||||||
Moonshot,
|
|
||||||
MOONSHOT_BASE_URL,
|
MOONSHOT_BASE_URL,
|
||||||
ApiPath,
|
ApiPath,
|
||||||
ModelProvider,
|
ModelProvider,
|
||||||
|
@ -10,7 +9,6 @@ import { prettyObject } from "@/app/utils/format";
|
||||||
import { NextRequest, NextResponse } from "next/server";
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
import { auth } from "@/app/api/auth";
|
import { auth } from "@/app/api/auth";
|
||||||
import { isModelAvailableInServer } from "@/app/utils/model";
|
import { isModelAvailableInServer } from "@/app/utils/model";
|
||||||
import type { RequestPayload } from "@/app/client/platforms/openai";
|
|
||||||
|
|
||||||
const serverConfig = getServerSideConfig();
|
const serverConfig = getServerSideConfig();
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,9 @@ function getModels(remoteModelRes: OpenAIListModelResponse) {
|
||||||
|
|
||||||
if (config.disableGPT4) {
|
if (config.disableGPT4) {
|
||||||
remoteModelRes.data = remoteModelRes.data.filter(
|
remoteModelRes.data = remoteModelRes.data.filter(
|
||||||
(m) => !m.id.startsWith("gpt-4"),
|
(m) =>
|
||||||
|
!(m.id.startsWith("gpt-4") || m.id.startsWith("chatgpt-4o")) ||
|
||||||
|
m.id.startsWith("gpt-4o-mini"),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
|
|
||||||
|
export async function handle(
|
||||||
|
req: NextRequest,
|
||||||
|
{ params }: { params: { path: string[] } },
|
||||||
|
) {
|
||||||
|
console.log("[Proxy Route] params ", params);
|
||||||
|
|
||||||
|
if (req.method === "OPTIONS") {
|
||||||
|
return NextResponse.json({ body: "OK" }, { status: 200 });
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove path params from searchParams
|
||||||
|
req.nextUrl.searchParams.delete("path");
|
||||||
|
req.nextUrl.searchParams.delete("provider");
|
||||||
|
|
||||||
|
const subpath = params.path.join("/");
|
||||||
|
const fetchUrl = `${req.headers.get(
|
||||||
|
"x-base-url",
|
||||||
|
)}/${subpath}?${req.nextUrl.searchParams.toString()}`;
|
||||||
|
const skipHeaders = ["connection", "host", "origin", "referer", "cookie"];
|
||||||
|
const headers = new Headers(
|
||||||
|
Array.from(req.headers.entries()).filter((item) => {
|
||||||
|
if (
|
||||||
|
item[0].indexOf("x-") > -1 ||
|
||||||
|
item[0].indexOf("sec-") > -1 ||
|
||||||
|
skipHeaders.includes(item[0])
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
const controller = new AbortController();
|
||||||
|
const fetchOptions: RequestInit = {
|
||||||
|
headers,
|
||||||
|
method: req.method,
|
||||||
|
body: req.body,
|
||||||
|
// to fix #2485: https://stackoverflow.com/questions/55920957/cloudflare-worker-typeerror-one-time-use-body
|
||||||
|
redirect: "manual",
|
||||||
|
// @ts-ignore
|
||||||
|
duplex: "half",
|
||||||
|
signal: controller.signal,
|
||||||
|
};
|
||||||
|
|
||||||
|
const timeoutId = setTimeout(
|
||||||
|
() => {
|
||||||
|
controller.abort();
|
||||||
|
},
|
||||||
|
10 * 60 * 1000,
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch(fetchUrl, fetchOptions);
|
||||||
|
// to prevent browser prompt for credentials
|
||||||
|
const newHeaders = new Headers(res.headers);
|
||||||
|
newHeaders.delete("www-authenticate");
|
||||||
|
// to disable nginx buffering
|
||||||
|
newHeaders.set("X-Accel-Buffering", "no");
|
||||||
|
|
||||||
|
// The latest version of the OpenAI API forced the content-encoding to be "br" in json response
|
||||||
|
// So if the streaming is disabled, we need to remove the content-encoding header
|
||||||
|
// Because Vercel uses gzip to compress the response, if we don't remove the content-encoding header
|
||||||
|
// The browser will try to decode the response with brotli and fail
|
||||||
|
newHeaders.delete("content-encoding");
|
||||||
|
|
||||||
|
return new Response(res.body, {
|
||||||
|
status: res.status,
|
||||||
|
statusText: res.statusText,
|
||||||
|
headers: newHeaders,
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,15 +1,8 @@
|
||||||
import { getServerSideConfig } from "@/app/config/server";
|
import { getServerSideConfig } from "@/app/config/server";
|
||||||
import {
|
import { TENCENT_BASE_URL, ModelProvider } from "@/app/constant";
|
||||||
TENCENT_BASE_URL,
|
|
||||||
ApiPath,
|
|
||||||
ModelProvider,
|
|
||||||
ServiceProvider,
|
|
||||||
Tencent,
|
|
||||||
} from "@/app/constant";
|
|
||||||
import { prettyObject } from "@/app/utils/format";
|
import { prettyObject } from "@/app/utils/format";
|
||||||
import { NextRequest, NextResponse } from "next/server";
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
import { auth } from "@/app/api/auth";
|
import { auth } from "@/app/api/auth";
|
||||||
import { isModelAvailableInServer } from "@/app/utils/model";
|
|
||||||
import { getHeader } from "@/app/utils/tencent";
|
import { getHeader } from "@/app/utils/tencent";
|
||||||
|
|
||||||
const serverConfig = getServerSideConfig();
|
const serverConfig = getServerSideConfig();
|
||||||
|
|
|
@ -6,7 +6,7 @@ const config = getServerSideConfig();
|
||||||
|
|
||||||
const mergedAllowedWebDavEndpoints = [
|
const mergedAllowedWebDavEndpoints = [
|
||||||
...internalAllowedWebDavEndpoints,
|
...internalAllowedWebDavEndpoints,
|
||||||
...config.allowedWebDevEndpoints,
|
...config.allowedWebDavEndpoints,
|
||||||
].filter((domain) => Boolean(domain.trim()));
|
].filter((domain) => Boolean(domain.trim()));
|
||||||
|
|
||||||
const normalizeUrl = (url: string) => {
|
const normalizeUrl = (url: string) => {
|
||||||
|
@ -29,6 +29,7 @@ async function handle(
|
||||||
|
|
||||||
const requestUrl = new URL(req.url);
|
const requestUrl = new URL(req.url);
|
||||||
let endpoint = requestUrl.searchParams.get("endpoint");
|
let endpoint = requestUrl.searchParams.get("endpoint");
|
||||||
|
let proxy_method = requestUrl.searchParams.get("proxy_method") || req.method;
|
||||||
|
|
||||||
// Validate the endpoint to prevent potential SSRF attacks
|
// Validate the endpoint to prevent potential SSRF attacks
|
||||||
if (
|
if (
|
||||||
|
@ -65,7 +66,11 @@ async function handle(
|
||||||
const targetPath = `${endpoint}${endpointPath}`;
|
const targetPath = `${endpoint}${endpointPath}`;
|
||||||
|
|
||||||
// only allow MKCOL, GET, PUT
|
// only allow MKCOL, GET, PUT
|
||||||
if (req.method !== "MKCOL" && req.method !== "GET" && req.method !== "PUT") {
|
if (
|
||||||
|
proxy_method !== "MKCOL" &&
|
||||||
|
proxy_method !== "GET" &&
|
||||||
|
proxy_method !== "PUT"
|
||||||
|
) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{
|
{
|
||||||
error: true,
|
error: true,
|
||||||
|
@ -78,7 +83,7 @@ async function handle(
|
||||||
}
|
}
|
||||||
|
|
||||||
// for MKCOL request, only allow request ${folder}
|
// for MKCOL request, only allow request ${folder}
|
||||||
if (req.method === "MKCOL" && !targetPath.endsWith(folder)) {
|
if (proxy_method === "MKCOL" && !targetPath.endsWith(folder)) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{
|
{
|
||||||
error: true,
|
error: true,
|
||||||
|
@ -91,7 +96,7 @@ async function handle(
|
||||||
}
|
}
|
||||||
|
|
||||||
// for GET request, only allow request ending with fileName
|
// for GET request, only allow request ending with fileName
|
||||||
if (req.method === "GET" && !targetPath.endsWith(fileName)) {
|
if (proxy_method === "GET" && !targetPath.endsWith(fileName)) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{
|
{
|
||||||
error: true,
|
error: true,
|
||||||
|
@ -104,7 +109,7 @@ async function handle(
|
||||||
}
|
}
|
||||||
|
|
||||||
// for PUT request, only allow request ending with fileName
|
// for PUT request, only allow request ending with fileName
|
||||||
if (req.method === "PUT" && !targetPath.endsWith(fileName)) {
|
if (proxy_method === "PUT" && !targetPath.endsWith(fileName)) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{
|
{
|
||||||
error: true,
|
error: true,
|
||||||
|
@ -118,7 +123,7 @@ async function handle(
|
||||||
|
|
||||||
const targetUrl = targetPath;
|
const targetUrl = targetPath;
|
||||||
|
|
||||||
const method = req.method;
|
const method = proxy_method || req.method;
|
||||||
const shouldNotHaveBody = ["get", "head"].includes(
|
const shouldNotHaveBody = ["get", "head"].includes(
|
||||||
method?.toLowerCase() ?? "",
|
method?.toLowerCase() ?? "",
|
||||||
);
|
);
|
||||||
|
@ -143,7 +148,7 @@ async function handle(
|
||||||
"[Any Proxy]",
|
"[Any Proxy]",
|
||||||
targetUrl,
|
targetUrl,
|
||||||
{
|
{
|
||||||
method: req.method,
|
method: method,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
status: fetchResult?.status,
|
status: fetchResult?.status,
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
import { getClientConfig } from "../config/client";
|
import { getClientConfig } from "../config/client";
|
||||||
import {
|
import {
|
||||||
ACCESS_CODE_PREFIX,
|
ACCESS_CODE_PREFIX,
|
||||||
Azure,
|
|
||||||
ModelProvider,
|
ModelProvider,
|
||||||
ServiceProvider,
|
ServiceProvider,
|
||||||
} from "../constant";
|
} from "../constant";
|
||||||
import { ChatMessage, ModelType, useAccessStore, useChatStore } from "../store";
|
import {
|
||||||
|
ChatMessageTool,
|
||||||
|
ChatMessage,
|
||||||
|
ModelType,
|
||||||
|
useAccessStore,
|
||||||
|
useChatStore,
|
||||||
|
} from "../store";
|
||||||
import { ChatGPTApi, DalleRequestPayload } from "./platforms/openai";
|
import { ChatGPTApi, DalleRequestPayload } from "./platforms/openai";
|
||||||
import { GeminiProApi } from "./platforms/google";
|
import { GeminiProApi } from "./platforms/google";
|
||||||
import { ClaudeApi } from "./platforms/anthropic";
|
import { ClaudeApi } from "./platforms/anthropic";
|
||||||
|
@ -14,11 +19,13 @@ import { DoubaoApi } from "./platforms/bytedance";
|
||||||
import { QwenApi } from "./platforms/alibaba";
|
import { QwenApi } from "./platforms/alibaba";
|
||||||
import { HunyuanApi } from "./platforms/tencent";
|
import { HunyuanApi } from "./platforms/tencent";
|
||||||
import { MoonshotApi } from "./platforms/moonshot";
|
import { MoonshotApi } from "./platforms/moonshot";
|
||||||
|
import { SparkApi } from "./platforms/iflytek";
|
||||||
|
|
||||||
export const ROLES = ["system", "user", "assistant"] as const;
|
export const ROLES = ["system", "user", "assistant"] as const;
|
||||||
export type MessageRole = (typeof ROLES)[number];
|
export type MessageRole = (typeof ROLES)[number];
|
||||||
|
|
||||||
export const Models = ["gpt-3.5-turbo", "gpt-4"] as const;
|
export const Models = ["gpt-3.5-turbo", "gpt-4"] as const;
|
||||||
|
export const TTSModels = ["tts-1", "tts-1-hd"] as const;
|
||||||
export type ChatModel = ModelType;
|
export type ChatModel = ModelType;
|
||||||
|
|
||||||
export interface MultimodalContent {
|
export interface MultimodalContent {
|
||||||
|
@ -43,6 +50,17 @@ export interface LLMConfig {
|
||||||
presence_penalty?: number;
|
presence_penalty?: number;
|
||||||
frequency_penalty?: number;
|
frequency_penalty?: number;
|
||||||
size?: DalleRequestPayload["size"];
|
size?: DalleRequestPayload["size"];
|
||||||
|
quality?: DalleRequestPayload["quality"];
|
||||||
|
style?: DalleRequestPayload["style"];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SpeechOptions {
|
||||||
|
model: string;
|
||||||
|
input: string;
|
||||||
|
voice: string;
|
||||||
|
response_format?: string;
|
||||||
|
speed?: number;
|
||||||
|
onController?: (controller: AbortController) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ChatOptions {
|
export interface ChatOptions {
|
||||||
|
@ -53,6 +71,8 @@ export interface ChatOptions {
|
||||||
onFinish: (message: string) => void;
|
onFinish: (message: string) => void;
|
||||||
onError?: (err: Error) => void;
|
onError?: (err: Error) => void;
|
||||||
onController?: (controller: AbortController) => void;
|
onController?: (controller: AbortController) => void;
|
||||||
|
onBeforeTool?: (tool: ChatMessageTool) => void;
|
||||||
|
onAfterTool?: (tool: ChatMessageTool) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LLMUsage {
|
export interface LLMUsage {
|
||||||
|
@ -77,6 +97,7 @@ export interface LLMModelProvider {
|
||||||
|
|
||||||
export abstract class LLMApi {
|
export abstract class LLMApi {
|
||||||
abstract chat(options: ChatOptions): Promise<void>;
|
abstract chat(options: ChatOptions): Promise<void>;
|
||||||
|
abstract speech(options: SpeechOptions): Promise<ArrayBuffer>;
|
||||||
abstract usage(): Promise<LLMUsage>;
|
abstract usage(): Promise<LLMUsage>;
|
||||||
abstract models(): Promise<LLMModel[]>;
|
abstract models(): Promise<LLMModel[]>;
|
||||||
}
|
}
|
||||||
|
@ -128,6 +149,9 @@ export class ClientApi {
|
||||||
case ModelProvider.Moonshot:
|
case ModelProvider.Moonshot:
|
||||||
this.llm = new MoonshotApi();
|
this.llm = new MoonshotApi();
|
||||||
break;
|
break;
|
||||||
|
case ModelProvider.Iflytek:
|
||||||
|
this.llm = new SparkApi();
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
this.llm = new ChatGPTApi();
|
this.llm = new ChatGPTApi();
|
||||||
}
|
}
|
||||||
|
@ -192,25 +216,29 @@ export function validString(x: string): boolean {
|
||||||
return x?.length > 0;
|
return x?.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getHeaders() {
|
export function getHeaders(ignoreHeaders: boolean = false) {
|
||||||
const accessStore = useAccessStore.getState();
|
const accessStore = useAccessStore.getState();
|
||||||
const chatStore = useChatStore.getState();
|
const chatStore = useChatStore.getState();
|
||||||
const headers: Record<string, string> = {
|
let headers: Record<string, string> = {};
|
||||||
|
if (!ignoreHeaders) {
|
||||||
|
headers = {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
Accept: "application/json",
|
Accept: "application/json",
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const clientConfig = getClientConfig();
|
const clientConfig = getClientConfig();
|
||||||
|
|
||||||
function getConfig() {
|
function getConfig() {
|
||||||
const modelConfig = chatStore.currentSession().mask.modelConfig;
|
const modelConfig = chatStore.currentSession().mask.modelConfig;
|
||||||
const isGoogle = modelConfig.providerName == ServiceProvider.Google;
|
const isGoogle = modelConfig.providerName === ServiceProvider.Google;
|
||||||
const isAzure = modelConfig.providerName === ServiceProvider.Azure;
|
const isAzure = modelConfig.providerName === ServiceProvider.Azure;
|
||||||
const isAnthropic = modelConfig.providerName === ServiceProvider.Anthropic;
|
const isAnthropic = modelConfig.providerName === ServiceProvider.Anthropic;
|
||||||
const isBaidu = modelConfig.providerName == ServiceProvider.Baidu;
|
const isBaidu = modelConfig.providerName == ServiceProvider.Baidu;
|
||||||
const isByteDance = modelConfig.providerName === ServiceProvider.ByteDance;
|
const isByteDance = modelConfig.providerName === ServiceProvider.ByteDance;
|
||||||
const isAlibaba = modelConfig.providerName === ServiceProvider.Alibaba;
|
const isAlibaba = modelConfig.providerName === ServiceProvider.Alibaba;
|
||||||
const isMoonshot = modelConfig.providerName === ServiceProvider.Moonshot;
|
const isMoonshot = modelConfig.providerName === ServiceProvider.Moonshot;
|
||||||
|
const isIflytek = modelConfig.providerName === ServiceProvider.Iflytek;
|
||||||
const isEnabledAccessControl = accessStore.enabledAccessControl();
|
const isEnabledAccessControl = accessStore.enabledAccessControl();
|
||||||
const apiKey = isGoogle
|
const apiKey = isGoogle
|
||||||
? accessStore.googleApiKey
|
? accessStore.googleApiKey
|
||||||
|
@ -224,6 +252,10 @@ export function getHeaders() {
|
||||||
? accessStore.alibabaApiKey
|
? accessStore.alibabaApiKey
|
||||||
: isMoonshot
|
: isMoonshot
|
||||||
? accessStore.moonshotApiKey
|
? accessStore.moonshotApiKey
|
||||||
|
: isIflytek
|
||||||
|
? accessStore.iflytekApiKey && accessStore.iflytekApiSecret
|
||||||
|
? accessStore.iflytekApiKey + ":" + accessStore.iflytekApiSecret
|
||||||
|
: ""
|
||||||
: accessStore.openaiApiKey;
|
: accessStore.openaiApiKey;
|
||||||
return {
|
return {
|
||||||
isGoogle,
|
isGoogle,
|
||||||
|
@ -233,13 +265,20 @@ export function getHeaders() {
|
||||||
isByteDance,
|
isByteDance,
|
||||||
isAlibaba,
|
isAlibaba,
|
||||||
isMoonshot,
|
isMoonshot,
|
||||||
|
isIflytek,
|
||||||
apiKey,
|
apiKey,
|
||||||
isEnabledAccessControl,
|
isEnabledAccessControl,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAuthHeader(): string {
|
function getAuthHeader(): string {
|
||||||
return isAzure ? "api-key" : isAnthropic ? "x-api-key" : "Authorization";
|
return isAzure
|
||||||
|
? "api-key"
|
||||||
|
: isAnthropic
|
||||||
|
? "x-api-key"
|
||||||
|
: isGoogle
|
||||||
|
? "x-goog-api-key"
|
||||||
|
: "Authorization";
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
@ -250,14 +289,15 @@ export function getHeaders() {
|
||||||
apiKey,
|
apiKey,
|
||||||
isEnabledAccessControl,
|
isEnabledAccessControl,
|
||||||
} = getConfig();
|
} = getConfig();
|
||||||
// when using google api in app, not set auth header
|
|
||||||
if (isGoogle && clientConfig?.isApp) return headers;
|
|
||||||
// when using baidu api in app, not set auth header
|
// when using baidu api in app, not set auth header
|
||||||
if (isBaidu && clientConfig?.isApp) return headers;
|
if (isBaidu && clientConfig?.isApp) return headers;
|
||||||
|
|
||||||
const authHeader = getAuthHeader();
|
const authHeader = getAuthHeader();
|
||||||
|
|
||||||
const bearerToken = getBearerToken(apiKey, isAzure || isAnthropic);
|
const bearerToken = getBearerToken(
|
||||||
|
apiKey,
|
||||||
|
isAzure || isAnthropic || isGoogle,
|
||||||
|
);
|
||||||
|
|
||||||
if (bearerToken) {
|
if (bearerToken) {
|
||||||
headers[authHeader] = bearerToken;
|
headers[authHeader] = bearerToken;
|
||||||
|
@ -286,6 +326,8 @@ export function getClientApi(provider: ServiceProvider): ClientApi {
|
||||||
return new ClientApi(ModelProvider.Hunyuan);
|
return new ClientApi(ModelProvider.Hunyuan);
|
||||||
case ServiceProvider.Moonshot:
|
case ServiceProvider.Moonshot:
|
||||||
return new ClientApi(ModelProvider.Moonshot);
|
return new ClientApi(ModelProvider.Moonshot);
|
||||||
|
case ServiceProvider.Iflytek:
|
||||||
|
return new ClientApi(ModelProvider.Iflytek);
|
||||||
default:
|
default:
|
||||||
return new ClientApi(ModelProvider.GPT);
|
return new ClientApi(ModelProvider.GPT);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import {
|
||||||
getHeaders,
|
getHeaders,
|
||||||
LLMApi,
|
LLMApi,
|
||||||
LLMModel,
|
LLMModel,
|
||||||
|
SpeechOptions,
|
||||||
MultimodalContent,
|
MultimodalContent,
|
||||||
} from "../api";
|
} from "../api";
|
||||||
import Locale from "../../locales";
|
import Locale from "../../locales";
|
||||||
|
@ -83,6 +84,10 @@ export class QwenApi implements LLMApi {
|
||||||
return res?.output?.choices?.at(0)?.message?.content ?? "";
|
return res?.output?.choices?.at(0)?.message?.content ?? "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
speech(options: SpeechOptions): Promise<ArrayBuffer> {
|
||||||
|
throw new Error("Method not implemented.");
|
||||||
|
}
|
||||||
|
|
||||||
async chat(options: ChatOptions) {
|
async chat(options: ChatOptions) {
|
||||||
const messages = options.messages.map((v) => ({
|
const messages = options.messages.map((v) => ({
|
||||||
role: v.role,
|
role: v.role,
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
import { ACCESS_CODE_PREFIX, Anthropic, ApiPath } from "@/app/constant";
|
import { Anthropic, ApiPath } from "@/app/constant";
|
||||||
import { ChatOptions, getHeaders, LLMApi, MultimodalContent } from "../api";
|
import { ChatOptions, getHeaders, LLMApi, SpeechOptions } from "../api";
|
||||||
import { useAccessStore, useAppConfig, useChatStore } from "@/app/store";
|
import {
|
||||||
|
useAccessStore,
|
||||||
|
useAppConfig,
|
||||||
|
useChatStore,
|
||||||
|
usePluginStore,
|
||||||
|
ChatMessageTool,
|
||||||
|
} from "@/app/store";
|
||||||
import { getClientConfig } from "@/app/config/client";
|
import { getClientConfig } from "@/app/config/client";
|
||||||
import { DEFAULT_API_HOST } from "@/app/constant";
|
import { DEFAULT_API_HOST } from "@/app/constant";
|
||||||
import {
|
|
||||||
EventStreamContentType,
|
|
||||||
fetchEventSource,
|
|
||||||
} from "@fortaine/fetch-event-source";
|
|
||||||
|
|
||||||
import Locale from "../../locales";
|
|
||||||
import { prettyObject } from "@/app/utils/format";
|
|
||||||
import { getMessageTextContent, isVisionModel } from "@/app/utils";
|
import { getMessageTextContent, isVisionModel } from "@/app/utils";
|
||||||
import { preProcessImageContent } from "@/app/utils/chat";
|
import { preProcessImageContent, stream } from "@/app/utils/chat";
|
||||||
import { cloudflareAIGatewayUrl } from "@/app/utils/cloudflare";
|
import { cloudflareAIGatewayUrl } from "@/app/utils/cloudflare";
|
||||||
|
import { RequestPayload } from "./openai";
|
||||||
|
|
||||||
export type MultiBlockContent = {
|
export type MultiBlockContent = {
|
||||||
type: "image" | "text";
|
type: "image" | "text";
|
||||||
|
@ -73,6 +73,10 @@ const ClaudeMapper = {
|
||||||
const keys = ["claude-2, claude-instant-1"];
|
const keys = ["claude-2, claude-instant-1"];
|
||||||
|
|
||||||
export class ClaudeApi implements LLMApi {
|
export class ClaudeApi implements LLMApi {
|
||||||
|
speech(options: SpeechOptions): Promise<ArrayBuffer> {
|
||||||
|
throw new Error("Method not implemented.");
|
||||||
|
}
|
||||||
|
|
||||||
extractMessage(res: any) {
|
extractMessage(res: any) {
|
||||||
console.log("[Response] claude response: ", res);
|
console.log("[Response] claude response: ", res);
|
||||||
|
|
||||||
|
@ -191,6 +195,114 @@ export class ClaudeApi implements LLMApi {
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
options.onController?.(controller);
|
options.onController?.(controller);
|
||||||
|
|
||||||
|
if (shouldStream) {
|
||||||
|
let index = -1;
|
||||||
|
const [tools, funcs] = usePluginStore
|
||||||
|
.getState()
|
||||||
|
.getAsTools(
|
||||||
|
useChatStore.getState().currentSession().mask?.plugin || [],
|
||||||
|
);
|
||||||
|
return stream(
|
||||||
|
path,
|
||||||
|
requestBody,
|
||||||
|
{
|
||||||
|
...getHeaders(),
|
||||||
|
"anthropic-version": accessStore.anthropicApiVersion,
|
||||||
|
},
|
||||||
|
// @ts-ignore
|
||||||
|
tools.map((tool) => ({
|
||||||
|
name: tool?.function?.name,
|
||||||
|
description: tool?.function?.description,
|
||||||
|
input_schema: tool?.function?.parameters,
|
||||||
|
})),
|
||||||
|
funcs,
|
||||||
|
controller,
|
||||||
|
// parseSSE
|
||||||
|
(text: string, runTools: ChatMessageTool[]) => {
|
||||||
|
// console.log("parseSSE", text, runTools);
|
||||||
|
let chunkJson:
|
||||||
|
| undefined
|
||||||
|
| {
|
||||||
|
type: "content_block_delta" | "content_block_stop";
|
||||||
|
content_block?: {
|
||||||
|
type: "tool_use";
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
delta?: {
|
||||||
|
type: "text_delta" | "input_json_delta";
|
||||||
|
text?: string;
|
||||||
|
partial_json?: string;
|
||||||
|
};
|
||||||
|
index: number;
|
||||||
|
};
|
||||||
|
chunkJson = JSON.parse(text);
|
||||||
|
|
||||||
|
if (chunkJson?.content_block?.type == "tool_use") {
|
||||||
|
index += 1;
|
||||||
|
const id = chunkJson?.content_block.id;
|
||||||
|
const name = chunkJson?.content_block.name;
|
||||||
|
runTools.push({
|
||||||
|
id,
|
||||||
|
type: "function",
|
||||||
|
function: {
|
||||||
|
name,
|
||||||
|
arguments: "",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
chunkJson?.delta?.type == "input_json_delta" &&
|
||||||
|
chunkJson?.delta?.partial_json
|
||||||
|
) {
|
||||||
|
// @ts-ignore
|
||||||
|
runTools[index]["function"]["arguments"] +=
|
||||||
|
chunkJson?.delta?.partial_json;
|
||||||
|
}
|
||||||
|
return chunkJson?.delta?.text;
|
||||||
|
},
|
||||||
|
// processToolMessage, include tool_calls message and tool call results
|
||||||
|
(
|
||||||
|
requestPayload: RequestPayload,
|
||||||
|
toolCallMessage: any,
|
||||||
|
toolCallResult: any[],
|
||||||
|
) => {
|
||||||
|
// reset index value
|
||||||
|
index = -1;
|
||||||
|
// @ts-ignore
|
||||||
|
requestPayload?.messages?.splice(
|
||||||
|
// @ts-ignore
|
||||||
|
requestPayload?.messages?.length,
|
||||||
|
0,
|
||||||
|
{
|
||||||
|
role: "assistant",
|
||||||
|
content: toolCallMessage.tool_calls.map(
|
||||||
|
(tool: ChatMessageTool) => ({
|
||||||
|
type: "tool_use",
|
||||||
|
id: tool.id,
|
||||||
|
name: tool?.function?.name,
|
||||||
|
input: tool?.function?.arguments
|
||||||
|
? JSON.parse(tool?.function?.arguments)
|
||||||
|
: {},
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
// @ts-ignore
|
||||||
|
...toolCallResult.map((result) => ({
|
||||||
|
role: "user",
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: "tool_result",
|
||||||
|
tool_use_id: result.tool_call_id,
|
||||||
|
content: result.content,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
const payload = {
|
const payload = {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify(requestBody),
|
body: JSON.stringify(requestBody),
|
||||||
|
@ -203,100 +315,6 @@ export class ClaudeApi implements LLMApi {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (shouldStream) {
|
|
||||||
try {
|
|
||||||
const context = {
|
|
||||||
text: "",
|
|
||||||
finished: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
const finish = () => {
|
|
||||||
if (!context.finished) {
|
|
||||||
options.onFinish(context.text);
|
|
||||||
context.finished = true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
controller.signal.onabort = finish;
|
|
||||||
fetchEventSource(path, {
|
|
||||||
...payload,
|
|
||||||
async onopen(res) {
|
|
||||||
const contentType = res.headers.get("content-type");
|
|
||||||
console.log("response content type: ", contentType);
|
|
||||||
|
|
||||||
if (contentType?.startsWith("text/plain")) {
|
|
||||||
context.text = await res.clone().text();
|
|
||||||
return finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
!res.ok ||
|
|
||||||
!res.headers
|
|
||||||
.get("content-type")
|
|
||||||
?.startsWith(EventStreamContentType) ||
|
|
||||||
res.status !== 200
|
|
||||||
) {
|
|
||||||
const responseTexts = [context.text];
|
|
||||||
let extraInfo = await res.clone().text();
|
|
||||||
try {
|
|
||||||
const resJson = await res.clone().json();
|
|
||||||
extraInfo = prettyObject(resJson);
|
|
||||||
} catch {}
|
|
||||||
|
|
||||||
if (res.status === 401) {
|
|
||||||
responseTexts.push(Locale.Error.Unauthorized);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (extraInfo) {
|
|
||||||
responseTexts.push(extraInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.text = responseTexts.join("\n\n");
|
|
||||||
|
|
||||||
return finish();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onmessage(msg) {
|
|
||||||
let chunkJson:
|
|
||||||
| undefined
|
|
||||||
| {
|
|
||||||
type: "content_block_delta" | "content_block_stop";
|
|
||||||
delta?: {
|
|
||||||
type: "text_delta";
|
|
||||||
text: string;
|
|
||||||
};
|
|
||||||
index: number;
|
|
||||||
};
|
|
||||||
try {
|
|
||||||
chunkJson = JSON.parse(msg.data);
|
|
||||||
} catch (e) {
|
|
||||||
console.error("[Response] parse error", msg.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!chunkJson || chunkJson.type === "content_block_stop") {
|
|
||||||
return finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
const { delta } = chunkJson;
|
|
||||||
if (delta?.text) {
|
|
||||||
context.text += delta.text;
|
|
||||||
options.onUpdate?.(context.text, delta.text);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onclose() {
|
|
||||||
finish();
|
|
||||||
},
|
|
||||||
onerror(e) {
|
|
||||||
options.onError?.(e);
|
|
||||||
throw e;
|
|
||||||
},
|
|
||||||
openWhenHidden: true,
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
console.error("failed to chat", e);
|
|
||||||
options.onError?.(e as Error);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
try {
|
try {
|
||||||
controller.signal.onabort = () => options.onFinish("");
|
controller.signal.onabort = () => options.onFinish("");
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ import {
|
||||||
LLMApi,
|
LLMApi,
|
||||||
LLMModel,
|
LLMModel,
|
||||||
MultimodalContent,
|
MultimodalContent,
|
||||||
|
SpeechOptions,
|
||||||
} from "../api";
|
} from "../api";
|
||||||
import Locale from "../../locales";
|
import Locale from "../../locales";
|
||||||
import {
|
import {
|
||||||
|
@ -75,19 +76,31 @@ export class ErnieApi implements LLMApi {
|
||||||
return [baseUrl, path].join("/");
|
return [baseUrl, path].join("/");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
speech(options: SpeechOptions): Promise<ArrayBuffer> {
|
||||||
|
throw new Error("Method not implemented.");
|
||||||
|
}
|
||||||
|
|
||||||
async chat(options: ChatOptions) {
|
async chat(options: ChatOptions) {
|
||||||
const messages = options.messages.map((v) => ({
|
const messages = options.messages.map((v) => ({
|
||||||
role: v.role,
|
// "error_code": 336006, "error_msg": "the role of message with even index in the messages must be user or function",
|
||||||
|
role: v.role === "system" ? "user" : v.role,
|
||||||
content: getMessageTextContent(v),
|
content: getMessageTextContent(v),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// "error_code": 336006, "error_msg": "the length of messages must be an odd number",
|
// "error_code": 336006, "error_msg": "the length of messages must be an odd number",
|
||||||
if (messages.length % 2 === 0) {
|
if (messages.length % 2 === 0) {
|
||||||
|
if (messages.at(0)?.role === "user") {
|
||||||
|
messages.splice(1, 0, {
|
||||||
|
role: "assistant",
|
||||||
|
content: " ",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
messages.unshift({
|
messages.unshift({
|
||||||
role: "user",
|
role: "user",
|
||||||
content: " ",
|
content: " ",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const modelConfig = {
|
const modelConfig = {
|
||||||
...useAppConfig.getState().modelConfig,
|
...useAppConfig.getState().modelConfig,
|
||||||
|
|
|
@ -13,6 +13,7 @@ import {
|
||||||
LLMApi,
|
LLMApi,
|
||||||
LLMModel,
|
LLMModel,
|
||||||
MultimodalContent,
|
MultimodalContent,
|
||||||
|
SpeechOptions,
|
||||||
} from "../api";
|
} from "../api";
|
||||||
import Locale from "../../locales";
|
import Locale from "../../locales";
|
||||||
import {
|
import {
|
||||||
|
@ -77,6 +78,10 @@ export class DoubaoApi implements LLMApi {
|
||||||
return res.choices?.at(0)?.message?.content ?? "";
|
return res.choices?.at(0)?.message?.content ?? "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
speech(options: SpeechOptions): Promise<ArrayBuffer> {
|
||||||
|
throw new Error("Method not implemented.");
|
||||||
|
}
|
||||||
|
|
||||||
async chat(options: ChatOptions) {
|
async chat(options: ChatOptions) {
|
||||||
const messages = options.messages.map((v) => ({
|
const messages = options.messages.map((v) => ({
|
||||||
role: v.role,
|
role: v.role,
|
||||||
|
|
|
@ -1,5 +1,12 @@
|
||||||
import { ApiPath, Google, REQUEST_TIMEOUT_MS } from "@/app/constant";
|
import { ApiPath, Google, REQUEST_TIMEOUT_MS } from "@/app/constant";
|
||||||
import { ChatOptions, getHeaders, LLMApi, LLMModel, LLMUsage } from "../api";
|
import {
|
||||||
|
ChatOptions,
|
||||||
|
getHeaders,
|
||||||
|
LLMApi,
|
||||||
|
LLMModel,
|
||||||
|
LLMUsage,
|
||||||
|
SpeechOptions,
|
||||||
|
} from "../api";
|
||||||
import { useAccessStore, useAppConfig, useChatStore } from "@/app/store";
|
import { useAccessStore, useAppConfig, useChatStore } from "@/app/store";
|
||||||
import { getClientConfig } from "@/app/config/client";
|
import { getClientConfig } from "@/app/config/client";
|
||||||
import { DEFAULT_API_HOST } from "@/app/constant";
|
import { DEFAULT_API_HOST } from "@/app/constant";
|
||||||
|
@ -41,10 +48,6 @@ export class GeminiProApi implements LLMApi {
|
||||||
let chatPath = [baseUrl, path].join("/");
|
let chatPath = [baseUrl, path].join("/");
|
||||||
|
|
||||||
chatPath += chatPath.includes("?") ? "&alt=sse" : "?alt=sse";
|
chatPath += chatPath.includes("?") ? "&alt=sse" : "?alt=sse";
|
||||||
// if chatPath.startsWith('http') then add key in query string
|
|
||||||
if (chatPath.startsWith("http") && accessStore.googleApiKey) {
|
|
||||||
chatPath += `&key=${accessStore.googleApiKey}`;
|
|
||||||
}
|
|
||||||
return chatPath;
|
return chatPath;
|
||||||
}
|
}
|
||||||
extractMessage(res: any) {
|
extractMessage(res: any) {
|
||||||
|
@ -56,6 +59,10 @@ export class GeminiProApi implements LLMApi {
|
||||||
""
|
""
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
speech(options: SpeechOptions): Promise<ArrayBuffer> {
|
||||||
|
throw new Error("Method not implemented.");
|
||||||
|
}
|
||||||
|
|
||||||
async chat(options: ChatOptions): Promise<void> {
|
async chat(options: ChatOptions): Promise<void> {
|
||||||
const apiClient = this;
|
const apiClient = this;
|
||||||
let multimodal = false;
|
let multimodal = false;
|
||||||
|
|
|
@ -0,0 +1,250 @@
|
||||||
|
"use client";
|
||||||
|
import {
|
||||||
|
ApiPath,
|
||||||
|
DEFAULT_API_HOST,
|
||||||
|
Iflytek,
|
||||||
|
REQUEST_TIMEOUT_MS,
|
||||||
|
} from "@/app/constant";
|
||||||
|
import { useAccessStore, useAppConfig, useChatStore } from "@/app/store";
|
||||||
|
|
||||||
|
import {
|
||||||
|
ChatOptions,
|
||||||
|
getHeaders,
|
||||||
|
LLMApi,
|
||||||
|
LLMModel,
|
||||||
|
SpeechOptions,
|
||||||
|
} from "../api";
|
||||||
|
import Locale from "../../locales";
|
||||||
|
import {
|
||||||
|
EventStreamContentType,
|
||||||
|
fetchEventSource,
|
||||||
|
} from "@fortaine/fetch-event-source";
|
||||||
|
import { prettyObject } from "@/app/utils/format";
|
||||||
|
import { getClientConfig } from "@/app/config/client";
|
||||||
|
import { getMessageTextContent } from "@/app/utils";
|
||||||
|
|
||||||
|
import { RequestPayload } from "./openai";
|
||||||
|
|
||||||
|
export class SparkApi implements LLMApi {
|
||||||
|
private disableListModels = true;
|
||||||
|
|
||||||
|
path(path: string): string {
|
||||||
|
const accessStore = useAccessStore.getState();
|
||||||
|
|
||||||
|
let baseUrl = "";
|
||||||
|
|
||||||
|
if (accessStore.useCustomConfig) {
|
||||||
|
baseUrl = accessStore.iflytekUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (baseUrl.length === 0) {
|
||||||
|
const isApp = !!getClientConfig()?.isApp;
|
||||||
|
const apiPath = ApiPath.Iflytek;
|
||||||
|
baseUrl = isApp ? DEFAULT_API_HOST + "/proxy" + apiPath : apiPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (baseUrl.endsWith("/")) {
|
||||||
|
baseUrl = baseUrl.slice(0, baseUrl.length - 1);
|
||||||
|
}
|
||||||
|
if (!baseUrl.startsWith("http") && !baseUrl.startsWith(ApiPath.Iflytek)) {
|
||||||
|
baseUrl = "https://" + baseUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("[Proxy Endpoint] ", baseUrl, path);
|
||||||
|
|
||||||
|
return [baseUrl, path].join("/");
|
||||||
|
}
|
||||||
|
|
||||||
|
extractMessage(res: any) {
|
||||||
|
return res.choices?.at(0)?.message?.content ?? "";
|
||||||
|
}
|
||||||
|
|
||||||
|
speech(options: SpeechOptions): Promise<ArrayBuffer> {
|
||||||
|
throw new Error("Method not implemented.");
|
||||||
|
}
|
||||||
|
|
||||||
|
async chat(options: ChatOptions) {
|
||||||
|
const messages: ChatOptions["messages"] = [];
|
||||||
|
for (const v of options.messages) {
|
||||||
|
const content = getMessageTextContent(v);
|
||||||
|
messages.push({ role: v.role, content });
|
||||||
|
}
|
||||||
|
|
||||||
|
const modelConfig = {
|
||||||
|
...useAppConfig.getState().modelConfig,
|
||||||
|
...useChatStore.getState().currentSession().mask.modelConfig,
|
||||||
|
...{
|
||||||
|
model: options.config.model,
|
||||||
|
providerName: options.config.providerName,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const requestPayload: RequestPayload = {
|
||||||
|
messages,
|
||||||
|
stream: options.config.stream,
|
||||||
|
model: modelConfig.model,
|
||||||
|
temperature: modelConfig.temperature,
|
||||||
|
presence_penalty: modelConfig.presence_penalty,
|
||||||
|
frequency_penalty: modelConfig.frequency_penalty,
|
||||||
|
top_p: modelConfig.top_p,
|
||||||
|
// max_tokens: Math.max(modelConfig.max_tokens, 1024),
|
||||||
|
// Please do not ask me why not send max_tokens, no reason, this param is just shit, I dont want to explain anymore.
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log("[Request] Spark payload: ", requestPayload);
|
||||||
|
|
||||||
|
const shouldStream = !!options.config.stream;
|
||||||
|
const controller = new AbortController();
|
||||||
|
options.onController?.(controller);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const chatPath = this.path(Iflytek.ChatPath);
|
||||||
|
const chatPayload = {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify(requestPayload),
|
||||||
|
signal: controller.signal,
|
||||||
|
headers: getHeaders(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Make a fetch request
|
||||||
|
const requestTimeoutId = setTimeout(
|
||||||
|
() => controller.abort(),
|
||||||
|
REQUEST_TIMEOUT_MS,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (shouldStream) {
|
||||||
|
let responseText = "";
|
||||||
|
let remainText = "";
|
||||||
|
let finished = false;
|
||||||
|
|
||||||
|
// Animate response text to make it look smooth
|
||||||
|
function animateResponseText() {
|
||||||
|
if (finished || controller.signal.aborted) {
|
||||||
|
responseText += remainText;
|
||||||
|
console.log("[Response Animation] finished");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remainText.length > 0) {
|
||||||
|
const fetchCount = Math.max(1, Math.round(remainText.length / 60));
|
||||||
|
const fetchText = remainText.slice(0, fetchCount);
|
||||||
|
responseText += fetchText;
|
||||||
|
remainText = remainText.slice(fetchCount);
|
||||||
|
options.onUpdate?.(responseText, fetchText);
|
||||||
|
}
|
||||||
|
|
||||||
|
requestAnimationFrame(animateResponseText);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start animation
|
||||||
|
animateResponseText();
|
||||||
|
|
||||||
|
const finish = () => {
|
||||||
|
if (!finished) {
|
||||||
|
finished = true;
|
||||||
|
options.onFinish(responseText + remainText);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
controller.signal.onabort = finish;
|
||||||
|
|
||||||
|
fetchEventSource(chatPath, {
|
||||||
|
...chatPayload,
|
||||||
|
async onopen(res) {
|
||||||
|
clearTimeout(requestTimeoutId);
|
||||||
|
const contentType = res.headers.get("content-type");
|
||||||
|
console.log("[Spark] request response content type: ", contentType);
|
||||||
|
|
||||||
|
if (contentType?.startsWith("text/plain")) {
|
||||||
|
responseText = await res.clone().text();
|
||||||
|
return finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle different error scenarios
|
||||||
|
if (
|
||||||
|
!res.ok ||
|
||||||
|
!res.headers
|
||||||
|
.get("content-type")
|
||||||
|
?.startsWith(EventStreamContentType) ||
|
||||||
|
res.status !== 200
|
||||||
|
) {
|
||||||
|
let extraInfo = await res.clone().text();
|
||||||
|
try {
|
||||||
|
const resJson = await res.clone().json();
|
||||||
|
extraInfo = prettyObject(resJson);
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
if (res.status === 401) {
|
||||||
|
extraInfo = Locale.Error.Unauthorized;
|
||||||
|
}
|
||||||
|
|
||||||
|
options.onError?.(
|
||||||
|
new Error(
|
||||||
|
`Request failed with status ${res.status}: ${extraInfo}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return finish();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onmessage(msg) {
|
||||||
|
if (msg.data === "[DONE]" || finished) {
|
||||||
|
return finish();
|
||||||
|
}
|
||||||
|
const text = msg.data;
|
||||||
|
try {
|
||||||
|
const json = JSON.parse(text);
|
||||||
|
const choices = json.choices as Array<{
|
||||||
|
delta: { content: string };
|
||||||
|
}>;
|
||||||
|
const delta = choices[0]?.delta?.content;
|
||||||
|
|
||||||
|
if (delta) {
|
||||||
|
remainText += delta;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error("[Request] parse error", text);
|
||||||
|
options.onError?.(new Error(`Failed to parse response: ${text}`));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onclose() {
|
||||||
|
finish();
|
||||||
|
},
|
||||||
|
onerror(e) {
|
||||||
|
options.onError?.(e);
|
||||||
|
throw e;
|
||||||
|
},
|
||||||
|
openWhenHidden: true,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const res = await fetch(chatPath, chatPayload);
|
||||||
|
clearTimeout(requestTimeoutId);
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
const errorText = await res.text();
|
||||||
|
options.onError?.(
|
||||||
|
new Error(`Request failed with status ${res.status}: ${errorText}`),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const resJson = await res.json();
|
||||||
|
const message = this.extractMessage(resJson);
|
||||||
|
options.onFinish(message);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log("[Request] failed to make a chat request", e);
|
||||||
|
options.onError?.(e as Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async usage() {
|
||||||
|
return {
|
||||||
|
used: 0,
|
||||||
|
total: 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async models(): Promise<LLMModel[]> {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,34 +3,27 @@
|
||||||
import {
|
import {
|
||||||
ApiPath,
|
ApiPath,
|
||||||
DEFAULT_API_HOST,
|
DEFAULT_API_HOST,
|
||||||
DEFAULT_MODELS,
|
|
||||||
Moonshot,
|
Moonshot,
|
||||||
REQUEST_TIMEOUT_MS,
|
REQUEST_TIMEOUT_MS,
|
||||||
ServiceProvider,
|
|
||||||
} from "@/app/constant";
|
} from "@/app/constant";
|
||||||
import { useAccessStore, useAppConfig, useChatStore } from "@/app/store";
|
import {
|
||||||
import { collectModelsWithDefaultModel } from "@/app/utils/model";
|
useAccessStore,
|
||||||
import { preProcessImageContent } from "@/app/utils/chat";
|
useAppConfig,
|
||||||
import { cloudflareAIGatewayUrl } from "@/app/utils/cloudflare";
|
useChatStore,
|
||||||
|
ChatMessageTool,
|
||||||
|
usePluginStore,
|
||||||
|
} from "@/app/store";
|
||||||
|
import { stream } from "@/app/utils/chat";
|
||||||
import {
|
import {
|
||||||
ChatOptions,
|
ChatOptions,
|
||||||
getHeaders,
|
getHeaders,
|
||||||
LLMApi,
|
LLMApi,
|
||||||
LLMModel,
|
LLMModel,
|
||||||
LLMUsage,
|
SpeechOptions,
|
||||||
MultimodalContent,
|
|
||||||
} from "../api";
|
} from "../api";
|
||||||
import Locale from "../../locales";
|
|
||||||
import {
|
|
||||||
EventStreamContentType,
|
|
||||||
fetchEventSource,
|
|
||||||
} from "@fortaine/fetch-event-source";
|
|
||||||
import { prettyObject } from "@/app/utils/format";
|
|
||||||
import { getClientConfig } from "@/app/config/client";
|
import { getClientConfig } from "@/app/config/client";
|
||||||
import { getMessageTextContent } from "@/app/utils";
|
import { getMessageTextContent } from "@/app/utils";
|
||||||
|
import { RequestPayload } from "./openai";
|
||||||
import { OpenAIListModelResponse, RequestPayload } from "./openai";
|
|
||||||
|
|
||||||
export class MoonshotApi implements LLMApi {
|
export class MoonshotApi implements LLMApi {
|
||||||
private disableListModels = true;
|
private disableListModels = true;
|
||||||
|
@ -66,6 +59,10 @@ export class MoonshotApi implements LLMApi {
|
||||||
return res.choices?.at(0)?.message?.content ?? "";
|
return res.choices?.at(0)?.message?.content ?? "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
speech(options: SpeechOptions): Promise<ArrayBuffer> {
|
||||||
|
throw new Error("Method not implemented.");
|
||||||
|
}
|
||||||
|
|
||||||
async chat(options: ChatOptions) {
|
async chat(options: ChatOptions) {
|
||||||
const messages: ChatOptions["messages"] = [];
|
const messages: ChatOptions["messages"] = [];
|
||||||
for (const v of options.messages) {
|
for (const v of options.messages) {
|
||||||
|
@ -116,115 +113,66 @@ export class MoonshotApi implements LLMApi {
|
||||||
);
|
);
|
||||||
|
|
||||||
if (shouldStream) {
|
if (shouldStream) {
|
||||||
let responseText = "";
|
const [tools, funcs] = usePluginStore
|
||||||
let remainText = "";
|
.getState()
|
||||||
let finished = false;
|
.getAsTools(
|
||||||
|
useChatStore.getState().currentSession().mask?.plugin || [],
|
||||||
// animate response to make it looks smooth
|
|
||||||
function animateResponseText() {
|
|
||||||
if (finished || controller.signal.aborted) {
|
|
||||||
responseText += remainText;
|
|
||||||
console.log("[Response Animation] finished");
|
|
||||||
if (responseText?.length === 0) {
|
|
||||||
options.onError?.(new Error("empty response from server"));
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (remainText.length > 0) {
|
|
||||||
const fetchCount = Math.max(1, Math.round(remainText.length / 60));
|
|
||||||
const fetchText = remainText.slice(0, fetchCount);
|
|
||||||
responseText += fetchText;
|
|
||||||
remainText = remainText.slice(fetchCount);
|
|
||||||
options.onUpdate?.(responseText, fetchText);
|
|
||||||
}
|
|
||||||
|
|
||||||
requestAnimationFrame(animateResponseText);
|
|
||||||
}
|
|
||||||
|
|
||||||
// start animaion
|
|
||||||
animateResponseText();
|
|
||||||
|
|
||||||
const finish = () => {
|
|
||||||
if (!finished) {
|
|
||||||
finished = true;
|
|
||||||
options.onFinish(responseText + remainText);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
controller.signal.onabort = finish;
|
|
||||||
|
|
||||||
fetchEventSource(chatPath, {
|
|
||||||
...chatPayload,
|
|
||||||
async onopen(res) {
|
|
||||||
clearTimeout(requestTimeoutId);
|
|
||||||
const contentType = res.headers.get("content-type");
|
|
||||||
console.log(
|
|
||||||
"[OpenAI] request response content type: ",
|
|
||||||
contentType,
|
|
||||||
);
|
);
|
||||||
|
return stream(
|
||||||
if (contentType?.startsWith("text/plain")) {
|
chatPath,
|
||||||
responseText = await res.clone().text();
|
requestPayload,
|
||||||
return finish();
|
getHeaders(),
|
||||||
}
|
tools as any,
|
||||||
|
funcs,
|
||||||
if (
|
controller,
|
||||||
!res.ok ||
|
// parseSSE
|
||||||
!res.headers
|
(text: string, runTools: ChatMessageTool[]) => {
|
||||||
.get("content-type")
|
// console.log("parseSSE", text, runTools);
|
||||||
?.startsWith(EventStreamContentType) ||
|
|
||||||
res.status !== 200
|
|
||||||
) {
|
|
||||||
const responseTexts = [responseText];
|
|
||||||
let extraInfo = await res.clone().text();
|
|
||||||
try {
|
|
||||||
const resJson = await res.clone().json();
|
|
||||||
extraInfo = prettyObject(resJson);
|
|
||||||
} catch {}
|
|
||||||
|
|
||||||
if (res.status === 401) {
|
|
||||||
responseTexts.push(Locale.Error.Unauthorized);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (extraInfo) {
|
|
||||||
responseTexts.push(extraInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
responseText = responseTexts.join("\n\n");
|
|
||||||
|
|
||||||
return finish();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onmessage(msg) {
|
|
||||||
if (msg.data === "[DONE]" || finished) {
|
|
||||||
return finish();
|
|
||||||
}
|
|
||||||
const text = msg.data;
|
|
||||||
try {
|
|
||||||
const json = JSON.parse(text);
|
const json = JSON.parse(text);
|
||||||
const choices = json.choices as Array<{
|
const choices = json.choices as Array<{
|
||||||
delta: { content: string };
|
delta: {
|
||||||
|
content: string;
|
||||||
|
tool_calls: ChatMessageTool[];
|
||||||
|
};
|
||||||
}>;
|
}>;
|
||||||
const delta = choices[0]?.delta?.content;
|
const tool_calls = choices[0]?.delta?.tool_calls;
|
||||||
const textmoderation = json?.prompt_filter_results;
|
if (tool_calls?.length > 0) {
|
||||||
|
const index = tool_calls[0]?.index;
|
||||||
if (delta) {
|
const id = tool_calls[0]?.id;
|
||||||
remainText += delta;
|
const args = tool_calls[0]?.function?.arguments;
|
||||||
}
|
if (id) {
|
||||||
} catch (e) {
|
runTools.push({
|
||||||
console.error("[Request] parse error", text, msg);
|
id,
|
||||||
}
|
type: tool_calls[0]?.type,
|
||||||
|
function: {
|
||||||
|
name: tool_calls[0]?.function?.name as string,
|
||||||
|
arguments: args,
|
||||||
},
|
},
|
||||||
onclose() {
|
|
||||||
finish();
|
|
||||||
},
|
|
||||||
onerror(e) {
|
|
||||||
options.onError?.(e);
|
|
||||||
throw e;
|
|
||||||
},
|
|
||||||
openWhenHidden: true,
|
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
// @ts-ignore
|
||||||
|
runTools[index]["function"]["arguments"] += args;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return choices[0]?.delta?.content;
|
||||||
|
},
|
||||||
|
// processToolMessage, include tool_calls message and tool call results
|
||||||
|
(
|
||||||
|
requestPayload: RequestPayload,
|
||||||
|
toolCallMessage: any,
|
||||||
|
toolCallResult: any[],
|
||||||
|
) => {
|
||||||
|
// @ts-ignore
|
||||||
|
requestPayload?.messages?.splice(
|
||||||
|
// @ts-ignore
|
||||||
|
requestPayload?.messages?.length,
|
||||||
|
0,
|
||||||
|
toolCallMessage,
|
||||||
|
...toolCallResult,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
options,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
const res = await fetch(chatPath, chatPayload);
|
const res = await fetch(chatPath, chatPayload);
|
||||||
clearTimeout(requestTimeoutId);
|
clearTimeout(requestTimeoutId);
|
||||||
|
|
|
@ -9,15 +9,22 @@ import {
|
||||||
REQUEST_TIMEOUT_MS,
|
REQUEST_TIMEOUT_MS,
|
||||||
ServiceProvider,
|
ServiceProvider,
|
||||||
} from "@/app/constant";
|
} from "@/app/constant";
|
||||||
import { useAccessStore, useAppConfig, useChatStore } from "@/app/store";
|
import {
|
||||||
|
ChatMessageTool,
|
||||||
|
useAccessStore,
|
||||||
|
useAppConfig,
|
||||||
|
useChatStore,
|
||||||
|
usePluginStore,
|
||||||
|
} from "@/app/store";
|
||||||
import { collectModelsWithDefaultModel } from "@/app/utils/model";
|
import { collectModelsWithDefaultModel } from "@/app/utils/model";
|
||||||
import {
|
import {
|
||||||
preProcessImageContent,
|
preProcessImageContent,
|
||||||
uploadImage,
|
uploadImage,
|
||||||
base64Image2Blob,
|
base64Image2Blob,
|
||||||
|
stream,
|
||||||
} from "@/app/utils/chat";
|
} from "@/app/utils/chat";
|
||||||
import { cloudflareAIGatewayUrl } from "@/app/utils/cloudflare";
|
import { cloudflareAIGatewayUrl } from "@/app/utils/cloudflare";
|
||||||
import { DalleSize } from "@/app/typing";
|
import { DalleSize, DalleQuality, DalleStyle } from "@/app/typing";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ChatOptions,
|
ChatOptions,
|
||||||
|
@ -26,17 +33,12 @@ import {
|
||||||
LLMModel,
|
LLMModel,
|
||||||
LLMUsage,
|
LLMUsage,
|
||||||
MultimodalContent,
|
MultimodalContent,
|
||||||
|
SpeechOptions,
|
||||||
} from "../api";
|
} from "../api";
|
||||||
import Locale from "../../locales";
|
import Locale from "../../locales";
|
||||||
import {
|
|
||||||
EventStreamContentType,
|
|
||||||
fetchEventSource,
|
|
||||||
} from "@fortaine/fetch-event-source";
|
|
||||||
import { prettyObject } from "@/app/utils/format";
|
|
||||||
import { getClientConfig } from "@/app/config/client";
|
import { getClientConfig } from "@/app/config/client";
|
||||||
import {
|
import {
|
||||||
getMessageTextContent,
|
getMessageTextContent,
|
||||||
getMessageImages,
|
|
||||||
isVisionModel,
|
isVisionModel,
|
||||||
isDalle3 as _isDalle3,
|
isDalle3 as _isDalle3,
|
||||||
} from "@/app/utils";
|
} from "@/app/utils";
|
||||||
|
@ -70,6 +72,8 @@ export interface DalleRequestPayload {
|
||||||
response_format: "url" | "b64_json";
|
response_format: "url" | "b64_json";
|
||||||
n: number;
|
n: number;
|
||||||
size: DalleSize;
|
size: DalleSize;
|
||||||
|
quality: DalleQuality;
|
||||||
|
style: DalleStyle;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ChatGPTApi implements LLMApi {
|
export class ChatGPTApi implements LLMApi {
|
||||||
|
@ -138,6 +142,44 @@ export class ChatGPTApi implements LLMApi {
|
||||||
return res.choices?.at(0)?.message?.content ?? res;
|
return res.choices?.at(0)?.message?.content ?? res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async speech(options: SpeechOptions): Promise<ArrayBuffer> {
|
||||||
|
const requestPayload = {
|
||||||
|
model: options.model,
|
||||||
|
input: options.input,
|
||||||
|
voice: options.voice,
|
||||||
|
response_format: options.response_format,
|
||||||
|
speed: options.speed,
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log("[Request] openai speech payload: ", requestPayload);
|
||||||
|
|
||||||
|
const controller = new AbortController();
|
||||||
|
options.onController?.(controller);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const speechPath = this.path(OpenaiPath.SpeechPath);
|
||||||
|
const speechPayload = {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify(requestPayload),
|
||||||
|
signal: controller.signal,
|
||||||
|
headers: getHeaders(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// make a fetch request
|
||||||
|
const requestTimeoutId = setTimeout(
|
||||||
|
() => controller.abort(),
|
||||||
|
REQUEST_TIMEOUT_MS,
|
||||||
|
);
|
||||||
|
|
||||||
|
const res = await fetch(speechPath, speechPayload);
|
||||||
|
clearTimeout(requestTimeoutId);
|
||||||
|
return await res.arrayBuffer();
|
||||||
|
} catch (e) {
|
||||||
|
console.log("[Request] failed to make a speech request", e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async chat(options: ChatOptions) {
|
async chat(options: ChatOptions) {
|
||||||
const modelConfig = {
|
const modelConfig = {
|
||||||
...useAppConfig.getState().modelConfig,
|
...useAppConfig.getState().modelConfig,
|
||||||
|
@ -151,6 +193,7 @@ export class ChatGPTApi implements LLMApi {
|
||||||
let requestPayload: RequestPayload | DalleRequestPayload;
|
let requestPayload: RequestPayload | DalleRequestPayload;
|
||||||
|
|
||||||
const isDalle3 = _isDalle3(options.config.model);
|
const isDalle3 = _isDalle3(options.config.model);
|
||||||
|
const isO1 = options.config.model.startsWith("o1");
|
||||||
if (isDalle3) {
|
if (isDalle3) {
|
||||||
const prompt = getMessageTextContent(
|
const prompt = getMessageTextContent(
|
||||||
options.messages.slice(-1)?.pop() as any,
|
options.messages.slice(-1)?.pop() as any,
|
||||||
|
@ -162,6 +205,8 @@ export class ChatGPTApi implements LLMApi {
|
||||||
response_format: "b64_json", // using b64_json, and save image in CacheStorage
|
response_format: "b64_json", // using b64_json, and save image in CacheStorage
|
||||||
n: 1,
|
n: 1,
|
||||||
size: options.config?.size ?? "1024x1024",
|
size: options.config?.size ?? "1024x1024",
|
||||||
|
quality: options.config?.quality ?? "standard",
|
||||||
|
style: options.config?.style ?? "vivid",
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
const visionModel = isVisionModel(options.config.model);
|
const visionModel = isVisionModel(options.config.model);
|
||||||
|
@ -170,30 +215,32 @@ export class ChatGPTApi implements LLMApi {
|
||||||
const content = visionModel
|
const content = visionModel
|
||||||
? await preProcessImageContent(v.content)
|
? await preProcessImageContent(v.content)
|
||||||
: getMessageTextContent(v);
|
: getMessageTextContent(v);
|
||||||
|
if (!(isO1 && v.role === "system"))
|
||||||
messages.push({ role: v.role, content });
|
messages.push({ role: v.role, content });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// O1 not support image, tools (plugin in ChatGPTNextWeb) and system, stream, logprobs, temperature, top_p, n, presence_penalty, frequency_penalty yet.
|
||||||
requestPayload = {
|
requestPayload = {
|
||||||
messages,
|
messages,
|
||||||
stream: options.config.stream,
|
stream: !isO1 ? options.config.stream : false,
|
||||||
model: modelConfig.model,
|
model: modelConfig.model,
|
||||||
temperature: modelConfig.temperature,
|
temperature: !isO1 ? modelConfig.temperature : 1,
|
||||||
presence_penalty: modelConfig.presence_penalty,
|
presence_penalty: !isO1 ? modelConfig.presence_penalty : 0,
|
||||||
frequency_penalty: modelConfig.frequency_penalty,
|
frequency_penalty: !isO1 ? modelConfig.frequency_penalty : 0,
|
||||||
top_p: modelConfig.top_p,
|
top_p: !isO1 ? modelConfig.top_p : 1,
|
||||||
// max_tokens: Math.max(modelConfig.max_tokens, 1024),
|
// max_tokens: Math.max(modelConfig.max_tokens, 1024),
|
||||||
// Please do not ask me why not send max_tokens, no reason, this param is just shit, I dont want to explain anymore.
|
// Please do not ask me why not send max_tokens, no reason, this param is just shit, I dont want to explain anymore.
|
||||||
};
|
};
|
||||||
|
|
||||||
// add max_tokens to vision model
|
// add max_tokens to vision model
|
||||||
if (visionModel && modelConfig.model.includes("preview")) {
|
if (visionModel) {
|
||||||
requestPayload["max_tokens"] = Math.max(modelConfig.max_tokens, 4000);
|
requestPayload["max_tokens"] = Math.max(modelConfig.max_tokens, 4000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("[Request] openai payload: ", requestPayload);
|
console.log("[Request] openai payload: ", requestPayload);
|
||||||
|
|
||||||
const shouldStream = !isDalle3 && !!options.config.stream;
|
const shouldStream = !isDalle3 && !!options.config.stream && !isO1;
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
options.onController?.(controller);
|
options.onController?.(controller);
|
||||||
|
|
||||||
|
@ -229,6 +276,72 @@ export class ChatGPTApi implements LLMApi {
|
||||||
isDalle3 ? OpenaiPath.ImagePath : OpenaiPath.ChatPath,
|
isDalle3 ? OpenaiPath.ImagePath : OpenaiPath.ChatPath,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (shouldStream) {
|
||||||
|
let index = -1;
|
||||||
|
const [tools, funcs] = usePluginStore
|
||||||
|
.getState()
|
||||||
|
.getAsTools(
|
||||||
|
useChatStore.getState().currentSession().mask?.plugin || [],
|
||||||
|
);
|
||||||
|
// console.log("getAsTools", tools, funcs);
|
||||||
|
stream(
|
||||||
|
chatPath,
|
||||||
|
requestPayload,
|
||||||
|
getHeaders(),
|
||||||
|
tools as any,
|
||||||
|
funcs,
|
||||||
|
controller,
|
||||||
|
// parseSSE
|
||||||
|
(text: string, runTools: ChatMessageTool[]) => {
|
||||||
|
// console.log("parseSSE", text, runTools);
|
||||||
|
const json = JSON.parse(text);
|
||||||
|
const choices = json.choices as Array<{
|
||||||
|
delta: {
|
||||||
|
content: string;
|
||||||
|
tool_calls: ChatMessageTool[];
|
||||||
|
};
|
||||||
|
}>;
|
||||||
|
const tool_calls = choices[0]?.delta?.tool_calls;
|
||||||
|
if (tool_calls?.length > 0) {
|
||||||
|
const id = tool_calls[0]?.id;
|
||||||
|
const args = tool_calls[0]?.function?.arguments;
|
||||||
|
if (id) {
|
||||||
|
index += 1;
|
||||||
|
runTools.push({
|
||||||
|
id,
|
||||||
|
type: tool_calls[0]?.type,
|
||||||
|
function: {
|
||||||
|
name: tool_calls[0]?.function?.name as string,
|
||||||
|
arguments: args,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// @ts-ignore
|
||||||
|
runTools[index]["function"]["arguments"] += args;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return choices[0]?.delta?.content;
|
||||||
|
},
|
||||||
|
// processToolMessage, include tool_calls message and tool call results
|
||||||
|
(
|
||||||
|
requestPayload: RequestPayload,
|
||||||
|
toolCallMessage: any,
|
||||||
|
toolCallResult: any[],
|
||||||
|
) => {
|
||||||
|
// reset index value
|
||||||
|
index = -1;
|
||||||
|
// @ts-ignore
|
||||||
|
requestPayload?.messages?.splice(
|
||||||
|
// @ts-ignore
|
||||||
|
requestPayload?.messages?.length,
|
||||||
|
0,
|
||||||
|
toolCallMessage,
|
||||||
|
...toolCallResult,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
const chatPayload = {
|
const chatPayload = {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify(requestPayload),
|
body: JSON.stringify(requestPayload),
|
||||||
|
@ -239,133 +352,9 @@ export class ChatGPTApi implements LLMApi {
|
||||||
// make a fetch request
|
// make a fetch request
|
||||||
const requestTimeoutId = setTimeout(
|
const requestTimeoutId = setTimeout(
|
||||||
() => controller.abort(),
|
() => controller.abort(),
|
||||||
isDalle3 ? REQUEST_TIMEOUT_MS * 2 : REQUEST_TIMEOUT_MS, // dalle3 using b64_json is slow.
|
isDalle3 || isO1 ? REQUEST_TIMEOUT_MS * 2 : REQUEST_TIMEOUT_MS, // dalle3 using b64_json is slow.
|
||||||
);
|
);
|
||||||
|
|
||||||
if (shouldStream) {
|
|
||||||
let responseText = "";
|
|
||||||
let remainText = "";
|
|
||||||
let finished = false;
|
|
||||||
|
|
||||||
// animate response to make it looks smooth
|
|
||||||
function animateResponseText() {
|
|
||||||
if (finished || controller.signal.aborted) {
|
|
||||||
responseText += remainText;
|
|
||||||
console.log("[Response Animation] finished");
|
|
||||||
if (responseText?.length === 0) {
|
|
||||||
options.onError?.(new Error("empty response from server"));
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (remainText.length > 0) {
|
|
||||||
const fetchCount = Math.max(1, Math.round(remainText.length / 60));
|
|
||||||
const fetchText = remainText.slice(0, fetchCount);
|
|
||||||
responseText += fetchText;
|
|
||||||
remainText = remainText.slice(fetchCount);
|
|
||||||
options.onUpdate?.(responseText, fetchText);
|
|
||||||
}
|
|
||||||
|
|
||||||
requestAnimationFrame(animateResponseText);
|
|
||||||
}
|
|
||||||
|
|
||||||
// start animaion
|
|
||||||
animateResponseText();
|
|
||||||
|
|
||||||
const finish = () => {
|
|
||||||
if (!finished) {
|
|
||||||
finished = true;
|
|
||||||
options.onFinish(responseText + remainText);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
controller.signal.onabort = finish;
|
|
||||||
|
|
||||||
fetchEventSource(chatPath, {
|
|
||||||
...chatPayload,
|
|
||||||
async onopen(res) {
|
|
||||||
clearTimeout(requestTimeoutId);
|
|
||||||
const contentType = res.headers.get("content-type");
|
|
||||||
console.log(
|
|
||||||
"[OpenAI] request response content type: ",
|
|
||||||
contentType,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (contentType?.startsWith("text/plain")) {
|
|
||||||
responseText = await res.clone().text();
|
|
||||||
return finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
!res.ok ||
|
|
||||||
!res.headers
|
|
||||||
.get("content-type")
|
|
||||||
?.startsWith(EventStreamContentType) ||
|
|
||||||
res.status !== 200
|
|
||||||
) {
|
|
||||||
const responseTexts = [responseText];
|
|
||||||
let extraInfo = await res.clone().text();
|
|
||||||
try {
|
|
||||||
const resJson = await res.clone().json();
|
|
||||||
extraInfo = prettyObject(resJson);
|
|
||||||
} catch {}
|
|
||||||
|
|
||||||
if (res.status === 401) {
|
|
||||||
responseTexts.push(Locale.Error.Unauthorized);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (extraInfo) {
|
|
||||||
responseTexts.push(extraInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
responseText = responseTexts.join("\n\n");
|
|
||||||
|
|
||||||
return finish();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onmessage(msg) {
|
|
||||||
if (msg.data === "[DONE]" || finished) {
|
|
||||||
return finish();
|
|
||||||
}
|
|
||||||
const text = msg.data;
|
|
||||||
try {
|
|
||||||
const json = JSON.parse(text);
|
|
||||||
const choices = json.choices as Array<{
|
|
||||||
delta: { content: string };
|
|
||||||
}>;
|
|
||||||
const delta = choices[0]?.delta?.content;
|
|
||||||
const textmoderation = json?.prompt_filter_results;
|
|
||||||
|
|
||||||
if (delta) {
|
|
||||||
remainText += delta;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
textmoderation &&
|
|
||||||
textmoderation.length > 0 &&
|
|
||||||
ServiceProvider.Azure
|
|
||||||
) {
|
|
||||||
const contentFilterResults =
|
|
||||||
textmoderation[0]?.content_filter_results;
|
|
||||||
console.log(
|
|
||||||
`[${ServiceProvider.Azure}] [Text Moderation] flagged categories result:`,
|
|
||||||
contentFilterResults,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error("[Request] parse error", text, msg);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onclose() {
|
|
||||||
finish();
|
|
||||||
},
|
|
||||||
onerror(e) {
|
|
||||||
options.onError?.(e);
|
|
||||||
throw e;
|
|
||||||
},
|
|
||||||
openWhenHidden: true,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
const res = await fetch(chatPath, chatPayload);
|
const res = await fetch(chatPath, chatPayload);
|
||||||
clearTimeout(requestTimeoutId);
|
clearTimeout(requestTimeoutId);
|
||||||
|
|
||||||
|
@ -457,7 +446,9 @@ export class ChatGPTApi implements LLMApi {
|
||||||
});
|
});
|
||||||
|
|
||||||
const resJson = (await res.json()) as OpenAIListModelResponse;
|
const resJson = (await res.json()) as OpenAIListModelResponse;
|
||||||
const chatModels = resJson.data?.filter((m) => m.id.startsWith("gpt-"));
|
const chatModels = resJson.data?.filter(
|
||||||
|
(m) => m.id.startsWith("gpt-") || m.id.startsWith("chatgpt-"),
|
||||||
|
);
|
||||||
console.log("[Models]", chatModels);
|
console.log("[Models]", chatModels);
|
||||||
|
|
||||||
if (!chatModels) {
|
if (!chatModels) {
|
||||||
|
|
|
@ -8,6 +8,7 @@ import {
|
||||||
LLMApi,
|
LLMApi,
|
||||||
LLMModel,
|
LLMModel,
|
||||||
MultimodalContent,
|
MultimodalContent,
|
||||||
|
SpeechOptions,
|
||||||
} from "../api";
|
} from "../api";
|
||||||
import Locale from "../../locales";
|
import Locale from "../../locales";
|
||||||
import {
|
import {
|
||||||
|
@ -89,10 +90,15 @@ export class HunyuanApi implements LLMApi {
|
||||||
return res.Choices?.at(0)?.Message?.Content ?? "";
|
return res.Choices?.at(0)?.Message?.Content ?? "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
speech(options: SpeechOptions): Promise<ArrayBuffer> {
|
||||||
|
throw new Error("Method not implemented.");
|
||||||
|
}
|
||||||
|
|
||||||
async chat(options: ChatOptions) {
|
async chat(options: ChatOptions) {
|
||||||
const visionModel = isVisionModel(options.config.model);
|
const visionModel = isVisionModel(options.config.model);
|
||||||
const messages = options.messages.map((v) => ({
|
const messages = options.messages.map((v, index) => ({
|
||||||
role: v.role,
|
// "Messages 中 system 角色必须位于列表的最开始"
|
||||||
|
role: index !== 0 && v.role === "system" ? "user" : v.role,
|
||||||
content: visionModel ? v.content : getMessageTextContent(v),
|
content: visionModel ? v.content : getMessageTextContent(v),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@ interface ChatCommands {
|
||||||
next?: Command;
|
next?: Command;
|
||||||
prev?: Command;
|
prev?: Command;
|
||||||
clear?: Command;
|
clear?: Command;
|
||||||
|
fork?: Command;
|
||||||
del?: Command;
|
del?: Command;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
import { useEffect, useState, useRef, useMemo } from "react";
|
import {
|
||||||
|
useEffect,
|
||||||
|
useState,
|
||||||
|
useRef,
|
||||||
|
useMemo,
|
||||||
|
forwardRef,
|
||||||
|
useImperativeHandle,
|
||||||
|
} from "react";
|
||||||
import { useParams } from "react-router";
|
import { useParams } from "react-router";
|
||||||
import { useWindowSize } from "@/app/utils";
|
|
||||||
import { IconButton } from "./button";
|
import { IconButton } from "./button";
|
||||||
import { nanoid } from "nanoid";
|
import { nanoid } from "nanoid";
|
||||||
import ExportIcon from "../icons/share.svg";
|
import ExportIcon from "../icons/share.svg";
|
||||||
|
@ -8,6 +14,7 @@ import CopyIcon from "../icons/copy.svg";
|
||||||
import DownloadIcon from "../icons/download.svg";
|
import DownloadIcon from "../icons/download.svg";
|
||||||
import GithubIcon from "../icons/github.svg";
|
import GithubIcon from "../icons/github.svg";
|
||||||
import LoadingButtonIcon from "../icons/loading.svg";
|
import LoadingButtonIcon from "../icons/loading.svg";
|
||||||
|
import ReloadButtonIcon from "../icons/reload.svg";
|
||||||
import Locale from "../locales";
|
import Locale from "../locales";
|
||||||
import { Modal, showToast } from "./ui-lib";
|
import { Modal, showToast } from "./ui-lib";
|
||||||
import { copyToClipboard, downloadAs } from "../utils";
|
import { copyToClipboard, downloadAs } from "../utils";
|
||||||
|
@ -15,14 +22,21 @@ import { Path, ApiPath, REPO_URL } from "@/app/constant";
|
||||||
import { Loading } from "./home";
|
import { Loading } from "./home";
|
||||||
import styles from "./artifacts.module.scss";
|
import styles from "./artifacts.module.scss";
|
||||||
|
|
||||||
export function HTMLPreview(props: {
|
type HTMLPreviewProps = {
|
||||||
code: string;
|
code: string;
|
||||||
autoHeight?: boolean;
|
autoHeight?: boolean;
|
||||||
height?: number | string;
|
height?: number | string;
|
||||||
onLoad?: (title?: string) => void;
|
onLoad?: (title?: string) => void;
|
||||||
}) {
|
};
|
||||||
const ref = useRef<HTMLIFrameElement>(null);
|
|
||||||
const frameId = useRef<string>(nanoid());
|
export type HTMLPreviewHander = {
|
||||||
|
reload: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const HTMLPreview = forwardRef<HTMLPreviewHander, HTMLPreviewProps>(
|
||||||
|
function HTMLPreview(props, ref) {
|
||||||
|
const iframeRef = useRef<HTMLIFrameElement>(null);
|
||||||
|
const [frameId, setFrameId] = useState<string>(nanoid());
|
||||||
const [iframeHeight, setIframeHeight] = useState(600);
|
const [iframeHeight, setIframeHeight] = useState(600);
|
||||||
const [title, setTitle] = useState("");
|
const [title, setTitle] = useState("");
|
||||||
/*
|
/*
|
||||||
|
@ -37,7 +51,7 @@ export function HTMLPreview(props: {
|
||||||
const handleMessage = (e: any) => {
|
const handleMessage = (e: any) => {
|
||||||
const { id, height, title } = e.data;
|
const { id, height, title } = e.data;
|
||||||
setTitle(title);
|
setTitle(title);
|
||||||
if (id == frameId.current) {
|
if (id == frameId) {
|
||||||
setIframeHeight(height);
|
setIframeHeight(height);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -45,7 +59,13 @@ export function HTMLPreview(props: {
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener("message", handleMessage);
|
window.removeEventListener("message", handleMessage);
|
||||||
};
|
};
|
||||||
}, []);
|
}, [frameId]);
|
||||||
|
|
||||||
|
useImperativeHandle(ref, () => ({
|
||||||
|
reload: () => {
|
||||||
|
setFrameId(nanoid());
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
const height = useMemo(() => {
|
const height = useMemo(() => {
|
||||||
if (!props.autoHeight) return props.height || 600;
|
if (!props.autoHeight) return props.height || 600;
|
||||||
|
@ -53,16 +73,18 @@ export function HTMLPreview(props: {
|
||||||
return props.height;
|
return props.height;
|
||||||
}
|
}
|
||||||
const parentHeight = props.height || 600;
|
const parentHeight = props.height || 600;
|
||||||
return iframeHeight + 40 > parentHeight ? parentHeight : iframeHeight + 40;
|
return iframeHeight + 40 > parentHeight
|
||||||
|
? parentHeight
|
||||||
|
: iframeHeight + 40;
|
||||||
}, [props.autoHeight, props.height, iframeHeight]);
|
}, [props.autoHeight, props.height, iframeHeight]);
|
||||||
|
|
||||||
const srcDoc = useMemo(() => {
|
const srcDoc = useMemo(() => {
|
||||||
const script = `<script>new ResizeObserver((entries) => parent.postMessage({id: '${frameId.current}', height: entries[0].target.clientHeight}, '*')).observe(document.body)</script>`;
|
const script = `<script>window.addEventListener("DOMContentLoaded", () => new ResizeObserver((entries) => parent.postMessage({id: '${frameId}', height: entries[0].target.clientHeight}, '*')).observe(document.body))</script>`;
|
||||||
if (props.code.includes("</head>")) {
|
if (props.code.includes("<!DOCTYPE html>")) {
|
||||||
props.code.replace("</head>", "</head>" + script);
|
props.code.replace("<!DOCTYPE html>", "<!DOCTYPE html>" + script);
|
||||||
}
|
}
|
||||||
return props.code + script;
|
return script + props.code;
|
||||||
}, [props.code]);
|
}, [props.code, frameId]);
|
||||||
|
|
||||||
const handleOnLoad = () => {
|
const handleOnLoad = () => {
|
||||||
if (props?.onLoad) {
|
if (props?.onLoad) {
|
||||||
|
@ -73,15 +95,16 @@ export function HTMLPreview(props: {
|
||||||
return (
|
return (
|
||||||
<iframe
|
<iframe
|
||||||
className={styles["artifacts-iframe"]}
|
className={styles["artifacts-iframe"]}
|
||||||
id={frameId.current}
|
key={frameId}
|
||||||
ref={ref}
|
ref={iframeRef}
|
||||||
sandbox="allow-forms allow-modals allow-scripts"
|
sandbox="allow-forms allow-modals allow-scripts"
|
||||||
style={{ height }}
|
style={{ height }}
|
||||||
srcDoc={srcDoc}
|
srcDoc={srcDoc}
|
||||||
onLoad={handleOnLoad}
|
onLoad={handleOnLoad}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
|
);
|
||||||
|
|
||||||
export function ArtifactsShareButton({
|
export function ArtifactsShareButton({
|
||||||
getCode,
|
getCode,
|
||||||
|
@ -184,6 +207,7 @@ export function Artifacts() {
|
||||||
const [code, setCode] = useState("");
|
const [code, setCode] = useState("");
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [fileName, setFileName] = useState("");
|
const [fileName, setFileName] = useState("");
|
||||||
|
const previewRef = useRef<HTMLPreviewHander>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (id) {
|
if (id) {
|
||||||
|
@ -208,6 +232,13 @@ export function Artifacts() {
|
||||||
<a href={REPO_URL} target="_blank" rel="noopener noreferrer">
|
<a href={REPO_URL} target="_blank" rel="noopener noreferrer">
|
||||||
<IconButton bordered icon={<GithubIcon />} shadow />
|
<IconButton bordered icon={<GithubIcon />} shadow />
|
||||||
</a>
|
</a>
|
||||||
|
<IconButton
|
||||||
|
bordered
|
||||||
|
style={{ marginLeft: 20 }}
|
||||||
|
icon={<ReloadButtonIcon />}
|
||||||
|
shadow
|
||||||
|
onClick={() => previewRef.current?.reload()}
|
||||||
|
/>
|
||||||
<div className={styles["artifacts-title"]}>NextChat Artifacts</div>
|
<div className={styles["artifacts-title"]}>NextChat Artifacts</div>
|
||||||
<ArtifactsShareButton
|
<ArtifactsShareButton
|
||||||
id={id}
|
id={id}
|
||||||
|
@ -220,6 +251,7 @@ export function Artifacts() {
|
||||||
{code && (
|
{code && (
|
||||||
<HTMLPreview
|
<HTMLPreview
|
||||||
code={code}
|
code={code}
|
||||||
|
ref={previewRef}
|
||||||
autoHeight={false}
|
autoHeight={false}
|
||||||
height={"100%"}
|
height={"100%"}
|
||||||
onLoad={(title) => {
|
onLoad={(title) => {
|
||||||
|
|
|
@ -1,12 +1,70 @@
|
||||||
.auth-page {
|
.auth-page {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: flex-start;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
.top-banner {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 12px 64px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background: var(--second);
|
||||||
|
.top-banner-inner {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 150%;
|
||||||
|
span {
|
||||||
|
gap: 8px;
|
||||||
|
a {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
text-decoration: none;
|
||||||
|
margin-left: 8px;
|
||||||
|
color: var(--primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.top-banner-close {
|
||||||
|
cursor: pointer;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
right: 48px;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.top-banner {
|
||||||
|
padding: 12px 24px 12px 12px;
|
||||||
|
.top-banner-close {
|
||||||
|
right: 10px;
|
||||||
|
}
|
||||||
|
.top-banner-inner {
|
||||||
|
.top-banner-logo {
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
animation: slide-in-from-top ease 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
.auth-logo {
|
.auth-logo {
|
||||||
|
margin-top: 10vh;
|
||||||
transform: scale(1.4);
|
transform: scale(1.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,6 +72,7 @@
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
line-height: 2;
|
line-height: 2;
|
||||||
|
margin-bottom: 1vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
.auth-tips {
|
.auth-tips {
|
||||||
|
@ -24,6 +83,10 @@
|
||||||
margin: 3vh 0;
|
margin: 3vh 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.auth-input-second {
|
||||||
|
margin: 0 0 3vh 0;
|
||||||
|
}
|
||||||
|
|
||||||
.auth-actions {
|
.auth-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
|
@ -1,21 +1,34 @@
|
||||||
import styles from "./auth.module.scss";
|
import styles from "./auth.module.scss";
|
||||||
import { IconButton } from "./button";
|
import { IconButton } from "./button";
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { Path } from "../constant";
|
import { Path, SAAS_CHAT_URL } from "../constant";
|
||||||
import { useAccessStore } from "../store";
|
import { useAccessStore } from "../store";
|
||||||
import Locale from "../locales";
|
import Locale from "../locales";
|
||||||
|
import Delete from "../icons/close.svg";
|
||||||
|
import Arrow from "../icons/arrow.svg";
|
||||||
|
import Logo from "../icons/logo.svg";
|
||||||
|
import { useMobileScreen } from "@/app/utils";
|
||||||
import BotIcon from "../icons/bot.svg";
|
import BotIcon from "../icons/bot.svg";
|
||||||
import { useEffect } from "react";
|
|
||||||
import { getClientConfig } from "../config/client";
|
import { getClientConfig } from "../config/client";
|
||||||
|
import LeftIcon from "@/app/icons/left.svg";
|
||||||
|
import { safeLocalStorage } from "@/app/utils";
|
||||||
|
import {
|
||||||
|
trackSettingsPageGuideToCPaymentClick,
|
||||||
|
trackAuthorizationPageButtonToCPaymentClick,
|
||||||
|
} from "../utils/auth-settings-events";
|
||||||
|
const storage = safeLocalStorage();
|
||||||
|
|
||||||
export function AuthPage() {
|
export function AuthPage() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const accessStore = useAccessStore();
|
const accessStore = useAccessStore();
|
||||||
|
|
||||||
const goHome = () => navigate(Path.Home);
|
const goHome = () => navigate(Path.Home);
|
||||||
const goChat = () => navigate(Path.Chat);
|
const goChat = () => navigate(Path.Chat);
|
||||||
|
const goSaas = () => {
|
||||||
|
trackAuthorizationPageButtonToCPaymentClick();
|
||||||
|
window.location.href = SAAS_CHAT_URL;
|
||||||
|
};
|
||||||
|
|
||||||
const resetAccessCode = () => {
|
const resetAccessCode = () => {
|
||||||
accessStore.update((access) => {
|
accessStore.update((access) => {
|
||||||
access.openaiApiKey = "";
|
access.openaiApiKey = "";
|
||||||
|
@ -32,6 +45,14 @@ export function AuthPage() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles["auth-page"]}>
|
<div className={styles["auth-page"]}>
|
||||||
|
<TopBanner></TopBanner>
|
||||||
|
<div className={styles["auth-header"]}>
|
||||||
|
<IconButton
|
||||||
|
icon={<LeftIcon />}
|
||||||
|
text={Locale.Auth.Return}
|
||||||
|
onClick={() => navigate(Path.Home)}
|
||||||
|
></IconButton>
|
||||||
|
</div>
|
||||||
<div className={`no-dark ${styles["auth-logo"]}`}>
|
<div className={`no-dark ${styles["auth-logo"]}`}>
|
||||||
<BotIcon />
|
<BotIcon />
|
||||||
</div>
|
</div>
|
||||||
|
@ -65,7 +86,7 @@ export function AuthPage() {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
className={styles["auth-input"]}
|
className={styles["auth-input-second"]}
|
||||||
type="password"
|
type="password"
|
||||||
placeholder={Locale.Settings.Access.Google.ApiKey.Placeholder}
|
placeholder={Locale.Settings.Access.Google.ApiKey.Placeholder}
|
||||||
value={accessStore.googleApiKey}
|
value={accessStore.googleApiKey}
|
||||||
|
@ -85,13 +106,74 @@ export function AuthPage() {
|
||||||
onClick={goChat}
|
onClick={goChat}
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
text={Locale.Auth.Later}
|
text={Locale.Auth.SaasTips}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
resetAccessCode();
|
goSaas();
|
||||||
goHome();
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function TopBanner() {
|
||||||
|
const [isHovered, setIsHovered] = useState(false);
|
||||||
|
const [isVisible, setIsVisible] = useState(true);
|
||||||
|
const isMobile = useMobileScreen();
|
||||||
|
useEffect(() => {
|
||||||
|
// 检查 localStorage 中是否有标记
|
||||||
|
const bannerDismissed = storage.getItem("bannerDismissed");
|
||||||
|
// 如果标记不存在,存储默认值并显示横幅
|
||||||
|
if (!bannerDismissed) {
|
||||||
|
storage.setItem("bannerDismissed", "false");
|
||||||
|
setIsVisible(true); // 显示横幅
|
||||||
|
} else if (bannerDismissed === "true") {
|
||||||
|
// 如果标记为 "true",则隐藏横幅
|
||||||
|
setIsVisible(false);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleMouseEnter = () => {
|
||||||
|
setIsHovered(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMouseLeave = () => {
|
||||||
|
setIsHovered(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
setIsVisible(false);
|
||||||
|
storage.setItem("bannerDismissed", "true");
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!isVisible) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={styles["top-banner"]}
|
||||||
|
onMouseEnter={handleMouseEnter}
|
||||||
|
onMouseLeave={handleMouseLeave}
|
||||||
|
>
|
||||||
|
<div className={`${styles["top-banner-inner"]} no-dark`}>
|
||||||
|
<Logo className={styles["top-banner-logo"]}></Logo>
|
||||||
|
<span>
|
||||||
|
{Locale.Auth.TopTips}
|
||||||
|
<a
|
||||||
|
href={SAAS_CHAT_URL}
|
||||||
|
rel="stylesheet"
|
||||||
|
onClick={() => {
|
||||||
|
trackSettingsPageGuideToCPaymentClick();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{Locale.Settings.Access.SaasStart.ChatNow}
|
||||||
|
<Arrow style={{ marginLeft: "4px" }} />
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{(isHovered || isMobile) && (
|
||||||
|
<Delete className={styles["top-banner-close"]} onClick={handleClose} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
|
@ -18,6 +18,7 @@ export function IconButton(props: {
|
||||||
tabIndex?: number;
|
tabIndex?: number;
|
||||||
autoFocus?: boolean;
|
autoFocus?: boolean;
|
||||||
style?: CSSProperties;
|
style?: CSSProperties;
|
||||||
|
aria?: string;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
|
@ -34,9 +35,11 @@ export function IconButton(props: {
|
||||||
tabIndex={props.tabIndex}
|
tabIndex={props.tabIndex}
|
||||||
autoFocus={props.autoFocus}
|
autoFocus={props.autoFocus}
|
||||||
style={props.style}
|
style={props.style}
|
||||||
|
aria-label={props.aria}
|
||||||
>
|
>
|
||||||
{props.icon && (
|
{props.icon && (
|
||||||
<div
|
<div
|
||||||
|
aria-label={props.text || props.title}
|
||||||
className={
|
className={
|
||||||
styles["icon-button-icon"] +
|
styles["icon-button-icon"] +
|
||||||
` ${props.type === "primary" && "no-dark"}`
|
` ${props.type === "primary" && "no-dark"}`
|
||||||
|
@ -47,7 +50,12 @@ export function IconButton(props: {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{props.text && (
|
{props.text && (
|
||||||
<div className={styles["icon-button-text"]}>{props.text}</div>
|
<div
|
||||||
|
aria-label={props.text || props.title}
|
||||||
|
className={styles["icon-button-text"]}
|
||||||
|
>
|
||||||
|
{props.text}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import DeleteIcon from "../icons/delete.svg";
|
import DeleteIcon from "../icons/delete.svg";
|
||||||
import BotIcon from "../icons/bot.svg";
|
|
||||||
|
|
||||||
import styles from "./home.module.scss";
|
import styles from "./home.module.scss";
|
||||||
import {
|
import {
|
||||||
|
@ -12,7 +11,7 @@ import {
|
||||||
import { useChatStore } from "../store";
|
import { useChatStore } from "../store";
|
||||||
|
|
||||||
import Locale from "../locales";
|
import Locale from "../locales";
|
||||||
import { Link, useLocation, useNavigate } from "react-router-dom";
|
import { useLocation, useNavigate } from "react-router-dom";
|
||||||
import { Path } from "../constant";
|
import { Path } from "../constant";
|
||||||
import { MaskAvatar } from "./mask";
|
import { MaskAvatar } from "./mask";
|
||||||
import { Mask } from "../store/mask";
|
import { Mask } from "../store/mask";
|
||||||
|
|
|
@ -346,6 +346,12 @@
|
||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.chat-model-name {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--black);
|
||||||
|
margin-left: 6px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-message-container {
|
.chat-message-container {
|
||||||
|
@ -407,6 +413,21 @@
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.chat-message-tools {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #aaa;
|
||||||
|
line-height: 1.5;
|
||||||
|
margin-top: 5px;
|
||||||
|
.chat-message-tool {
|
||||||
|
display: flex;
|
||||||
|
align-items: end;
|
||||||
|
svg {
|
||||||
|
margin-left: 5px;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.chat-message-item {
|
.chat-message-item {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
|
@ -625,3 +646,51 @@
|
||||||
bottom: 30px;
|
bottom: 30px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.shortcut-key-container {
|
||||||
|
padding: 10px;
|
||||||
|
overflow-y: auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shortcut-key-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shortcut-key-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 10px;
|
||||||
|
background-color: var(--white);
|
||||||
|
}
|
||||||
|
|
||||||
|
.shortcut-key-title {
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--black);
|
||||||
|
}
|
||||||
|
|
||||||
|
.shortcut-key-keys {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shortcut-key {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border: var(--border-in-light);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 4px;
|
||||||
|
background-color: var(--gray);
|
||||||
|
min-width: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shortcut-key span {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--black);
|
||||||
|
}
|
|
@ -15,6 +15,8 @@ import RenameIcon from "../icons/rename.svg";
|
||||||
import ExportIcon from "../icons/share.svg";
|
import ExportIcon from "../icons/share.svg";
|
||||||
import ReturnIcon from "../icons/return.svg";
|
import ReturnIcon from "../icons/return.svg";
|
||||||
import CopyIcon from "../icons/copy.svg";
|
import CopyIcon from "../icons/copy.svg";
|
||||||
|
import SpeakIcon from "../icons/speak.svg";
|
||||||
|
import SpeakStopIcon from "../icons/speak-stop.svg";
|
||||||
import LoadingIcon from "../icons/three-dots.svg";
|
import LoadingIcon from "../icons/three-dots.svg";
|
||||||
import LoadingButtonIcon from "../icons/loading.svg";
|
import LoadingButtonIcon from "../icons/loading.svg";
|
||||||
import PromptIcon from "../icons/prompt.svg";
|
import PromptIcon from "../icons/prompt.svg";
|
||||||
|
@ -28,6 +30,7 @@ import DeleteIcon from "../icons/clear.svg";
|
||||||
import PinIcon from "../icons/pin.svg";
|
import PinIcon from "../icons/pin.svg";
|
||||||
import EditIcon from "../icons/rename.svg";
|
import EditIcon from "../icons/rename.svg";
|
||||||
import ConfirmIcon from "../icons/confirm.svg";
|
import ConfirmIcon from "../icons/confirm.svg";
|
||||||
|
import CloseIcon from "../icons/close.svg";
|
||||||
import CancelIcon from "../icons/cancel.svg";
|
import CancelIcon from "../icons/cancel.svg";
|
||||||
import ImageIcon from "../icons/image.svg";
|
import ImageIcon from "../icons/image.svg";
|
||||||
|
|
||||||
|
@ -38,7 +41,11 @@ import BottomIcon from "../icons/bottom.svg";
|
||||||
import StopIcon from "../icons/pause.svg";
|
import StopIcon from "../icons/pause.svg";
|
||||||
import RobotIcon from "../icons/robot.svg";
|
import RobotIcon from "../icons/robot.svg";
|
||||||
import SizeIcon from "../icons/size.svg";
|
import SizeIcon from "../icons/size.svg";
|
||||||
|
import QualityIcon from "../icons/hd.svg";
|
||||||
|
import StyleIcon from "../icons/palette.svg";
|
||||||
import PluginIcon from "../icons/plugin.svg";
|
import PluginIcon from "../icons/plugin.svg";
|
||||||
|
import ShortcutkeyIcon from "../icons/shortcutkey.svg";
|
||||||
|
import ReloadIcon from "../icons/reload.svg";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ChatMessage,
|
ChatMessage,
|
||||||
|
@ -51,6 +58,7 @@ import {
|
||||||
useAppConfig,
|
useAppConfig,
|
||||||
DEFAULT_TOPIC,
|
DEFAULT_TOPIC,
|
||||||
ModelType,
|
ModelType,
|
||||||
|
usePluginStore,
|
||||||
} from "../store";
|
} from "../store";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -62,6 +70,8 @@ import {
|
||||||
getMessageImages,
|
getMessageImages,
|
||||||
isVisionModel,
|
isVisionModel,
|
||||||
isDalle3,
|
isDalle3,
|
||||||
|
showPlugins,
|
||||||
|
safeLocalStorage,
|
||||||
} from "../utils";
|
} from "../utils";
|
||||||
|
|
||||||
import { uploadImage as uploadImageRemote } from "@/app/utils/chat";
|
import { uploadImage as uploadImageRemote } from "@/app/utils/chat";
|
||||||
|
@ -69,7 +79,7 @@ import { uploadImage as uploadImageRemote } from "@/app/utils/chat";
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
|
|
||||||
import { ChatControllerPool } from "../client/controller";
|
import { ChatControllerPool } from "../client/controller";
|
||||||
import { DalleSize } from "../typing";
|
import { DalleSize, DalleQuality, DalleStyle } from "../typing";
|
||||||
import { Prompt, usePromptStore } from "../store/prompt";
|
import { Prompt, usePromptStore } from "../store/prompt";
|
||||||
import Locale from "../locales";
|
import Locale from "../locales";
|
||||||
|
|
||||||
|
@ -88,12 +98,12 @@ import {
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import {
|
import {
|
||||||
CHAT_PAGE_SIZE,
|
CHAT_PAGE_SIZE,
|
||||||
LAST_INPUT_KEY,
|
DEFAULT_TTS_ENGINE,
|
||||||
|
ModelProvider,
|
||||||
Path,
|
Path,
|
||||||
REQUEST_TIMEOUT_MS,
|
REQUEST_TIMEOUT_MS,
|
||||||
UNFINISHED_INPUT,
|
UNFINISHED_INPUT,
|
||||||
ServiceProvider,
|
ServiceProvider,
|
||||||
Plugin,
|
|
||||||
} from "../constant";
|
} from "../constant";
|
||||||
import { Avatar } from "./emoji";
|
import { Avatar } from "./emoji";
|
||||||
import { ContextPrompts, MaskAvatar, MaskConfig } from "./mask";
|
import { ContextPrompts, MaskAvatar, MaskConfig } from "./mask";
|
||||||
|
@ -105,6 +115,13 @@ import { getClientConfig } from "../config/client";
|
||||||
import { useAllModels } from "../utils/hooks";
|
import { useAllModels } from "../utils/hooks";
|
||||||
import { MultimodalContent } from "../client/api";
|
import { MultimodalContent } from "../client/api";
|
||||||
|
|
||||||
|
const localStorage = safeLocalStorage();
|
||||||
|
import { ClientApi } from "../client/api";
|
||||||
|
import { createTTSPlayer } from "../utils/audio";
|
||||||
|
import { MsEdgeTTS, OUTPUT_FORMAT } from "../utils/ms_edge_tts";
|
||||||
|
|
||||||
|
const ttsPlayer = createTTSPlayer();
|
||||||
|
|
||||||
const Markdown = dynamic(async () => (await import("./markdown")).Markdown, {
|
const Markdown = dynamic(async () => (await import("./markdown")).Markdown, {
|
||||||
loading: () => <LoadingIcon />,
|
loading: () => <LoadingIcon />,
|
||||||
});
|
});
|
||||||
|
@ -184,7 +201,7 @@ function PromptToast(props: {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles["prompt-toast"]} key="prompt-toast">
|
<div className={styles["prompt-toast"]} key="prompt-toast">
|
||||||
{props.showToast && (
|
{props.showToast && context.length > 0 && (
|
||||||
<div
|
<div
|
||||||
className={styles["prompt-toast-inner"] + " clickable"}
|
className={styles["prompt-toast-inner"] + " clickable"}
|
||||||
role="button"
|
role="button"
|
||||||
|
@ -433,10 +450,13 @@ export function ChatActions(props: {
|
||||||
showPromptHints: () => void;
|
showPromptHints: () => void;
|
||||||
hitBottom: boolean;
|
hitBottom: boolean;
|
||||||
uploading: boolean;
|
uploading: boolean;
|
||||||
|
setShowShortcutKeyModal: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
|
setUserInput: (input: string) => void;
|
||||||
}) {
|
}) {
|
||||||
const config = useAppConfig();
|
const config = useAppConfig();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const chatStore = useChatStore();
|
const chatStore = useChatStore();
|
||||||
|
const pluginStore = usePluginStore();
|
||||||
|
|
||||||
// switch themes
|
// switch themes
|
||||||
const theme = config.theme;
|
const theme = config.theme;
|
||||||
|
@ -485,9 +505,19 @@ export function ChatActions(props: {
|
||||||
const [showUploadImage, setShowUploadImage] = useState(false);
|
const [showUploadImage, setShowUploadImage] = useState(false);
|
||||||
|
|
||||||
const [showSizeSelector, setShowSizeSelector] = useState(false);
|
const [showSizeSelector, setShowSizeSelector] = useState(false);
|
||||||
|
const [showQualitySelector, setShowQualitySelector] = useState(false);
|
||||||
|
const [showStyleSelector, setShowStyleSelector] = useState(false);
|
||||||
const dalle3Sizes: DalleSize[] = ["1024x1024", "1792x1024", "1024x1792"];
|
const dalle3Sizes: DalleSize[] = ["1024x1024", "1792x1024", "1024x1792"];
|
||||||
|
const dalle3Qualitys: DalleQuality[] = ["standard", "hd"];
|
||||||
|
const dalle3Styles: DalleStyle[] = ["vivid", "natural"];
|
||||||
const currentSize =
|
const currentSize =
|
||||||
chatStore.currentSession().mask.modelConfig?.size ?? "1024x1024";
|
chatStore.currentSession().mask.modelConfig?.size ?? "1024x1024";
|
||||||
|
const currentQuality =
|
||||||
|
chatStore.currentSession().mask.modelConfig?.quality ?? "standard";
|
||||||
|
const currentStyle =
|
||||||
|
chatStore.currentSession().mask.modelConfig?.style ?? "vivid";
|
||||||
|
|
||||||
|
const isMobileScreen = useMobileScreen();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const show = isVisionModel(currentModel);
|
const show = isVisionModel(currentModel);
|
||||||
|
@ -499,8 +529,8 @@ export function ChatActions(props: {
|
||||||
|
|
||||||
// if current model is not available
|
// if current model is not available
|
||||||
// switch to first available model
|
// switch to first available model
|
||||||
const isUnavaliableModel = !models.some((m) => m.name === currentModel);
|
const isUnavailableModel = !models.some((m) => m.name === currentModel);
|
||||||
if (isUnavaliableModel && models.length > 0) {
|
if (isUnavailableModel && models.length > 0) {
|
||||||
// show next model to default model if exist
|
// show next model to default model if exist
|
||||||
let nextModel = models.find((model) => model.isDefault) || models[0];
|
let nextModel = models.find((model) => model.isDefault) || models[0];
|
||||||
chatStore.updateCurrentSession((session) => {
|
chatStore.updateCurrentSession((session) => {
|
||||||
|
@ -604,7 +634,7 @@ export function ChatActions(props: {
|
||||||
items={models.map((m) => ({
|
items={models.map((m) => ({
|
||||||
title: `${m.displayName}${
|
title: `${m.displayName}${
|
||||||
m?.provider?.providerName
|
m?.provider?.providerName
|
||||||
? "(" + m?.provider?.providerName + ")"
|
? " (" + m?.provider?.providerName + ")"
|
||||||
: ""
|
: ""
|
||||||
}`,
|
}`,
|
||||||
value: `${m.name}@${m?.provider?.providerName}`,
|
value: `${m.name}@${m?.provider?.providerName}`,
|
||||||
|
@ -659,33 +689,97 @@ export function ChatActions(props: {
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{isDalle3(currentModel) && (
|
||||||
<ChatAction
|
<ChatAction
|
||||||
onClick={() => setShowPluginSelector(true)}
|
onClick={() => setShowQualitySelector(true)}
|
||||||
|
text={currentQuality}
|
||||||
|
icon={<QualityIcon />}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{showQualitySelector && (
|
||||||
|
<Selector
|
||||||
|
defaultSelectedValue={currentQuality}
|
||||||
|
items={dalle3Qualitys.map((m) => ({
|
||||||
|
title: m,
|
||||||
|
value: m,
|
||||||
|
}))}
|
||||||
|
onClose={() => setShowQualitySelector(false)}
|
||||||
|
onSelection={(q) => {
|
||||||
|
if (q.length === 0) return;
|
||||||
|
const quality = q[0];
|
||||||
|
chatStore.updateCurrentSession((session) => {
|
||||||
|
session.mask.modelConfig.quality = quality;
|
||||||
|
});
|
||||||
|
showToast(quality);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isDalle3(currentModel) && (
|
||||||
|
<ChatAction
|
||||||
|
onClick={() => setShowStyleSelector(true)}
|
||||||
|
text={currentStyle}
|
||||||
|
icon={<StyleIcon />}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{showStyleSelector && (
|
||||||
|
<Selector
|
||||||
|
defaultSelectedValue={currentStyle}
|
||||||
|
items={dalle3Styles.map((m) => ({
|
||||||
|
title: m,
|
||||||
|
value: m,
|
||||||
|
}))}
|
||||||
|
onClose={() => setShowStyleSelector(false)}
|
||||||
|
onSelection={(s) => {
|
||||||
|
if (s.length === 0) return;
|
||||||
|
const style = s[0];
|
||||||
|
chatStore.updateCurrentSession((session) => {
|
||||||
|
session.mask.modelConfig.style = style;
|
||||||
|
});
|
||||||
|
showToast(style);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{showPlugins(currentProviderName, currentModel) && (
|
||||||
|
<ChatAction
|
||||||
|
onClick={() => {
|
||||||
|
if (pluginStore.getAll().length == 0) {
|
||||||
|
navigate(Path.Plugins);
|
||||||
|
} else {
|
||||||
|
setShowPluginSelector(true);
|
||||||
|
}
|
||||||
|
}}
|
||||||
text={Locale.Plugin.Name}
|
text={Locale.Plugin.Name}
|
||||||
icon={<PluginIcon />}
|
icon={<PluginIcon />}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
{showPluginSelector && (
|
{showPluginSelector && (
|
||||||
<Selector
|
<Selector
|
||||||
multiple
|
multiple
|
||||||
defaultSelectedValue={chatStore.currentSession().mask?.plugin}
|
defaultSelectedValue={chatStore.currentSession().mask?.plugin}
|
||||||
items={[
|
items={pluginStore.getAll().map((item) => ({
|
||||||
{
|
title: `${item?.title}@${item?.version}`,
|
||||||
title: Locale.Plugin.Artifacts,
|
value: item?.id,
|
||||||
value: Plugin.Artifacts,
|
}))}
|
||||||
},
|
|
||||||
]}
|
|
||||||
onClose={() => setShowPluginSelector(false)}
|
onClose={() => setShowPluginSelector(false)}
|
||||||
onSelection={(s) => {
|
onSelection={(s) => {
|
||||||
const plugin = s[0];
|
|
||||||
chatStore.updateCurrentSession((session) => {
|
chatStore.updateCurrentSession((session) => {
|
||||||
session.mask.plugin = s;
|
session.mask.plugin = s as string[];
|
||||||
});
|
});
|
||||||
if (plugin) {
|
|
||||||
showToast(plugin);
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{!isMobileScreen && (
|
||||||
|
<ChatAction
|
||||||
|
onClick={() => props.setShowShortcutKeyModal(true)}
|
||||||
|
text={Locale.Chat.ShortcutKey.Title}
|
||||||
|
icon={<ShortcutkeyIcon />}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -760,6 +854,67 @@ export function DeleteImageButton(props: { deleteImage: () => void }) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function ShortcutKeyModal(props: { onClose: () => void }) {
|
||||||
|
const isMac = navigator.platform.toUpperCase().indexOf("MAC") >= 0;
|
||||||
|
const shortcuts = [
|
||||||
|
{
|
||||||
|
title: Locale.Chat.ShortcutKey.newChat,
|
||||||
|
keys: isMac ? ["⌘", "Shift", "O"] : ["Ctrl", "Shift", "O"],
|
||||||
|
},
|
||||||
|
{ title: Locale.Chat.ShortcutKey.focusInput, keys: ["Shift", "Esc"] },
|
||||||
|
{
|
||||||
|
title: Locale.Chat.ShortcutKey.copyLastCode,
|
||||||
|
keys: isMac ? ["⌘", "Shift", ";"] : ["Ctrl", "Shift", ";"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: Locale.Chat.ShortcutKey.copyLastMessage,
|
||||||
|
keys: isMac ? ["⌘", "Shift", "C"] : ["Ctrl", "Shift", "C"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: Locale.Chat.ShortcutKey.showShortcutKey,
|
||||||
|
keys: isMac ? ["⌘", "/"] : ["Ctrl", "/"],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
return (
|
||||||
|
<div className="modal-mask">
|
||||||
|
<Modal
|
||||||
|
title={Locale.Chat.ShortcutKey.Title}
|
||||||
|
onClose={props.onClose}
|
||||||
|
actions={[
|
||||||
|
<IconButton
|
||||||
|
type="primary"
|
||||||
|
text={Locale.UI.Confirm}
|
||||||
|
icon={<ConfirmIcon />}
|
||||||
|
key="ok"
|
||||||
|
onClick={() => {
|
||||||
|
props.onClose();
|
||||||
|
}}
|
||||||
|
/>,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<div className={styles["shortcut-key-container"]}>
|
||||||
|
<div className={styles["shortcut-key-grid"]}>
|
||||||
|
{shortcuts.map((shortcut, index) => (
|
||||||
|
<div key={index} className={styles["shortcut-key-item"]}>
|
||||||
|
<div className={styles["shortcut-key-title"]}>
|
||||||
|
{shortcut.title}
|
||||||
|
</div>
|
||||||
|
<div className={styles["shortcut-key-keys"]}>
|
||||||
|
{shortcut.keys.map((key, i) => (
|
||||||
|
<div key={i} className={styles["shortcut-key"]}>
|
||||||
|
<span>{key}</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function _Chat() {
|
function _Chat() {
|
||||||
type RenderMessage = ChatMessage & { preview?: boolean };
|
type RenderMessage = ChatMessage & { preview?: boolean };
|
||||||
|
|
||||||
|
@ -835,6 +990,7 @@ function _Chat() {
|
||||||
chatStore.updateCurrentSession(
|
chatStore.updateCurrentSession(
|
||||||
(session) => (session.clearContextIndex = session.messages.length),
|
(session) => (session.clearContextIndex = session.messages.length),
|
||||||
),
|
),
|
||||||
|
fork: () => chatStore.forkSession(),
|
||||||
del: () => chatStore.deleteSession(chatStore.currentSessionIndex),
|
del: () => chatStore.deleteSession(chatStore.currentSessionIndex),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -872,7 +1028,7 @@ function _Chat() {
|
||||||
.onUserInput(userInput, attachImages)
|
.onUserInput(userInput, attachImages)
|
||||||
.then(() => setIsLoading(false));
|
.then(() => setIsLoading(false));
|
||||||
setAttachImages([]);
|
setAttachImages([]);
|
||||||
localStorage.setItem(LAST_INPUT_KEY, userInput);
|
chatStore.setLastInput(userInput);
|
||||||
setUserInput("");
|
setUserInput("");
|
||||||
setPromptHints([]);
|
setPromptHints([]);
|
||||||
if (!isMobileScreen) inputRef.current?.focus();
|
if (!isMobileScreen) inputRef.current?.focus();
|
||||||
|
@ -938,7 +1094,7 @@ function _Chat() {
|
||||||
userInput.length <= 0 &&
|
userInput.length <= 0 &&
|
||||||
!(e.metaKey || e.altKey || e.ctrlKey)
|
!(e.metaKey || e.altKey || e.ctrlKey)
|
||||||
) {
|
) {
|
||||||
setUserInput(localStorage.getItem(LAST_INPUT_KEY) ?? "");
|
setUserInput(chatStore.lastInput ?? "");
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1038,10 +1194,55 @@ function _Chat() {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const accessStore = useAccessStore();
|
||||||
|
const [speechStatus, setSpeechStatus] = useState(false);
|
||||||
|
const [speechLoading, setSpeechLoading] = useState(false);
|
||||||
|
async function openaiSpeech(text: string) {
|
||||||
|
if (speechStatus) {
|
||||||
|
ttsPlayer.stop();
|
||||||
|
setSpeechStatus(false);
|
||||||
|
} else {
|
||||||
|
var api: ClientApi;
|
||||||
|
api = new ClientApi(ModelProvider.GPT);
|
||||||
|
const config = useAppConfig.getState();
|
||||||
|
setSpeechLoading(true);
|
||||||
|
ttsPlayer.init();
|
||||||
|
let audioBuffer: ArrayBuffer;
|
||||||
|
const { markdownToTxt } = require("markdown-to-txt");
|
||||||
|
const textContent = markdownToTxt(text);
|
||||||
|
if (config.ttsConfig.engine !== DEFAULT_TTS_ENGINE) {
|
||||||
|
const edgeVoiceName = accessStore.edgeVoiceName();
|
||||||
|
const tts = new MsEdgeTTS();
|
||||||
|
await tts.setMetadata(
|
||||||
|
edgeVoiceName,
|
||||||
|
OUTPUT_FORMAT.AUDIO_24KHZ_96KBITRATE_MONO_MP3,
|
||||||
|
);
|
||||||
|
audioBuffer = await tts.toArrayBuffer(textContent);
|
||||||
|
} else {
|
||||||
|
audioBuffer = await api.llm.speech({
|
||||||
|
model: config.ttsConfig.model,
|
||||||
|
input: textContent,
|
||||||
|
voice: config.ttsConfig.voice,
|
||||||
|
speed: config.ttsConfig.speed,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setSpeechStatus(true);
|
||||||
|
ttsPlayer
|
||||||
|
.play(audioBuffer, () => {
|
||||||
|
setSpeechStatus(false);
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
console.error("[OpenAI Speech]", e);
|
||||||
|
showToast(prettyObject(e));
|
||||||
|
setSpeechStatus(false);
|
||||||
|
})
|
||||||
|
.finally(() => setSpeechLoading(false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const context: RenderMessage[] = useMemo(() => {
|
const context: RenderMessage[] = useMemo(() => {
|
||||||
return session.mask.hideContext ? [] : session.mask.context.slice();
|
return session.mask.hideContext ? [] : session.mask.context.slice();
|
||||||
}, [session.mask.context, session.mask.hideContext]);
|
}, [session.mask.context, session.mask.hideContext]);
|
||||||
const accessStore = useAccessStore();
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
context.length === 0 &&
|
context.length === 0 &&
|
||||||
|
@ -1304,6 +1505,70 @@ function _Chat() {
|
||||||
setAttachImages(images);
|
setAttachImages(images);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 快捷键 shortcut keys
|
||||||
|
const [showShortcutKeyModal, setShowShortcutKeyModal] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleKeyDown = (event: any) => {
|
||||||
|
// 打开新聊天 command + shift + o
|
||||||
|
if (
|
||||||
|
(event.metaKey || event.ctrlKey) &&
|
||||||
|
event.shiftKey &&
|
||||||
|
event.key.toLowerCase() === "o"
|
||||||
|
) {
|
||||||
|
event.preventDefault();
|
||||||
|
setTimeout(() => {
|
||||||
|
chatStore.newSession();
|
||||||
|
navigate(Path.Chat);
|
||||||
|
}, 10);
|
||||||
|
}
|
||||||
|
// 聚焦聊天输入 shift + esc
|
||||||
|
else if (event.shiftKey && event.key.toLowerCase() === "escape") {
|
||||||
|
event.preventDefault();
|
||||||
|
inputRef.current?.focus();
|
||||||
|
}
|
||||||
|
// 复制最后一个代码块 command + shift + ;
|
||||||
|
else if (
|
||||||
|
(event.metaKey || event.ctrlKey) &&
|
||||||
|
event.shiftKey &&
|
||||||
|
event.code === "Semicolon"
|
||||||
|
) {
|
||||||
|
event.preventDefault();
|
||||||
|
const copyCodeButton =
|
||||||
|
document.querySelectorAll<HTMLElement>(".copy-code-button");
|
||||||
|
if (copyCodeButton.length > 0) {
|
||||||
|
copyCodeButton[copyCodeButton.length - 1].click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 复制最后一个回复 command + shift + c
|
||||||
|
else if (
|
||||||
|
(event.metaKey || event.ctrlKey) &&
|
||||||
|
event.shiftKey &&
|
||||||
|
event.key.toLowerCase() === "c"
|
||||||
|
) {
|
||||||
|
event.preventDefault();
|
||||||
|
const lastNonUserMessage = messages
|
||||||
|
.filter((message) => message.role !== "user")
|
||||||
|
.pop();
|
||||||
|
if (lastNonUserMessage) {
|
||||||
|
const lastMessageContent = getMessageTextContent(lastNonUserMessage);
|
||||||
|
copyToClipboard(lastMessageContent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 展示快捷键 command + /
|
||||||
|
else if ((event.metaKey || event.ctrlKey) && event.key === "/") {
|
||||||
|
event.preventDefault();
|
||||||
|
setShowShortcutKeyModal(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("keydown", handleKeyDown);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("keydown", handleKeyDown);
|
||||||
|
};
|
||||||
|
}, [messages, chatStore, navigate]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.chat} key={session.id}>
|
<div className={styles.chat} key={session.id}>
|
||||||
<div className="window-header" data-tauri-drag-region>
|
<div className="window-header" data-tauri-drag-region>
|
||||||
|
@ -1332,11 +1597,24 @@ function _Chat() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="window-actions">
|
<div className="window-actions">
|
||||||
|
<div className="window-action-button">
|
||||||
|
<IconButton
|
||||||
|
icon={<ReloadIcon />}
|
||||||
|
bordered
|
||||||
|
title={Locale.Chat.Actions.RefreshTitle}
|
||||||
|
onClick={() => {
|
||||||
|
showToast(Locale.Chat.Actions.RefreshToast);
|
||||||
|
chatStore.summarizeSession(true);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
{!isMobileScreen && (
|
{!isMobileScreen && (
|
||||||
<div className="window-action-button">
|
<div className="window-action-button">
|
||||||
<IconButton
|
<IconButton
|
||||||
icon={<RenameIcon />}
|
icon={<RenameIcon />}
|
||||||
bordered
|
bordered
|
||||||
|
title={Locale.Chat.EditMessage.Title}
|
||||||
|
aria={Locale.Chat.EditMessage.Title}
|
||||||
onClick={() => setIsEditingMessage(true)}
|
onClick={() => setIsEditingMessage(true)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1356,6 +1634,8 @@ function _Chat() {
|
||||||
<IconButton
|
<IconButton
|
||||||
icon={config.tightBorder ? <MinIcon /> : <MaxIcon />}
|
icon={config.tightBorder ? <MinIcon /> : <MaxIcon />}
|
||||||
bordered
|
bordered
|
||||||
|
title={Locale.Chat.Actions.FullScreen}
|
||||||
|
aria={Locale.Chat.Actions.FullScreen}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
config.update(
|
config.update(
|
||||||
(config) => (config.tightBorder = !config.tightBorder),
|
(config) => (config.tightBorder = !config.tightBorder),
|
||||||
|
@ -1407,6 +1687,7 @@ function _Chat() {
|
||||||
<div className={styles["chat-message-edit"]}>
|
<div className={styles["chat-message-edit"]}>
|
||||||
<IconButton
|
<IconButton
|
||||||
icon={<EditIcon />}
|
icon={<EditIcon />}
|
||||||
|
aria={Locale.Chat.Actions.Edit}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
const newMessage = await showPrompt(
|
const newMessage = await showPrompt(
|
||||||
Locale.Chat.Actions.Edit,
|
Locale.Chat.Actions.Edit,
|
||||||
|
@ -1455,6 +1736,11 @@ function _Chat() {
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
{!isUser && (
|
||||||
|
<div className={styles["chat-model-name"]}>
|
||||||
|
{message.model}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{showActions && (
|
{showActions && (
|
||||||
<div className={styles["chat-message-actions"]}>
|
<div className={styles["chat-message-actions"]}>
|
||||||
|
@ -1493,26 +1779,66 @@ function _Chat() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
{config.ttsConfig.enable && (
|
||||||
|
<ChatAction
|
||||||
|
text={
|
||||||
|
speechStatus
|
||||||
|
? Locale.Chat.Actions.StopSpeech
|
||||||
|
: Locale.Chat.Actions.Speech
|
||||||
|
}
|
||||||
|
icon={
|
||||||
|
speechStatus ? (
|
||||||
|
<SpeakStopIcon />
|
||||||
|
) : (
|
||||||
|
<SpeakIcon />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
onClick={() =>
|
||||||
|
openaiSpeech(getMessageTextContent(message))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{showTyping && (
|
{message?.tools?.length == 0 && showTyping && (
|
||||||
<div className={styles["chat-message-status"]}>
|
<div className={styles["chat-message-status"]}>
|
||||||
{Locale.Chat.Typing}
|
{Locale.Chat.Typing}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{/*@ts-ignore*/}
|
||||||
|
{message?.tools?.length > 0 && (
|
||||||
|
<div className={styles["chat-message-tools"]}>
|
||||||
|
{message?.tools?.map((tool) => (
|
||||||
|
<div
|
||||||
|
key={tool.id}
|
||||||
|
className={styles["chat-message-tool"]}
|
||||||
|
>
|
||||||
|
{tool.isError === false ? (
|
||||||
|
<ConfirmIcon />
|
||||||
|
) : tool.isError === true ? (
|
||||||
|
<CloseIcon />
|
||||||
|
) : (
|
||||||
|
<LoadingButtonIcon />
|
||||||
|
)}
|
||||||
|
<span>{tool?.function?.name}</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div className={styles["chat-message-item"]}>
|
<div className={styles["chat-message-item"]}>
|
||||||
<Markdown
|
<Markdown
|
||||||
|
key={message.streaming ? "loading" : "done"}
|
||||||
content={getMessageTextContent(message)}
|
content={getMessageTextContent(message)}
|
||||||
loading={
|
loading={
|
||||||
(message.preview || message.streaming) &&
|
(message.preview || message.streaming) &&
|
||||||
message.content.length === 0 &&
|
message.content.length === 0 &&
|
||||||
!isUser
|
!isUser
|
||||||
}
|
}
|
||||||
onContextMenu={(e) => onRightClick(e, message)}
|
// onContextMenu={(e) => onRightClick(e, message)} // hard to use
|
||||||
onDoubleClickCapture={() => {
|
onDoubleClickCapture={() => {
|
||||||
if (!isMobileScreen) return;
|
if (!isMobileScreen) return;
|
||||||
setUserInput(getMessageTextContent(message));
|
setUserInput(getMessageTextContent(message));
|
||||||
|
@ -1589,6 +1915,8 @@ function _Chat() {
|
||||||
setUserInput("/");
|
setUserInput("/");
|
||||||
onSearch("");
|
onSearch("");
|
||||||
}}
|
}}
|
||||||
|
setShowShortcutKeyModal={setShowShortcutKeyModal}
|
||||||
|
setUserInput={setUserInput}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
className={`${styles["chat-input-panel-inner"]} ${
|
className={`${styles["chat-input-panel-inner"]} ${
|
||||||
|
@ -1660,6 +1988,10 @@ function _Chat() {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{showShortcutKeyModal && (
|
||||||
|
<ShortcutKeyModal onClose={() => setShowShortcutKeyModal(false)} />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,8 @@ export function Avatar(props: { model?: ModelType; avatar?: string }) {
|
||||||
if (props.model) {
|
if (props.model) {
|
||||||
return (
|
return (
|
||||||
<div className="no-dark">
|
<div className="no-dark">
|
||||||
{props.model?.startsWith("gpt-4") ? (
|
{props.model?.startsWith("gpt-4") ||
|
||||||
|
props.model?.startsWith("chatgpt-4o") ? (
|
||||||
<BlackBotIcon className="user-avatar" />
|
<BlackBotIcon className="user-avatar" />
|
||||||
) : (
|
) : (
|
||||||
<BotIcon className="user-avatar" />
|
<BotIcon className="user-avatar" />
|
||||||
|
|
|
@ -8,6 +8,7 @@ import { ISSUE_URL } from "../constant";
|
||||||
import Locale from "../locales";
|
import Locale from "../locales";
|
||||||
import { showConfirm } from "./ui-lib";
|
import { showConfirm } from "./ui-lib";
|
||||||
import { useSyncStore } from "../store/sync";
|
import { useSyncStore } from "../store/sync";
|
||||||
|
import { useChatStore } from "../store/chat";
|
||||||
|
|
||||||
interface IErrorBoundaryState {
|
interface IErrorBoundaryState {
|
||||||
hasError: boolean;
|
hasError: boolean;
|
||||||
|
@ -30,8 +31,7 @@ export class ErrorBoundary extends React.Component<any, IErrorBoundaryState> {
|
||||||
try {
|
try {
|
||||||
useSyncStore.getState().export();
|
useSyncStore.getState().export();
|
||||||
} finally {
|
} finally {
|
||||||
localStorage.clear();
|
useChatStore.getState().clearAllData();
|
||||||
location.reload();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* eslint-disable @next/next/no-img-element */
|
/* eslint-disable @next/next/no-img-element */
|
||||||
import { ChatMessage, ModelType, useAppConfig, useChatStore } from "../store";
|
import { ChatMessage, useAppConfig, useChatStore } from "../store";
|
||||||
import Locale from "../locales";
|
import Locale from "../locales";
|
||||||
import styles from "./exporter.module.scss";
|
import styles from "./exporter.module.scss";
|
||||||
import {
|
import {
|
||||||
|
|
|
@ -59,6 +59,17 @@ const MaskPage = dynamic(async () => (await import("./mask")).MaskPage, {
|
||||||
loading: () => <Loading noLogo />,
|
loading: () => <Loading noLogo />,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const PluginPage = dynamic(async () => (await import("./plugin")).PluginPage, {
|
||||||
|
loading: () => <Loading noLogo />,
|
||||||
|
});
|
||||||
|
|
||||||
|
const SearchChat = dynamic(
|
||||||
|
async () => (await import("./search-chat")).SearchChatPage,
|
||||||
|
{
|
||||||
|
loading: () => <Loading noLogo />,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const Sd = dynamic(async () => (await import("./sd")).Sd, {
|
const Sd = dynamic(async () => (await import("./sd")).Sd, {
|
||||||
loading: () => <Loading noLogo />,
|
loading: () => <Loading noLogo />,
|
||||||
});
|
});
|
||||||
|
@ -174,6 +185,8 @@ function Screen() {
|
||||||
<Route path={Path.Home} element={<Chat />} />
|
<Route path={Path.Home} element={<Chat />} />
|
||||||
<Route path={Path.NewChat} element={<NewChat />} />
|
<Route path={Path.NewChat} element={<NewChat />} />
|
||||||
<Route path={Path.Masks} element={<MaskPage />} />
|
<Route path={Path.Masks} element={<MaskPage />} />
|
||||||
|
<Route path={Path.Plugins} element={<PluginPage />} />
|
||||||
|
<Route path={Path.SearchChat} element={<SearchChat />} />
|
||||||
<Route path={Path.Chat} element={<Chat />} />
|
<Route path={Path.Chat} element={<Chat />} />
|
||||||
<Route path={Path.Settings} element={<Settings />} />
|
<Route path={Path.Settings} element={<Settings />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
|
|
|
@ -9,6 +9,7 @@ interface InputRangeProps {
|
||||||
min: string;
|
min: string;
|
||||||
max: string;
|
max: string;
|
||||||
step: string;
|
step: string;
|
||||||
|
aria: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function InputRange({
|
export function InputRange({
|
||||||
|
@ -19,11 +20,13 @@ export function InputRange({
|
||||||
min,
|
min,
|
||||||
max,
|
max,
|
||||||
step,
|
step,
|
||||||
|
aria,
|
||||||
}: InputRangeProps) {
|
}: InputRangeProps) {
|
||||||
return (
|
return (
|
||||||
<div className={styles["input-range"] + ` ${className ?? ""}`}>
|
<div className={styles["input-range"] + ` ${className ?? ""}`}>
|
||||||
{title || value}
|
{title || value}
|
||||||
<input
|
<input
|
||||||
|
aria-label={aria}
|
||||||
type="range"
|
type="range"
|
||||||
title={title}
|
title={title}
|
||||||
value={value}
|
value={value}
|
||||||
|
|
|
@ -8,14 +8,22 @@ import RehypeHighlight from "rehype-highlight";
|
||||||
import { useRef, useState, RefObject, useEffect, useMemo } from "react";
|
import { useRef, useState, RefObject, useEffect, useMemo } from "react";
|
||||||
import { copyToClipboard, useWindowSize } from "../utils";
|
import { copyToClipboard, useWindowSize } from "../utils";
|
||||||
import mermaid from "mermaid";
|
import mermaid from "mermaid";
|
||||||
|
import Locale from "../locales";
|
||||||
import LoadingIcon from "../icons/three-dots.svg";
|
import LoadingIcon from "../icons/three-dots.svg";
|
||||||
|
import ReloadButtonIcon from "../icons/reload.svg";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useDebouncedCallback } from "use-debounce";
|
import { useDebouncedCallback } from "use-debounce";
|
||||||
import { showImageModal, FullScreen } from "./ui-lib";
|
import { showImageModal, FullScreen } from "./ui-lib";
|
||||||
import { ArtifactsShareButton, HTMLPreview } from "./artifacts";
|
import {
|
||||||
import { Plugin } from "../constant";
|
ArtifactsShareButton,
|
||||||
|
HTMLPreview,
|
||||||
|
HTMLPreviewHander,
|
||||||
|
} from "./artifacts";
|
||||||
import { useChatStore } from "../store";
|
import { useChatStore } from "../store";
|
||||||
|
import { IconButton } from "./button";
|
||||||
|
|
||||||
|
import { useAppConfig } from "../store/config";
|
||||||
|
|
||||||
export function Mermaid(props: { code: string }) {
|
export function Mermaid(props: { code: string }) {
|
||||||
const ref = useRef<HTMLDivElement>(null);
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
const [hasError, setHasError] = useState(false);
|
const [hasError, setHasError] = useState(false);
|
||||||
|
@ -64,13 +72,12 @@ export function Mermaid(props: { code: string }) {
|
||||||
|
|
||||||
export function PreCode(props: { children: any }) {
|
export function PreCode(props: { children: any }) {
|
||||||
const ref = useRef<HTMLPreElement>(null);
|
const ref = useRef<HTMLPreElement>(null);
|
||||||
const refText = ref.current?.innerText;
|
const previewRef = useRef<HTMLPreviewHander>(null);
|
||||||
const [mermaidCode, setMermaidCode] = useState("");
|
const [mermaidCode, setMermaidCode] = useState("");
|
||||||
const [htmlCode, setHtmlCode] = useState("");
|
const [htmlCode, setHtmlCode] = useState("");
|
||||||
const { height } = useWindowSize();
|
const { height } = useWindowSize();
|
||||||
const chatStore = useChatStore();
|
const chatStore = useChatStore();
|
||||||
const session = chatStore.currentSession();
|
const session = chatStore.currentSession();
|
||||||
const plugins = session.mask?.plugin;
|
|
||||||
|
|
||||||
const renderArtifacts = useDebouncedCallback(() => {
|
const renderArtifacts = useDebouncedCallback(() => {
|
||||||
if (!ref.current) return;
|
if (!ref.current) return;
|
||||||
|
@ -79,6 +86,7 @@ export function PreCode(props: { children: any }) {
|
||||||
setMermaidCode((mermaidDom as HTMLElement).innerText);
|
setMermaidCode((mermaidDom as HTMLElement).innerText);
|
||||||
}
|
}
|
||||||
const htmlDom = ref.current.querySelector("code.language-html");
|
const htmlDom = ref.current.querySelector("code.language-html");
|
||||||
|
const refText = ref.current.querySelector("code")?.innerText;
|
||||||
if (htmlDom) {
|
if (htmlDom) {
|
||||||
setHtmlCode((htmlDom as HTMLElement).innerText);
|
setHtmlCode((htmlDom as HTMLElement).innerText);
|
||||||
} else if (refText?.startsWith("<!DOCTYPE")) {
|
} else if (refText?.startsWith("<!DOCTYPE")) {
|
||||||
|
@ -86,15 +94,9 @@ export function PreCode(props: { children: any }) {
|
||||||
}
|
}
|
||||||
}, 600);
|
}, 600);
|
||||||
|
|
||||||
useEffect(() => {
|
const config = useAppConfig();
|
||||||
setTimeout(renderArtifacts, 1);
|
const enableArtifacts =
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
session.mask?.enableArtifacts !== false && config.enableArtifacts;
|
||||||
}, [refText]);
|
|
||||||
|
|
||||||
const enableArtifacts = useMemo(
|
|
||||||
() => plugins?.includes(Plugin.Artifacts),
|
|
||||||
[plugins],
|
|
||||||
);
|
|
||||||
|
|
||||||
//Wrap the paragraph for plain-text
|
//Wrap the paragraph for plain-text
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -119,6 +121,7 @@ export function PreCode(props: { children: any }) {
|
||||||
codeElement.style.whiteSpace = "pre-wrap";
|
codeElement.style.whiteSpace = "pre-wrap";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
setTimeout(renderArtifacts, 1);
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
@ -129,8 +132,9 @@ export function PreCode(props: { children: any }) {
|
||||||
className="copy-code-button"
|
className="copy-code-button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (ref.current) {
|
if (ref.current) {
|
||||||
const code = ref.current.innerText;
|
copyToClipboard(
|
||||||
copyToClipboard(code);
|
ref.current.querySelector("code")?.innerText ?? "",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
></span>
|
></span>
|
||||||
|
@ -145,7 +149,15 @@ export function PreCode(props: { children: any }) {
|
||||||
style={{ position: "absolute", right: 20, top: 10 }}
|
style={{ position: "absolute", right: 20, top: 10 }}
|
||||||
getCode={() => htmlCode}
|
getCode={() => htmlCode}
|
||||||
/>
|
/>
|
||||||
|
<IconButton
|
||||||
|
style={{ position: "absolute", right: 120, top: 10 }}
|
||||||
|
bordered
|
||||||
|
icon={<ReloadButtonIcon />}
|
||||||
|
shadow
|
||||||
|
onClick={() => previewRef.current?.reload()}
|
||||||
|
/>
|
||||||
<HTMLPreview
|
<HTMLPreview
|
||||||
|
ref={previewRef}
|
||||||
code={htmlCode}
|
code={htmlCode}
|
||||||
autoHeight={!document.fullscreenElement}
|
autoHeight={!document.fullscreenElement}
|
||||||
height={!document.fullscreenElement ? 600 : height}
|
height={!document.fullscreenElement ? 600 : height}
|
||||||
|
@ -156,21 +168,43 @@ export function PreCode(props: { children: any }) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function escapeDollarNumber(text: string) {
|
function CustomCode(props: { children: any; className?: string }) {
|
||||||
let escapedText = "";
|
const ref = useRef<HTMLPreElement>(null);
|
||||||
|
const [collapsed, setCollapsed] = useState(true);
|
||||||
|
const [showToggle, setShowToggle] = useState(false);
|
||||||
|
|
||||||
for (let i = 0; i < text.length; i += 1) {
|
useEffect(() => {
|
||||||
let char = text[i];
|
if (ref.current) {
|
||||||
const nextChar = text[i + 1] || " ";
|
const codeHeight = ref.current.scrollHeight;
|
||||||
|
setShowToggle(codeHeight > 400);
|
||||||
if (char === "$" && nextChar >= "0" && nextChar <= "9") {
|
ref.current.scrollTop = ref.current.scrollHeight;
|
||||||
char = "\\$";
|
|
||||||
}
|
}
|
||||||
|
}, [props.children]);
|
||||||
|
|
||||||
escapedText += char;
|
const toggleCollapsed = () => {
|
||||||
}
|
setCollapsed((collapsed) => !collapsed);
|
||||||
|
};
|
||||||
return escapedText;
|
return (
|
||||||
|
<>
|
||||||
|
<code
|
||||||
|
className={props?.className}
|
||||||
|
ref={ref}
|
||||||
|
style={{
|
||||||
|
maxHeight: collapsed ? "400px" : "none",
|
||||||
|
overflowY: "hidden",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{props.children}
|
||||||
|
</code>
|
||||||
|
{showToggle && collapsed && (
|
||||||
|
<div
|
||||||
|
className={`show-hide-button ${collapsed ? "collapsed" : "expanded"}`}
|
||||||
|
>
|
||||||
|
<button onClick={toggleCollapsed}>{Locale.NewChat.More}</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function escapeBrackets(text: string) {
|
function escapeBrackets(text: string) {
|
||||||
|
@ -191,9 +225,26 @@ function escapeBrackets(text: string) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function tryWrapHtmlCode(text: string) {
|
||||||
|
// try add wrap html code (fixed: html codeblock include 2 newline)
|
||||||
|
return text
|
||||||
|
.replace(
|
||||||
|
/([`]*?)(\w*?)([\n\r]*?)(<!DOCTYPE html>)/g,
|
||||||
|
(match, quoteStart, lang, newLine, doctype) => {
|
||||||
|
return !quoteStart ? "\n```html\n" + doctype : match;
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.replace(
|
||||||
|
/(<\/body>)([\r\n\s]*?)(<\/html>)([\n\r]*)([`]*)([\n\r]*?)/g,
|
||||||
|
(match, bodyEnd, space, htmlEnd, newLine, quoteEnd) => {
|
||||||
|
return !quoteEnd ? bodyEnd + space + htmlEnd + "\n```\n" : match;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function _MarkDownContent(props: { content: string }) {
|
function _MarkDownContent(props: { content: string }) {
|
||||||
const escapedContent = useMemo(() => {
|
const escapedContent = useMemo(() => {
|
||||||
return escapeBrackets(escapeDollarNumber(props.content));
|
return tryWrapHtmlCode(escapeBrackets(props.content));
|
||||||
}, [props.content]);
|
}, [props.content]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -211,9 +262,24 @@ function _MarkDownContent(props: { content: string }) {
|
||||||
]}
|
]}
|
||||||
components={{
|
components={{
|
||||||
pre: PreCode,
|
pre: PreCode,
|
||||||
|
code: CustomCode,
|
||||||
p: (pProps) => <p {...pProps} dir="auto" />,
|
p: (pProps) => <p {...pProps} dir="auto" />,
|
||||||
a: (aProps) => {
|
a: (aProps) => {
|
||||||
const href = aProps.href || "";
|
const href = aProps.href || "";
|
||||||
|
if (/\.(aac|mp3|opus|wav)$/.test(href)) {
|
||||||
|
return (
|
||||||
|
<figure>
|
||||||
|
<audio controls src={href}></audio>
|
||||||
|
</figure>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (/\.(3gp|3g2|webm|ogv|mpeg|mp4|avi)$/.test(href)) {
|
||||||
|
return (
|
||||||
|
<video controls width="99.9%">
|
||||||
|
<source src={href} />
|
||||||
|
</video>
|
||||||
|
);
|
||||||
|
}
|
||||||
const isInternal = /^\/#/i.test(href);
|
const isInternal = /^\/#/i.test(href);
|
||||||
const target = isInternal ? "_self" : aProps.target ?? "_blank";
|
const target = isInternal ? "_self" : aProps.target ?? "_blank";
|
||||||
return <a {...aProps} target={target} />;
|
return <a {...aProps} target={target} />;
|
||||||
|
|
|
@ -37,7 +37,7 @@ import Locale, { AllLangs, ALL_LANG_OPTIONS, Lang } from "../locales";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
import chatStyle from "./chat.module.scss";
|
import chatStyle from "./chat.module.scss";
|
||||||
import { useEffect, useState } from "react";
|
import { useState } from "react";
|
||||||
import {
|
import {
|
||||||
copyToClipboard,
|
copyToClipboard,
|
||||||
downloadAs,
|
downloadAs,
|
||||||
|
@ -48,7 +48,6 @@ import { Updater } from "../typing";
|
||||||
import { ModelConfigList } from "./model-config";
|
import { ModelConfigList } from "./model-config";
|
||||||
import { FileName, Path } from "../constant";
|
import { FileName, Path } from "../constant";
|
||||||
import { BUILTIN_MASK_STORE } from "../masks";
|
import { BUILTIN_MASK_STORE } from "../masks";
|
||||||
import { nanoid } from "nanoid";
|
|
||||||
import {
|
import {
|
||||||
DragDropContext,
|
DragDropContext,
|
||||||
Droppable,
|
Droppable,
|
||||||
|
@ -127,6 +126,8 @@ export function MaskConfig(props: {
|
||||||
onClose={() => setShowPicker(false)}
|
onClose={() => setShowPicker(false)}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
tabIndex={0}
|
||||||
|
aria-label={Locale.Mask.Config.Avatar}
|
||||||
onClick={() => setShowPicker(true)}
|
onClick={() => setShowPicker(true)}
|
||||||
style={{ cursor: "pointer" }}
|
style={{ cursor: "pointer" }}
|
||||||
>
|
>
|
||||||
|
@ -139,6 +140,7 @@ export function MaskConfig(props: {
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<ListItem title={Locale.Mask.Config.Name}>
|
<ListItem title={Locale.Mask.Config.Name}>
|
||||||
<input
|
<input
|
||||||
|
aria-label={Locale.Mask.Config.Name}
|
||||||
type="text"
|
type="text"
|
||||||
value={props.mask.name}
|
value={props.mask.name}
|
||||||
onInput={(e) =>
|
onInput={(e) =>
|
||||||
|
@ -153,6 +155,7 @@ export function MaskConfig(props: {
|
||||||
subTitle={Locale.Mask.Config.HideContext.SubTitle}
|
subTitle={Locale.Mask.Config.HideContext.SubTitle}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
aria-label={Locale.Mask.Config.HideContext.Title}
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={props.mask.hideContext}
|
checked={props.mask.hideContext}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
|
@ -163,12 +166,31 @@ export function MaskConfig(props: {
|
||||||
></input>
|
></input>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
|
||||||
|
{globalConfig.enableArtifacts && (
|
||||||
|
<ListItem
|
||||||
|
title={Locale.Mask.Config.Artifacts.Title}
|
||||||
|
subTitle={Locale.Mask.Config.Artifacts.SubTitle}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
aria-label={Locale.Mask.Config.Artifacts.Title}
|
||||||
|
type="checkbox"
|
||||||
|
checked={props.mask.enableArtifacts !== false}
|
||||||
|
onChange={(e) => {
|
||||||
|
props.updateMask((mask) => {
|
||||||
|
mask.enableArtifacts = e.currentTarget.checked;
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
></input>
|
||||||
|
</ListItem>
|
||||||
|
)}
|
||||||
|
|
||||||
{!props.shouldSyncFromGlobal ? (
|
{!props.shouldSyncFromGlobal ? (
|
||||||
<ListItem
|
<ListItem
|
||||||
title={Locale.Mask.Config.Share.Title}
|
title={Locale.Mask.Config.Share.Title}
|
||||||
subTitle={Locale.Mask.Config.Share.SubTitle}
|
subTitle={Locale.Mask.Config.Share.SubTitle}
|
||||||
>
|
>
|
||||||
<IconButton
|
<IconButton
|
||||||
|
aria={Locale.Mask.Config.Share.Title}
|
||||||
icon={<CopyIcon />}
|
icon={<CopyIcon />}
|
||||||
text={Locale.Mask.Config.Share.Action}
|
text={Locale.Mask.Config.Share.Action}
|
||||||
onClick={copyMaskLink}
|
onClick={copyMaskLink}
|
||||||
|
@ -182,6 +204,7 @@ export function MaskConfig(props: {
|
||||||
subTitle={Locale.Mask.Config.Sync.SubTitle}
|
subTitle={Locale.Mask.Config.Sync.SubTitle}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
aria-label={Locale.Mask.Config.Sync.Title}
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={props.mask.syncGlobalConfig}
|
checked={props.mask.syncGlobalConfig}
|
||||||
onChange={async (e) => {
|
onChange={async (e) => {
|
||||||
|
@ -404,16 +427,7 @@ export function MaskPage() {
|
||||||
const maskStore = useMaskStore();
|
const maskStore = useMaskStore();
|
||||||
const chatStore = useChatStore();
|
const chatStore = useChatStore();
|
||||||
|
|
||||||
const [filterLang, setFilterLang] = useState<Lang | undefined>(
|
const filterLang = maskStore.language;
|
||||||
() => localStorage.getItem("Mask-language") as Lang | undefined,
|
|
||||||
);
|
|
||||||
useEffect(() => {
|
|
||||||
if (filterLang) {
|
|
||||||
localStorage.setItem("Mask-language", filterLang);
|
|
||||||
} else {
|
|
||||||
localStorage.removeItem("Mask-language");
|
|
||||||
}
|
|
||||||
}, [filterLang]);
|
|
||||||
|
|
||||||
const allMasks = maskStore
|
const allMasks = maskStore
|
||||||
.getAll()
|
.getAll()
|
||||||
|
@ -520,9 +534,9 @@ export function MaskPage() {
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const value = e.currentTarget.value;
|
const value = e.currentTarget.value;
|
||||||
if (value === Locale.Settings.Lang.All) {
|
if (value === Locale.Settings.Lang.All) {
|
||||||
setFilterLang(undefined);
|
maskStore.setLanguage(undefined);
|
||||||
} else {
|
} else {
|
||||||
setFilterLang(value as Lang);
|
maskStore.setLanguage(value as Lang);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
.select-compress-model {
|
||||||
|
width: 60%;
|
||||||
|
select {
|
||||||
|
max-width: 100%;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,19 +5,28 @@ import Locale from "../locales";
|
||||||
import { InputRange } from "./input-range";
|
import { InputRange } from "./input-range";
|
||||||
import { ListItem, Select } from "./ui-lib";
|
import { ListItem, Select } from "./ui-lib";
|
||||||
import { useAllModels } from "../utils/hooks";
|
import { useAllModels } from "../utils/hooks";
|
||||||
|
import { groupBy } from "lodash-es";
|
||||||
|
import styles from "./model-config.module.scss";
|
||||||
|
|
||||||
export function ModelConfigList(props: {
|
export function ModelConfigList(props: {
|
||||||
modelConfig: ModelConfig;
|
modelConfig: ModelConfig;
|
||||||
updateConfig: (updater: (config: ModelConfig) => void) => void;
|
updateConfig: (updater: (config: ModelConfig) => void) => void;
|
||||||
}) {
|
}) {
|
||||||
const allModels = useAllModels();
|
const allModels = useAllModels();
|
||||||
|
const groupModels = groupBy(
|
||||||
|
allModels.filter((v) => v.available),
|
||||||
|
"provider.providerName",
|
||||||
|
);
|
||||||
const value = `${props.modelConfig.model}@${props.modelConfig?.providerName}`;
|
const value = `${props.modelConfig.model}@${props.modelConfig?.providerName}`;
|
||||||
|
const compressModelValue = `${props.modelConfig.compressModel}@${props.modelConfig?.compressProviderName}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ListItem title={Locale.Settings.Model}>
|
<ListItem title={Locale.Settings.Model}>
|
||||||
<Select
|
<Select
|
||||||
|
aria-label={Locale.Settings.Model}
|
||||||
value={value}
|
value={value}
|
||||||
|
align="left"
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const [model, providerName] = e.currentTarget.value.split("@");
|
const [model, providerName] = e.currentTarget.value.split("@");
|
||||||
props.updateConfig((config) => {
|
props.updateConfig((config) => {
|
||||||
|
@ -26,13 +35,15 @@ export function ModelConfigList(props: {
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{allModels
|
{Object.keys(groupModels).map((providerName, index) => (
|
||||||
.filter((v) => v.available)
|
<optgroup label={providerName} key={index}>
|
||||||
.map((v, i) => (
|
{groupModels[providerName].map((v, i) => (
|
||||||
<option value={`${v.name}@${v.provider?.providerName}`} key={i}>
|
<option value={`${v.name}@${v.provider?.providerName}`} key={i}>
|
||||||
{v.displayName}({v.provider?.providerName})
|
{v.displayName}
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
|
</optgroup>
|
||||||
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<ListItem
|
<ListItem
|
||||||
|
@ -40,6 +51,7 @@ export function ModelConfigList(props: {
|
||||||
subTitle={Locale.Settings.Temperature.SubTitle}
|
subTitle={Locale.Settings.Temperature.SubTitle}
|
||||||
>
|
>
|
||||||
<InputRange
|
<InputRange
|
||||||
|
aria={Locale.Settings.Temperature.Title}
|
||||||
value={props.modelConfig.temperature?.toFixed(1)}
|
value={props.modelConfig.temperature?.toFixed(1)}
|
||||||
min="0"
|
min="0"
|
||||||
max="1" // lets limit it to 0-1
|
max="1" // lets limit it to 0-1
|
||||||
|
@ -59,6 +71,7 @@ export function ModelConfigList(props: {
|
||||||
subTitle={Locale.Settings.TopP.SubTitle}
|
subTitle={Locale.Settings.TopP.SubTitle}
|
||||||
>
|
>
|
||||||
<InputRange
|
<InputRange
|
||||||
|
aria={Locale.Settings.TopP.Title}
|
||||||
value={(props.modelConfig.top_p ?? 1).toFixed(1)}
|
value={(props.modelConfig.top_p ?? 1).toFixed(1)}
|
||||||
min="0"
|
min="0"
|
||||||
max="1"
|
max="1"
|
||||||
|
@ -78,6 +91,7 @@ export function ModelConfigList(props: {
|
||||||
subTitle={Locale.Settings.MaxTokens.SubTitle}
|
subTitle={Locale.Settings.MaxTokens.SubTitle}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
aria-label={Locale.Settings.MaxTokens.Title}
|
||||||
type="number"
|
type="number"
|
||||||
min={1024}
|
min={1024}
|
||||||
max={512000}
|
max={512000}
|
||||||
|
@ -100,6 +114,7 @@ export function ModelConfigList(props: {
|
||||||
subTitle={Locale.Settings.PresencePenalty.SubTitle}
|
subTitle={Locale.Settings.PresencePenalty.SubTitle}
|
||||||
>
|
>
|
||||||
<InputRange
|
<InputRange
|
||||||
|
aria={Locale.Settings.PresencePenalty.Title}
|
||||||
value={props.modelConfig.presence_penalty?.toFixed(1)}
|
value={props.modelConfig.presence_penalty?.toFixed(1)}
|
||||||
min="-2"
|
min="-2"
|
||||||
max="2"
|
max="2"
|
||||||
|
@ -121,6 +136,7 @@ export function ModelConfigList(props: {
|
||||||
subTitle={Locale.Settings.FrequencyPenalty.SubTitle}
|
subTitle={Locale.Settings.FrequencyPenalty.SubTitle}
|
||||||
>
|
>
|
||||||
<InputRange
|
<InputRange
|
||||||
|
aria={Locale.Settings.FrequencyPenalty.Title}
|
||||||
value={props.modelConfig.frequency_penalty?.toFixed(1)}
|
value={props.modelConfig.frequency_penalty?.toFixed(1)}
|
||||||
min="-2"
|
min="-2"
|
||||||
max="2"
|
max="2"
|
||||||
|
@ -142,6 +158,7 @@ export function ModelConfigList(props: {
|
||||||
subTitle={Locale.Settings.InjectSystemPrompts.SubTitle}
|
subTitle={Locale.Settings.InjectSystemPrompts.SubTitle}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
aria-label={Locale.Settings.InjectSystemPrompts.Title}
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={props.modelConfig.enableInjectSystemPrompts}
|
checked={props.modelConfig.enableInjectSystemPrompts}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
|
@ -159,6 +176,7 @@ export function ModelConfigList(props: {
|
||||||
subTitle={Locale.Settings.InputTemplate.SubTitle}
|
subTitle={Locale.Settings.InputTemplate.SubTitle}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
aria-label={Locale.Settings.InputTemplate.Title}
|
||||||
type="text"
|
type="text"
|
||||||
value={props.modelConfig.template}
|
value={props.modelConfig.template}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
|
@ -175,6 +193,7 @@ export function ModelConfigList(props: {
|
||||||
subTitle={Locale.Settings.HistoryCount.SubTitle}
|
subTitle={Locale.Settings.HistoryCount.SubTitle}
|
||||||
>
|
>
|
||||||
<InputRange
|
<InputRange
|
||||||
|
aria={Locale.Settings.HistoryCount.Title}
|
||||||
title={props.modelConfig.historyMessageCount.toString()}
|
title={props.modelConfig.historyMessageCount.toString()}
|
||||||
value={props.modelConfig.historyMessageCount}
|
value={props.modelConfig.historyMessageCount}
|
||||||
min="0"
|
min="0"
|
||||||
|
@ -193,6 +212,7 @@ export function ModelConfigList(props: {
|
||||||
subTitle={Locale.Settings.CompressThreshold.SubTitle}
|
subTitle={Locale.Settings.CompressThreshold.SubTitle}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
aria-label={Locale.Settings.CompressThreshold.Title}
|
||||||
type="number"
|
type="number"
|
||||||
min={500}
|
min={500}
|
||||||
max={4000}
|
max={4000}
|
||||||
|
@ -208,6 +228,7 @@ export function ModelConfigList(props: {
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<ListItem title={Locale.Memory.Title} subTitle={Locale.Memory.Send}>
|
<ListItem title={Locale.Memory.Title} subTitle={Locale.Memory.Send}>
|
||||||
<input
|
<input
|
||||||
|
aria-label={Locale.Memory.Title}
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={props.modelConfig.sendMemory}
|
checked={props.modelConfig.sendMemory}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
|
@ -217,6 +238,31 @@ export function ModelConfigList(props: {
|
||||||
}
|
}
|
||||||
></input>
|
></input>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
<ListItem
|
||||||
|
title={Locale.Settings.CompressModel.Title}
|
||||||
|
subTitle={Locale.Settings.CompressModel.SubTitle}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
className={styles["select-compress-model"]}
|
||||||
|
aria-label={Locale.Settings.CompressModel.Title}
|
||||||
|
value={compressModelValue}
|
||||||
|
onChange={(e) => {
|
||||||
|
const [model, providerName] = e.currentTarget.value.split("@");
|
||||||
|
props.updateConfig((config) => {
|
||||||
|
config.compressModel = ModalConfigValidator.model(model);
|
||||||
|
config.compressProviderName = providerName as ServiceProvider;
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{allModels
|
||||||
|
.filter((v) => v.available)
|
||||||
|
.map((v, i) => (
|
||||||
|
<option value={`${v.name}@${v.provider?.providerName}`} key={i}>
|
||||||
|
{v.displayName}({v.provider?.providerName})
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</ListItem>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
.plugin-title {
|
||||||
|
font-weight: bolder;
|
||||||
|
font-size: 16px;
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
.plugin-content {
|
||||||
|
font-size: 14px;
|
||||||
|
font-family: inherit;
|
||||||
|
pre code {
|
||||||
|
max-height: 240px;
|
||||||
|
overflow-y: auto;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
min-width: 280px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.plugin-schema {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
input {
|
||||||
|
margin-right: 20px;
|
||||||
|
|
||||||
|
@media screen and (max-width: 600px) {
|
||||||
|
margin-right: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 600px) {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 5px;
|
||||||
|
|
||||||
|
button {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,366 @@
|
||||||
|
import { useDebouncedCallback } from "use-debounce";
|
||||||
|
import OpenAPIClientAxios from "openapi-client-axios";
|
||||||
|
import yaml from "js-yaml";
|
||||||
|
import { PLUGINS_REPO_URL } from "../constant";
|
||||||
|
import { IconButton } from "./button";
|
||||||
|
import { ErrorBoundary } from "./error";
|
||||||
|
|
||||||
|
import styles from "./mask.module.scss";
|
||||||
|
import pluginStyles from "./plugin.module.scss";
|
||||||
|
|
||||||
|
import EditIcon from "../icons/edit.svg";
|
||||||
|
import AddIcon from "../icons/add.svg";
|
||||||
|
import CloseIcon from "../icons/close.svg";
|
||||||
|
import DeleteIcon from "../icons/delete.svg";
|
||||||
|
import ConfirmIcon from "../icons/confirm.svg";
|
||||||
|
import ReloadIcon from "../icons/reload.svg";
|
||||||
|
import GithubIcon from "../icons/github.svg";
|
||||||
|
|
||||||
|
import { Plugin, usePluginStore, FunctionToolService } from "../store/plugin";
|
||||||
|
import {
|
||||||
|
PasswordInput,
|
||||||
|
List,
|
||||||
|
ListItem,
|
||||||
|
Modal,
|
||||||
|
showConfirm,
|
||||||
|
showToast,
|
||||||
|
} from "./ui-lib";
|
||||||
|
import Locale from "../locales";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
export function PluginPage() {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const pluginStore = usePluginStore();
|
||||||
|
|
||||||
|
const allPlugins = pluginStore.getAll();
|
||||||
|
const [searchPlugins, setSearchPlugins] = useState<Plugin[]>([]);
|
||||||
|
const [searchText, setSearchText] = useState("");
|
||||||
|
const plugins = searchText.length > 0 ? searchPlugins : allPlugins;
|
||||||
|
|
||||||
|
// refactored already, now it accurate
|
||||||
|
const onSearch = (text: string) => {
|
||||||
|
setSearchText(text);
|
||||||
|
if (text.length > 0) {
|
||||||
|
const result = allPlugins.filter(
|
||||||
|
(m) => m?.title.toLowerCase().includes(text.toLowerCase()),
|
||||||
|
);
|
||||||
|
setSearchPlugins(result);
|
||||||
|
} else {
|
||||||
|
setSearchPlugins(allPlugins);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const [editingPluginId, setEditingPluginId] = useState<string | undefined>();
|
||||||
|
const editingPlugin = pluginStore.get(editingPluginId);
|
||||||
|
const editingPluginTool = FunctionToolService.get(editingPlugin?.id);
|
||||||
|
const closePluginModal = () => setEditingPluginId(undefined);
|
||||||
|
|
||||||
|
const onChangePlugin = useDebouncedCallback((editingPlugin, e) => {
|
||||||
|
const content = e.target.innerText;
|
||||||
|
try {
|
||||||
|
const api = new OpenAPIClientAxios({
|
||||||
|
definition: yaml.load(content) as any,
|
||||||
|
});
|
||||||
|
api
|
||||||
|
.init()
|
||||||
|
.then(() => {
|
||||||
|
if (content != editingPlugin.content) {
|
||||||
|
pluginStore.updatePlugin(editingPlugin.id, (plugin) => {
|
||||||
|
plugin.content = content;
|
||||||
|
const tool = FunctionToolService.add(plugin, true);
|
||||||
|
plugin.title = tool.api.definition.info.title;
|
||||||
|
plugin.version = tool.api.definition.info.version;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
console.error(e);
|
||||||
|
showToast(Locale.Plugin.EditModal.Error);
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
showToast(Locale.Plugin.EditModal.Error);
|
||||||
|
}
|
||||||
|
}, 100).bind(null, editingPlugin);
|
||||||
|
|
||||||
|
const [loadUrl, setLoadUrl] = useState<string>("");
|
||||||
|
const loadFromUrl = (loadUrl: string) =>
|
||||||
|
fetch(loadUrl)
|
||||||
|
.catch((e) => {
|
||||||
|
const p = new URL(loadUrl);
|
||||||
|
return fetch(`/api/proxy/${p.pathname}?${p.search}`, {
|
||||||
|
headers: {
|
||||||
|
"X-Base-URL": p.origin,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then((res) => res.text())
|
||||||
|
.then((content) => {
|
||||||
|
try {
|
||||||
|
return JSON.stringify(JSON.parse(content), null, " ");
|
||||||
|
} catch (e) {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((content) => {
|
||||||
|
pluginStore.updatePlugin(editingPlugin.id, (plugin) => {
|
||||||
|
plugin.content = content;
|
||||||
|
const tool = FunctionToolService.add(plugin, true);
|
||||||
|
plugin.title = tool.api.definition.info.title;
|
||||||
|
plugin.version = tool.api.definition.info.version;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
showToast(Locale.Plugin.EditModal.Error);
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ErrorBoundary>
|
||||||
|
<div className={styles["mask-page"]}>
|
||||||
|
<div className="window-header">
|
||||||
|
<div className="window-header-title">
|
||||||
|
<div className="window-header-main-title">
|
||||||
|
{Locale.Plugin.Page.Title}
|
||||||
|
</div>
|
||||||
|
<div className="window-header-submai-title">
|
||||||
|
{Locale.Plugin.Page.SubTitle(plugins.length)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="window-actions">
|
||||||
|
<div className="window-action-button">
|
||||||
|
<a
|
||||||
|
href={PLUGINS_REPO_URL}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
<IconButton icon={<GithubIcon />} bordered />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div className="window-action-button">
|
||||||
|
<IconButton
|
||||||
|
icon={<CloseIcon />}
|
||||||
|
bordered
|
||||||
|
onClick={() => navigate(-1)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles["mask-page-body"]}>
|
||||||
|
<div className={styles["mask-filter"]}>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className={styles["search-bar"]}
|
||||||
|
placeholder={Locale.Plugin.Page.Search}
|
||||||
|
autoFocus
|
||||||
|
onInput={(e) => onSearch(e.currentTarget.value)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<IconButton
|
||||||
|
className={styles["mask-create"]}
|
||||||
|
icon={<AddIcon />}
|
||||||
|
text={Locale.Plugin.Page.Create}
|
||||||
|
bordered
|
||||||
|
onClick={() => {
|
||||||
|
const createdPlugin = pluginStore.create();
|
||||||
|
setEditingPluginId(createdPlugin.id);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{plugins.length == 0 && (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
margin: "60px auto",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{Locale.Plugin.Page.Find}
|
||||||
|
<a
|
||||||
|
href={PLUGINS_REPO_URL}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
style={{ marginLeft: 16 }}
|
||||||
|
>
|
||||||
|
<IconButton icon={<GithubIcon />} bordered />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{plugins.map((m) => (
|
||||||
|
<div className={styles["mask-item"]} key={m.id}>
|
||||||
|
<div className={styles["mask-header"]}>
|
||||||
|
<div className={styles["mask-icon"]}></div>
|
||||||
|
<div className={styles["mask-title"]}>
|
||||||
|
<div className={styles["mask-name"]}>
|
||||||
|
{m.title}@<small>{m.version}</small>
|
||||||
|
</div>
|
||||||
|
<div className={styles["mask-info"] + " one-line"}>
|
||||||
|
{Locale.Plugin.Item.Info(
|
||||||
|
FunctionToolService.add(m).length,
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={styles["mask-actions"]}>
|
||||||
|
<IconButton
|
||||||
|
icon={<EditIcon />}
|
||||||
|
text={Locale.Plugin.Item.Edit}
|
||||||
|
onClick={() => setEditingPluginId(m.id)}
|
||||||
|
/>
|
||||||
|
{!m.builtin && (
|
||||||
|
<IconButton
|
||||||
|
icon={<DeleteIcon />}
|
||||||
|
text={Locale.Plugin.Item.Delete}
|
||||||
|
onClick={async () => {
|
||||||
|
if (
|
||||||
|
await showConfirm(Locale.Plugin.Item.DeleteConfirm)
|
||||||
|
) {
|
||||||
|
pluginStore.delete(m.id);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{editingPlugin && (
|
||||||
|
<div className="modal-mask">
|
||||||
|
<Modal
|
||||||
|
title={Locale.Plugin.EditModal.Title(editingPlugin?.builtin)}
|
||||||
|
onClose={closePluginModal}
|
||||||
|
actions={[
|
||||||
|
<IconButton
|
||||||
|
icon={<ConfirmIcon />}
|
||||||
|
text={Locale.UI.Confirm}
|
||||||
|
key="export"
|
||||||
|
bordered
|
||||||
|
onClick={() => setEditingPluginId("")}
|
||||||
|
/>,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<List>
|
||||||
|
<ListItem title={Locale.Plugin.EditModal.Auth}>
|
||||||
|
<select
|
||||||
|
value={editingPlugin?.authType}
|
||||||
|
onChange={(e) => {
|
||||||
|
pluginStore.updatePlugin(editingPlugin.id, (plugin) => {
|
||||||
|
plugin.authType = e.target.value;
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<option value="">{Locale.Plugin.Auth.None}</option>
|
||||||
|
<option value="bearer">{Locale.Plugin.Auth.Bearer}</option>
|
||||||
|
<option value="basic">{Locale.Plugin.Auth.Basic}</option>
|
||||||
|
<option value="custom">{Locale.Plugin.Auth.Custom}</option>
|
||||||
|
</select>
|
||||||
|
</ListItem>
|
||||||
|
{["bearer", "basic", "custom"].includes(
|
||||||
|
editingPlugin.authType as string,
|
||||||
|
) && (
|
||||||
|
<ListItem title={Locale.Plugin.Auth.Location}>
|
||||||
|
<select
|
||||||
|
value={editingPlugin?.authLocation}
|
||||||
|
onChange={(e) => {
|
||||||
|
pluginStore.updatePlugin(editingPlugin.id, (plugin) => {
|
||||||
|
plugin.authLocation = e.target.value;
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<option value="header">
|
||||||
|
{Locale.Plugin.Auth.LocationHeader}
|
||||||
|
</option>
|
||||||
|
<option value="query">
|
||||||
|
{Locale.Plugin.Auth.LocationQuery}
|
||||||
|
</option>
|
||||||
|
<option value="body">
|
||||||
|
{Locale.Plugin.Auth.LocationBody}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</ListItem>
|
||||||
|
)}
|
||||||
|
{editingPlugin.authType == "custom" && (
|
||||||
|
<ListItem title={Locale.Plugin.Auth.CustomHeader}>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={editingPlugin?.authHeader}
|
||||||
|
onChange={(e) => {
|
||||||
|
pluginStore.updatePlugin(editingPlugin.id, (plugin) => {
|
||||||
|
plugin.authHeader = e.target.value;
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
></input>
|
||||||
|
</ListItem>
|
||||||
|
)}
|
||||||
|
{["bearer", "basic", "custom"].includes(
|
||||||
|
editingPlugin.authType as string,
|
||||||
|
) && (
|
||||||
|
<ListItem title={Locale.Plugin.Auth.Token}>
|
||||||
|
<PasswordInput
|
||||||
|
type="text"
|
||||||
|
value={editingPlugin?.authToken}
|
||||||
|
onChange={(e) => {
|
||||||
|
pluginStore.updatePlugin(editingPlugin.id, (plugin) => {
|
||||||
|
plugin.authToken = e.currentTarget.value;
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
></PasswordInput>
|
||||||
|
</ListItem>
|
||||||
|
)}
|
||||||
|
</List>
|
||||||
|
<List>
|
||||||
|
<ListItem title={Locale.Plugin.EditModal.Content}>
|
||||||
|
<div className={pluginStyles["plugin-schema"]}>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
style={{ minWidth: 200 }}
|
||||||
|
onInput={(e) => setLoadUrl(e.currentTarget.value)}
|
||||||
|
></input>
|
||||||
|
<IconButton
|
||||||
|
icon={<ReloadIcon />}
|
||||||
|
text={Locale.Plugin.EditModal.Load}
|
||||||
|
bordered
|
||||||
|
onClick={() => loadFromUrl(loadUrl)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</ListItem>
|
||||||
|
<ListItem
|
||||||
|
subTitle={
|
||||||
|
<div
|
||||||
|
className={`markdown-body ${pluginStyles["plugin-content"]}`}
|
||||||
|
dir="auto"
|
||||||
|
>
|
||||||
|
<pre>
|
||||||
|
<code
|
||||||
|
contentEditable={true}
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: editingPlugin.content,
|
||||||
|
}}
|
||||||
|
onBlur={onChangePlugin}
|
||||||
|
></code>
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
></ListItem>
|
||||||
|
{editingPluginTool?.tools.map((tool, index) => (
|
||||||
|
<ListItem
|
||||||
|
key={index}
|
||||||
|
title={tool?.function?.name}
|
||||||
|
subTitle={tool?.function?.description}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</List>
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</ErrorBoundary>
|
||||||
|
);
|
||||||
|
}
|
|
@ -192,6 +192,7 @@ export function ControlParam(props: {
|
||||||
required={item.required}
|
required={item.required}
|
||||||
>
|
>
|
||||||
<Select
|
<Select
|
||||||
|
aria-label={item.name}
|
||||||
value={props.data[item.value]}
|
value={props.data[item.value]}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
props.onChange(item.value, e.currentTarget.value);
|
props.onChange(item.value, e.currentTarget.value);
|
||||||
|
@ -216,6 +217,7 @@ export function ControlParam(props: {
|
||||||
required={item.required}
|
required={item.required}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
aria-label={item.name}
|
||||||
type="number"
|
type="number"
|
||||||
min={item.min}
|
min={item.min}
|
||||||
max={item.max}
|
max={item.max}
|
||||||
|
@ -235,6 +237,7 @@ export function ControlParam(props: {
|
||||||
required={item.required}
|
required={item.required}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
aria-label={item.name}
|
||||||
type="text"
|
type="text"
|
||||||
value={props.data[item.value]}
|
value={props.data[item.value]}
|
||||||
style={{ maxWidth: "100%", width: "100%" }}
|
style={{ maxWidth: "100%", width: "100%" }}
|
||||||
|
|
|
@ -133,6 +133,7 @@ export function Sd() {
|
||||||
{showMaxIcon && (
|
{showMaxIcon && (
|
||||||
<div className="window-action-button">
|
<div className="window-action-button">
|
||||||
<IconButton
|
<IconButton
|
||||||
|
aria={Locale.Chat.Actions.FullScreen}
|
||||||
icon={config.tightBorder ? <MinIcon /> : <MaxIcon />}
|
icon={config.tightBorder ? <MinIcon /> : <MaxIcon />}
|
||||||
bordered
|
bordered
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|
|
@ -0,0 +1,167 @@
|
||||||
|
import { useState, useEffect, useRef, useCallback } from "react";
|
||||||
|
import { ErrorBoundary } from "./error";
|
||||||
|
import styles from "./mask.module.scss";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import { IconButton } from "./button";
|
||||||
|
import CloseIcon from "../icons/close.svg";
|
||||||
|
import EyeIcon from "../icons/eye.svg";
|
||||||
|
import Locale from "../locales";
|
||||||
|
import { Path } from "../constant";
|
||||||
|
|
||||||
|
import { useChatStore } from "../store";
|
||||||
|
|
||||||
|
type Item = {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
content: string;
|
||||||
|
};
|
||||||
|
export function SearchChatPage() {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const chatStore = useChatStore();
|
||||||
|
|
||||||
|
const sessions = chatStore.sessions;
|
||||||
|
const selectSession = chatStore.selectSession;
|
||||||
|
|
||||||
|
const [searchResults, setSearchResults] = useState<Item[]>([]);
|
||||||
|
|
||||||
|
const previousValueRef = useRef<string>("");
|
||||||
|
const searchInputRef = useRef<HTMLInputElement>(null);
|
||||||
|
const doSearch = useCallback((text: string) => {
|
||||||
|
const lowerCaseText = text.toLowerCase();
|
||||||
|
const results: Item[] = [];
|
||||||
|
|
||||||
|
sessions.forEach((session, index) => {
|
||||||
|
const fullTextContents: string[] = [];
|
||||||
|
|
||||||
|
session.messages.forEach((message) => {
|
||||||
|
const content = message.content as string;
|
||||||
|
if (!content.toLowerCase || content === "") return;
|
||||||
|
const lowerCaseContent = content.toLowerCase();
|
||||||
|
|
||||||
|
// full text search
|
||||||
|
let pos = lowerCaseContent.indexOf(lowerCaseText);
|
||||||
|
while (pos !== -1) {
|
||||||
|
const start = Math.max(0, pos - 35);
|
||||||
|
const end = Math.min(content.length, pos + lowerCaseText.length + 35);
|
||||||
|
fullTextContents.push(content.substring(start, end));
|
||||||
|
pos = lowerCaseContent.indexOf(
|
||||||
|
lowerCaseText,
|
||||||
|
pos + lowerCaseText.length,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (fullTextContents.length > 0) {
|
||||||
|
results.push({
|
||||||
|
id: index,
|
||||||
|
name: session.topic,
|
||||||
|
content: fullTextContents.join("... "), // concat content with...
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// sort by length of matching content
|
||||||
|
results.sort((a, b) => b.content.length - a.content.length);
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const intervalId = setInterval(() => {
|
||||||
|
if (searchInputRef.current) {
|
||||||
|
const currentValue = searchInputRef.current.value;
|
||||||
|
if (currentValue !== previousValueRef.current) {
|
||||||
|
if (currentValue.length > 0) {
|
||||||
|
const result = doSearch(currentValue);
|
||||||
|
setSearchResults(result);
|
||||||
|
}
|
||||||
|
previousValueRef.current = currentValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
// Cleanup the interval on component unmount
|
||||||
|
return () => clearInterval(intervalId);
|
||||||
|
}, [doSearch]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ErrorBoundary>
|
||||||
|
<div className={styles["mask-page"]}>
|
||||||
|
{/* header */}
|
||||||
|
<div className="window-header">
|
||||||
|
<div className="window-header-title">
|
||||||
|
<div className="window-header-main-title">
|
||||||
|
{Locale.SearchChat.Page.Title}
|
||||||
|
</div>
|
||||||
|
<div className="window-header-submai-title">
|
||||||
|
{Locale.SearchChat.Page.SubTitle(searchResults.length)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="window-actions">
|
||||||
|
<div className="window-action-button">
|
||||||
|
<IconButton
|
||||||
|
icon={<CloseIcon />}
|
||||||
|
bordered
|
||||||
|
onClick={() => navigate(-1)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles["mask-page-body"]}>
|
||||||
|
<div className={styles["mask-filter"]}>
|
||||||
|
{/**搜索输入框 */}
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className={styles["search-bar"]}
|
||||||
|
placeholder={Locale.SearchChat.Page.Search}
|
||||||
|
autoFocus
|
||||||
|
ref={searchInputRef}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
e.preventDefault();
|
||||||
|
const searchText = e.currentTarget.value;
|
||||||
|
if (searchText.length > 0) {
|
||||||
|
const result = doSearch(searchText);
|
||||||
|
setSearchResults(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{searchResults.map((item) => (
|
||||||
|
<div
|
||||||
|
className={styles["mask-item"]}
|
||||||
|
key={item.id}
|
||||||
|
onClick={() => {
|
||||||
|
navigate(Path.Chat);
|
||||||
|
selectSession(item.id);
|
||||||
|
}}
|
||||||
|
style={{ cursor: "pointer" }}
|
||||||
|
>
|
||||||
|
{/** 搜索匹配的文本 */}
|
||||||
|
<div className={styles["mask-header"]}>
|
||||||
|
<div className={styles["mask-title"]}>
|
||||||
|
<div className={styles["mask-name"]}>{item.name}</div>
|
||||||
|
{item.content.slice(0, 70)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/** 操作按钮 */}
|
||||||
|
<div className={styles["mask-actions"]}>
|
||||||
|
<IconButton
|
||||||
|
icon={<EyeIcon />}
|
||||||
|
text={Locale.SearchChat.Item.View}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ErrorBoundary>
|
||||||
|
);
|
||||||
|
}
|
|
@ -72,3 +72,9 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.subtitle-button {
|
||||||
|
button {
|
||||||
|
overflow:visible ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import CopyIcon from "../icons/copy.svg";
|
||||||
import ClearIcon from "../icons/clear.svg";
|
import ClearIcon from "../icons/clear.svg";
|
||||||
import LoadingIcon from "../icons/three-dots.svg";
|
import LoadingIcon from "../icons/three-dots.svg";
|
||||||
import EditIcon from "../icons/edit.svg";
|
import EditIcon from "../icons/edit.svg";
|
||||||
|
import FireIcon from "../icons/fire.svg";
|
||||||
import EyeIcon from "../icons/eye.svg";
|
import EyeIcon from "../icons/eye.svg";
|
||||||
import DownloadIcon from "../icons/download.svg";
|
import DownloadIcon from "../icons/download.svg";
|
||||||
import UploadIcon from "../icons/upload.svg";
|
import UploadIcon from "../icons/upload.svg";
|
||||||
|
@ -18,7 +19,7 @@ import ConfirmIcon from "../icons/confirm.svg";
|
||||||
import ConnectionIcon from "../icons/connection.svg";
|
import ConnectionIcon from "../icons/connection.svg";
|
||||||
import CloudSuccessIcon from "../icons/cloud-success.svg";
|
import CloudSuccessIcon from "../icons/cloud-success.svg";
|
||||||
import CloudFailIcon from "../icons/cloud-fail.svg";
|
import CloudFailIcon from "../icons/cloud-fail.svg";
|
||||||
|
import { trackSettingsPageGuideToCPaymentClick } from "../utils/auth-settings-events";
|
||||||
import {
|
import {
|
||||||
Input,
|
Input,
|
||||||
List,
|
List,
|
||||||
|
@ -68,6 +69,8 @@ import {
|
||||||
SlotID,
|
SlotID,
|
||||||
UPDATE_URL,
|
UPDATE_URL,
|
||||||
Stability,
|
Stability,
|
||||||
|
Iflytek,
|
||||||
|
SAAS_CHAT_URL,
|
||||||
} from "../constant";
|
} from "../constant";
|
||||||
import { Prompt, SearchService, usePromptStore } from "../store/prompt";
|
import { Prompt, SearchService, usePromptStore } from "../store/prompt";
|
||||||
import { ErrorBoundary } from "./error";
|
import { ErrorBoundary } from "./error";
|
||||||
|
@ -79,6 +82,7 @@ import { useSyncStore } from "../store/sync";
|
||||||
import { nanoid } from "nanoid";
|
import { nanoid } from "nanoid";
|
||||||
import { useMaskStore } from "../store/mask";
|
import { useMaskStore } from "../store/mask";
|
||||||
import { ProviderType } from "../utils/cloud";
|
import { ProviderType } from "../utils/cloud";
|
||||||
|
import { TTSConfigList } from "./tts-config";
|
||||||
|
|
||||||
function EditPromptModal(props: { id: string; onClose: () => void }) {
|
function EditPromptModal(props: { id: string; onClose: () => void }) {
|
||||||
const promptStore = usePromptStore();
|
const promptStore = usePromptStore();
|
||||||
|
@ -245,6 +249,7 @@ function DangerItems() {
|
||||||
subTitle={Locale.Settings.Danger.Reset.SubTitle}
|
subTitle={Locale.Settings.Danger.Reset.SubTitle}
|
||||||
>
|
>
|
||||||
<IconButton
|
<IconButton
|
||||||
|
aria={Locale.Settings.Danger.Reset.Title}
|
||||||
text={Locale.Settings.Danger.Reset.Action}
|
text={Locale.Settings.Danger.Reset.Action}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
if (await showConfirm(Locale.Settings.Danger.Reset.Confirm)) {
|
if (await showConfirm(Locale.Settings.Danger.Reset.Confirm)) {
|
||||||
|
@ -259,6 +264,7 @@ function DangerItems() {
|
||||||
subTitle={Locale.Settings.Danger.Clear.SubTitle}
|
subTitle={Locale.Settings.Danger.Clear.SubTitle}
|
||||||
>
|
>
|
||||||
<IconButton
|
<IconButton
|
||||||
|
aria={Locale.Settings.Danger.Clear.Title}
|
||||||
text={Locale.Settings.Danger.Clear.Action}
|
text={Locale.Settings.Danger.Clear.Action}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
if (await showConfirm(Locale.Settings.Danger.Clear.Confirm)) {
|
if (await showConfirm(Locale.Settings.Danger.Clear.Confirm)) {
|
||||||
|
@ -512,6 +518,7 @@ function SyncItems() {
|
||||||
>
|
>
|
||||||
<div style={{ display: "flex" }}>
|
<div style={{ display: "flex" }}>
|
||||||
<IconButton
|
<IconButton
|
||||||
|
aria={Locale.Settings.Sync.CloudState + Locale.UI.Config}
|
||||||
icon={<ConfigIcon />}
|
icon={<ConfigIcon />}
|
||||||
text={Locale.UI.Config}
|
text={Locale.UI.Config}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -542,6 +549,7 @@ function SyncItems() {
|
||||||
>
|
>
|
||||||
<div style={{ display: "flex" }}>
|
<div style={{ display: "flex" }}>
|
||||||
<IconButton
|
<IconButton
|
||||||
|
aria={Locale.Settings.Sync.LocalState + Locale.UI.Export}
|
||||||
icon={<UploadIcon />}
|
icon={<UploadIcon />}
|
||||||
text={Locale.UI.Export}
|
text={Locale.UI.Export}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -549,6 +557,7 @@ function SyncItems() {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
|
aria={Locale.Settings.Sync.LocalState + Locale.UI.Import}
|
||||||
icon={<DownloadIcon />}
|
icon={<DownloadIcon />}
|
||||||
text={Locale.UI.Import}
|
text={Locale.UI.Import}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -679,6 +688,31 @@ export function Settings() {
|
||||||
</ListItem>
|
</ListItem>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const saasStartComponent = (
|
||||||
|
<ListItem
|
||||||
|
className={styles["subtitle-button"]}
|
||||||
|
title={
|
||||||
|
Locale.Settings.Access.SaasStart.Title +
|
||||||
|
`${Locale.Settings.Access.SaasStart.Label}`
|
||||||
|
}
|
||||||
|
subTitle={Locale.Settings.Access.SaasStart.SubTitle}
|
||||||
|
>
|
||||||
|
<IconButton
|
||||||
|
aria={
|
||||||
|
Locale.Settings.Access.SaasStart.Title +
|
||||||
|
Locale.Settings.Access.SaasStart.ChatNow
|
||||||
|
}
|
||||||
|
icon={<FireIcon />}
|
||||||
|
type={"primary"}
|
||||||
|
text={Locale.Settings.Access.SaasStart.ChatNow}
|
||||||
|
onClick={() => {
|
||||||
|
trackSettingsPageGuideToCPaymentClick();
|
||||||
|
window.location.href = SAAS_CHAT_URL;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</ListItem>
|
||||||
|
);
|
||||||
|
|
||||||
const useCustomConfigComponent = // Conditionally render the following ListItem based on clientConfig.isApp
|
const useCustomConfigComponent = // Conditionally render the following ListItem based on clientConfig.isApp
|
||||||
!clientConfig?.isApp && ( // only show if isApp is false
|
!clientConfig?.isApp && ( // only show if isApp is false
|
||||||
<ListItem
|
<ListItem
|
||||||
|
@ -686,6 +720,7 @@ export function Settings() {
|
||||||
subTitle={Locale.Settings.Access.CustomEndpoint.SubTitle}
|
subTitle={Locale.Settings.Access.CustomEndpoint.SubTitle}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
aria-label={Locale.Settings.Access.CustomEndpoint.Title}
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={accessStore.useCustomConfig}
|
checked={accessStore.useCustomConfig}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
|
@ -705,6 +740,7 @@ export function Settings() {
|
||||||
subTitle={Locale.Settings.Access.OpenAI.Endpoint.SubTitle}
|
subTitle={Locale.Settings.Access.OpenAI.Endpoint.SubTitle}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
aria-label={Locale.Settings.Access.OpenAI.Endpoint.Title}
|
||||||
type="text"
|
type="text"
|
||||||
value={accessStore.openaiUrl}
|
value={accessStore.openaiUrl}
|
||||||
placeholder={OPENAI_BASE_URL}
|
placeholder={OPENAI_BASE_URL}
|
||||||
|
@ -720,6 +756,8 @@ export function Settings() {
|
||||||
subTitle={Locale.Settings.Access.OpenAI.ApiKey.SubTitle}
|
subTitle={Locale.Settings.Access.OpenAI.ApiKey.SubTitle}
|
||||||
>
|
>
|
||||||
<PasswordInput
|
<PasswordInput
|
||||||
|
aria={Locale.Settings.ShowPassword}
|
||||||
|
aria-label={Locale.Settings.Access.OpenAI.ApiKey.Title}
|
||||||
value={accessStore.openaiApiKey}
|
value={accessStore.openaiApiKey}
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={Locale.Settings.Access.OpenAI.ApiKey.Placeholder}
|
placeholder={Locale.Settings.Access.OpenAI.ApiKey.Placeholder}
|
||||||
|
@ -743,6 +781,7 @@ export function Settings() {
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
aria-label={Locale.Settings.Access.Azure.Endpoint.Title}
|
||||||
type="text"
|
type="text"
|
||||||
value={accessStore.azureUrl}
|
value={accessStore.azureUrl}
|
||||||
placeholder={Azure.ExampleEndpoint}
|
placeholder={Azure.ExampleEndpoint}
|
||||||
|
@ -758,6 +797,7 @@ export function Settings() {
|
||||||
subTitle={Locale.Settings.Access.Azure.ApiKey.SubTitle}
|
subTitle={Locale.Settings.Access.Azure.ApiKey.SubTitle}
|
||||||
>
|
>
|
||||||
<PasswordInput
|
<PasswordInput
|
||||||
|
aria-label={Locale.Settings.Access.Azure.ApiKey.Title}
|
||||||
value={accessStore.azureApiKey}
|
value={accessStore.azureApiKey}
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={Locale.Settings.Access.Azure.ApiKey.Placeholder}
|
placeholder={Locale.Settings.Access.Azure.ApiKey.Placeholder}
|
||||||
|
@ -773,6 +813,7 @@ export function Settings() {
|
||||||
subTitle={Locale.Settings.Access.Azure.ApiVerion.SubTitle}
|
subTitle={Locale.Settings.Access.Azure.ApiVerion.SubTitle}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
aria-label={Locale.Settings.Access.Azure.ApiVerion.Title}
|
||||||
type="text"
|
type="text"
|
||||||
value={accessStore.azureApiVersion}
|
value={accessStore.azureApiVersion}
|
||||||
placeholder="2023-08-01-preview"
|
placeholder="2023-08-01-preview"
|
||||||
|
@ -797,6 +838,7 @@ export function Settings() {
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
aria-label={Locale.Settings.Access.Google.Endpoint.Title}
|
||||||
type="text"
|
type="text"
|
||||||
value={accessStore.googleUrl}
|
value={accessStore.googleUrl}
|
||||||
placeholder={Google.ExampleEndpoint}
|
placeholder={Google.ExampleEndpoint}
|
||||||
|
@ -812,6 +854,7 @@ export function Settings() {
|
||||||
subTitle={Locale.Settings.Access.Google.ApiKey.SubTitle}
|
subTitle={Locale.Settings.Access.Google.ApiKey.SubTitle}
|
||||||
>
|
>
|
||||||
<PasswordInput
|
<PasswordInput
|
||||||
|
aria-label={Locale.Settings.Access.Google.ApiKey.Title}
|
||||||
value={accessStore.googleApiKey}
|
value={accessStore.googleApiKey}
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={Locale.Settings.Access.Google.ApiKey.Placeholder}
|
placeholder={Locale.Settings.Access.Google.ApiKey.Placeholder}
|
||||||
|
@ -827,6 +870,7 @@ export function Settings() {
|
||||||
subTitle={Locale.Settings.Access.Google.ApiVersion.SubTitle}
|
subTitle={Locale.Settings.Access.Google.ApiVersion.SubTitle}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
aria-label={Locale.Settings.Access.Google.ApiVersion.Title}
|
||||||
type="text"
|
type="text"
|
||||||
value={accessStore.googleApiVersion}
|
value={accessStore.googleApiVersion}
|
||||||
placeholder="2023-08-01-preview"
|
placeholder="2023-08-01-preview"
|
||||||
|
@ -842,6 +886,7 @@ export function Settings() {
|
||||||
subTitle={Locale.Settings.Access.Google.GoogleSafetySettings.SubTitle}
|
subTitle={Locale.Settings.Access.Google.GoogleSafetySettings.SubTitle}
|
||||||
>
|
>
|
||||||
<Select
|
<Select
|
||||||
|
aria-label={Locale.Settings.Access.Google.GoogleSafetySettings.Title}
|
||||||
value={accessStore.googleSafetySettings}
|
value={accessStore.googleSafetySettings}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
accessStore.update(
|
accessStore.update(
|
||||||
|
@ -872,6 +917,7 @@ export function Settings() {
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
aria-label={Locale.Settings.Access.Anthropic.Endpoint.Title}
|
||||||
type="text"
|
type="text"
|
||||||
value={accessStore.anthropicUrl}
|
value={accessStore.anthropicUrl}
|
||||||
placeholder={Anthropic.ExampleEndpoint}
|
placeholder={Anthropic.ExampleEndpoint}
|
||||||
|
@ -887,6 +933,7 @@ export function Settings() {
|
||||||
subTitle={Locale.Settings.Access.Anthropic.ApiKey.SubTitle}
|
subTitle={Locale.Settings.Access.Anthropic.ApiKey.SubTitle}
|
||||||
>
|
>
|
||||||
<PasswordInput
|
<PasswordInput
|
||||||
|
aria-label={Locale.Settings.Access.Anthropic.ApiKey.Title}
|
||||||
value={accessStore.anthropicApiKey}
|
value={accessStore.anthropicApiKey}
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={Locale.Settings.Access.Anthropic.ApiKey.Placeholder}
|
placeholder={Locale.Settings.Access.Anthropic.ApiKey.Placeholder}
|
||||||
|
@ -902,6 +949,7 @@ export function Settings() {
|
||||||
subTitle={Locale.Settings.Access.Anthropic.ApiVerion.SubTitle}
|
subTitle={Locale.Settings.Access.Anthropic.ApiVerion.SubTitle}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
aria-label={Locale.Settings.Access.Anthropic.ApiVerion.Title}
|
||||||
type="text"
|
type="text"
|
||||||
value={accessStore.anthropicApiVersion}
|
value={accessStore.anthropicApiVersion}
|
||||||
placeholder={Anthropic.Vision}
|
placeholder={Anthropic.Vision}
|
||||||
|
@ -923,6 +971,7 @@ export function Settings() {
|
||||||
subTitle={Locale.Settings.Access.Baidu.Endpoint.SubTitle}
|
subTitle={Locale.Settings.Access.Baidu.Endpoint.SubTitle}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
aria-label={Locale.Settings.Access.Baidu.Endpoint.Title}
|
||||||
type="text"
|
type="text"
|
||||||
value={accessStore.baiduUrl}
|
value={accessStore.baiduUrl}
|
||||||
placeholder={Baidu.ExampleEndpoint}
|
placeholder={Baidu.ExampleEndpoint}
|
||||||
|
@ -938,6 +987,7 @@ export function Settings() {
|
||||||
subTitle={Locale.Settings.Access.Baidu.ApiKey.SubTitle}
|
subTitle={Locale.Settings.Access.Baidu.ApiKey.SubTitle}
|
||||||
>
|
>
|
||||||
<PasswordInput
|
<PasswordInput
|
||||||
|
aria-label={Locale.Settings.Access.Baidu.ApiKey.Title}
|
||||||
value={accessStore.baiduApiKey}
|
value={accessStore.baiduApiKey}
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={Locale.Settings.Access.Baidu.ApiKey.Placeholder}
|
placeholder={Locale.Settings.Access.Baidu.ApiKey.Placeholder}
|
||||||
|
@ -953,6 +1003,7 @@ export function Settings() {
|
||||||
subTitle={Locale.Settings.Access.Baidu.SecretKey.SubTitle}
|
subTitle={Locale.Settings.Access.Baidu.SecretKey.SubTitle}
|
||||||
>
|
>
|
||||||
<PasswordInput
|
<PasswordInput
|
||||||
|
aria-label={Locale.Settings.Access.Baidu.SecretKey.Title}
|
||||||
value={accessStore.baiduSecretKey}
|
value={accessStore.baiduSecretKey}
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={Locale.Settings.Access.Baidu.SecretKey.Placeholder}
|
placeholder={Locale.Settings.Access.Baidu.SecretKey.Placeholder}
|
||||||
|
@ -974,6 +1025,7 @@ export function Settings() {
|
||||||
subTitle={Locale.Settings.Access.Tencent.Endpoint.SubTitle}
|
subTitle={Locale.Settings.Access.Tencent.Endpoint.SubTitle}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
aria-label={Locale.Settings.Access.Tencent.Endpoint.Title}
|
||||||
type="text"
|
type="text"
|
||||||
value={accessStore.tencentUrl}
|
value={accessStore.tencentUrl}
|
||||||
placeholder={Tencent.ExampleEndpoint}
|
placeholder={Tencent.ExampleEndpoint}
|
||||||
|
@ -989,6 +1041,7 @@ export function Settings() {
|
||||||
subTitle={Locale.Settings.Access.Tencent.ApiKey.SubTitle}
|
subTitle={Locale.Settings.Access.Tencent.ApiKey.SubTitle}
|
||||||
>
|
>
|
||||||
<PasswordInput
|
<PasswordInput
|
||||||
|
aria-label={Locale.Settings.Access.Tencent.ApiKey.Title}
|
||||||
value={accessStore.tencentSecretId}
|
value={accessStore.tencentSecretId}
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={Locale.Settings.Access.Tencent.ApiKey.Placeholder}
|
placeholder={Locale.Settings.Access.Tencent.ApiKey.Placeholder}
|
||||||
|
@ -1004,6 +1057,7 @@ export function Settings() {
|
||||||
subTitle={Locale.Settings.Access.Tencent.SecretKey.SubTitle}
|
subTitle={Locale.Settings.Access.Tencent.SecretKey.SubTitle}
|
||||||
>
|
>
|
||||||
<PasswordInput
|
<PasswordInput
|
||||||
|
aria-label={Locale.Settings.Access.Tencent.SecretKey.Title}
|
||||||
value={accessStore.tencentSecretKey}
|
value={accessStore.tencentSecretKey}
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={Locale.Settings.Access.Tencent.SecretKey.Placeholder}
|
placeholder={Locale.Settings.Access.Tencent.SecretKey.Placeholder}
|
||||||
|
@ -1028,6 +1082,7 @@ export function Settings() {
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
aria-label={Locale.Settings.Access.ByteDance.Endpoint.Title}
|
||||||
type="text"
|
type="text"
|
||||||
value={accessStore.bytedanceUrl}
|
value={accessStore.bytedanceUrl}
|
||||||
placeholder={ByteDance.ExampleEndpoint}
|
placeholder={ByteDance.ExampleEndpoint}
|
||||||
|
@ -1043,6 +1098,7 @@ export function Settings() {
|
||||||
subTitle={Locale.Settings.Access.ByteDance.ApiKey.SubTitle}
|
subTitle={Locale.Settings.Access.ByteDance.ApiKey.SubTitle}
|
||||||
>
|
>
|
||||||
<PasswordInput
|
<PasswordInput
|
||||||
|
aria-label={Locale.Settings.Access.ByteDance.ApiKey.Title}
|
||||||
value={accessStore.bytedanceApiKey}
|
value={accessStore.bytedanceApiKey}
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={Locale.Settings.Access.ByteDance.ApiKey.Placeholder}
|
placeholder={Locale.Settings.Access.ByteDance.ApiKey.Placeholder}
|
||||||
|
@ -1067,6 +1123,7 @@ export function Settings() {
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
aria-label={Locale.Settings.Access.Alibaba.Endpoint.Title}
|
||||||
type="text"
|
type="text"
|
||||||
value={accessStore.alibabaUrl}
|
value={accessStore.alibabaUrl}
|
||||||
placeholder={Alibaba.ExampleEndpoint}
|
placeholder={Alibaba.ExampleEndpoint}
|
||||||
|
@ -1082,6 +1139,7 @@ export function Settings() {
|
||||||
subTitle={Locale.Settings.Access.Alibaba.ApiKey.SubTitle}
|
subTitle={Locale.Settings.Access.Alibaba.ApiKey.SubTitle}
|
||||||
>
|
>
|
||||||
<PasswordInput
|
<PasswordInput
|
||||||
|
aria-label={Locale.Settings.Access.Alibaba.ApiKey.Title}
|
||||||
value={accessStore.alibabaApiKey}
|
value={accessStore.alibabaApiKey}
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={Locale.Settings.Access.Alibaba.ApiKey.Placeholder}
|
placeholder={Locale.Settings.Access.Alibaba.ApiKey.Placeholder}
|
||||||
|
@ -1106,6 +1164,7 @@ export function Settings() {
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
aria-label={Locale.Settings.Access.Moonshot.Endpoint.Title}
|
||||||
type="text"
|
type="text"
|
||||||
value={accessStore.moonshotUrl}
|
value={accessStore.moonshotUrl}
|
||||||
placeholder={Moonshot.ExampleEndpoint}
|
placeholder={Moonshot.ExampleEndpoint}
|
||||||
|
@ -1121,6 +1180,7 @@ export function Settings() {
|
||||||
subTitle={Locale.Settings.Access.Moonshot.ApiKey.SubTitle}
|
subTitle={Locale.Settings.Access.Moonshot.ApiKey.SubTitle}
|
||||||
>
|
>
|
||||||
<PasswordInput
|
<PasswordInput
|
||||||
|
aria-label={Locale.Settings.Access.Moonshot.ApiKey.Title}
|
||||||
value={accessStore.moonshotApiKey}
|
value={accessStore.moonshotApiKey}
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={Locale.Settings.Access.Moonshot.ApiKey.Placeholder}
|
placeholder={Locale.Settings.Access.Moonshot.ApiKey.Placeholder}
|
||||||
|
@ -1145,6 +1205,7 @@ export function Settings() {
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
aria-label={Locale.Settings.Access.Stability.Endpoint.Title}
|
||||||
type="text"
|
type="text"
|
||||||
value={accessStore.stabilityUrl}
|
value={accessStore.stabilityUrl}
|
||||||
placeholder={Stability.ExampleEndpoint}
|
placeholder={Stability.ExampleEndpoint}
|
||||||
|
@ -1160,6 +1221,7 @@ export function Settings() {
|
||||||
subTitle={Locale.Settings.Access.Stability.ApiKey.SubTitle}
|
subTitle={Locale.Settings.Access.Stability.ApiKey.SubTitle}
|
||||||
>
|
>
|
||||||
<PasswordInput
|
<PasswordInput
|
||||||
|
aria-label={Locale.Settings.Access.Stability.ApiKey.Title}
|
||||||
value={accessStore.stabilityApiKey}
|
value={accessStore.stabilityApiKey}
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={Locale.Settings.Access.Stability.ApiKey.Placeholder}
|
placeholder={Locale.Settings.Access.Stability.ApiKey.Placeholder}
|
||||||
|
@ -1172,6 +1234,63 @@ export function Settings() {
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
const lflytekConfigComponent = accessStore.provider ===
|
||||||
|
ServiceProvider.Iflytek && (
|
||||||
|
<>
|
||||||
|
<ListItem
|
||||||
|
title={Locale.Settings.Access.Iflytek.Endpoint.Title}
|
||||||
|
subTitle={
|
||||||
|
Locale.Settings.Access.Iflytek.Endpoint.SubTitle +
|
||||||
|
Iflytek.ExampleEndpoint
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
aria-label={Locale.Settings.Access.Iflytek.Endpoint.Title}
|
||||||
|
type="text"
|
||||||
|
value={accessStore.iflytekUrl}
|
||||||
|
placeholder={Iflytek.ExampleEndpoint}
|
||||||
|
onChange={(e) =>
|
||||||
|
accessStore.update(
|
||||||
|
(access) => (access.iflytekUrl = e.currentTarget.value),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
></input>
|
||||||
|
</ListItem>
|
||||||
|
<ListItem
|
||||||
|
title={Locale.Settings.Access.Iflytek.ApiKey.Title}
|
||||||
|
subTitle={Locale.Settings.Access.Iflytek.ApiKey.SubTitle}
|
||||||
|
>
|
||||||
|
<PasswordInput
|
||||||
|
aria-label={Locale.Settings.Access.Iflytek.ApiKey.Title}
|
||||||
|
value={accessStore.iflytekApiKey}
|
||||||
|
type="text"
|
||||||
|
placeholder={Locale.Settings.Access.Iflytek.ApiKey.Placeholder}
|
||||||
|
onChange={(e) => {
|
||||||
|
accessStore.update(
|
||||||
|
(access) => (access.iflytekApiKey = e.currentTarget.value),
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</ListItem>
|
||||||
|
|
||||||
|
<ListItem
|
||||||
|
title={Locale.Settings.Access.Iflytek.ApiSecret.Title}
|
||||||
|
subTitle={Locale.Settings.Access.Iflytek.ApiSecret.SubTitle}
|
||||||
|
>
|
||||||
|
<PasswordInput
|
||||||
|
aria-label={Locale.Settings.Access.Iflytek.ApiSecret.Title}
|
||||||
|
value={accessStore.iflytekApiSecret}
|
||||||
|
type="text"
|
||||||
|
placeholder={Locale.Settings.Access.Iflytek.ApiSecret.Placeholder}
|
||||||
|
onChange={(e) => {
|
||||||
|
accessStore.update(
|
||||||
|
(access) => (access.iflytekApiSecret = e.currentTarget.value),
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</ListItem>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
|
@ -1189,6 +1308,7 @@ export function Settings() {
|
||||||
<div className="window-action-button"></div>
|
<div className="window-action-button"></div>
|
||||||
<div className="window-action-button">
|
<div className="window-action-button">
|
||||||
<IconButton
|
<IconButton
|
||||||
|
aria={Locale.UI.Close}
|
||||||
icon={<CloseIcon />}
|
icon={<CloseIcon />}
|
||||||
onClick={() => navigate(Path.Home)}
|
onClick={() => navigate(Path.Home)}
|
||||||
bordered
|
bordered
|
||||||
|
@ -1212,6 +1332,8 @@ export function Settings() {
|
||||||
open={showEmojiPicker}
|
open={showEmojiPicker}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
aria-label={Locale.Settings.Avatar}
|
||||||
|
tabIndex={0}
|
||||||
className={styles.avatar}
|
className={styles.avatar}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setShowEmojiPicker(!showEmojiPicker);
|
setShowEmojiPicker(!showEmojiPicker);
|
||||||
|
@ -1249,6 +1371,7 @@ export function Settings() {
|
||||||
|
|
||||||
<ListItem title={Locale.Settings.SendKey}>
|
<ListItem title={Locale.Settings.SendKey}>
|
||||||
<Select
|
<Select
|
||||||
|
aria-label={Locale.Settings.SendKey}
|
||||||
value={config.submitKey}
|
value={config.submitKey}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
updateConfig(
|
updateConfig(
|
||||||
|
@ -1267,6 +1390,7 @@ export function Settings() {
|
||||||
|
|
||||||
<ListItem title={Locale.Settings.Theme}>
|
<ListItem title={Locale.Settings.Theme}>
|
||||||
<Select
|
<Select
|
||||||
|
aria-label={Locale.Settings.Theme}
|
||||||
value={config.theme}
|
value={config.theme}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
updateConfig(
|
updateConfig(
|
||||||
|
@ -1284,6 +1408,7 @@ export function Settings() {
|
||||||
|
|
||||||
<ListItem title={Locale.Settings.Lang.Name}>
|
<ListItem title={Locale.Settings.Lang.Name}>
|
||||||
<Select
|
<Select
|
||||||
|
aria-label={Locale.Settings.Lang.Name}
|
||||||
value={getLang()}
|
value={getLang()}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
changeLang(e.target.value as any);
|
changeLang(e.target.value as any);
|
||||||
|
@ -1302,6 +1427,7 @@ export function Settings() {
|
||||||
subTitle={Locale.Settings.FontSize.SubTitle}
|
subTitle={Locale.Settings.FontSize.SubTitle}
|
||||||
>
|
>
|
||||||
<InputRange
|
<InputRange
|
||||||
|
aria={Locale.Settings.FontSize.Title}
|
||||||
title={`${config.fontSize ?? 14}px`}
|
title={`${config.fontSize ?? 14}px`}
|
||||||
value={config.fontSize}
|
value={config.fontSize}
|
||||||
min="12"
|
min="12"
|
||||||
|
@ -1321,6 +1447,7 @@ export function Settings() {
|
||||||
subTitle={Locale.Settings.FontFamily.SubTitle}
|
subTitle={Locale.Settings.FontFamily.SubTitle}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
aria-label={Locale.Settings.FontFamily.Title}
|
||||||
type="text"
|
type="text"
|
||||||
value={config.fontFamily}
|
value={config.fontFamily}
|
||||||
placeholder={Locale.Settings.FontFamily.Placeholder}
|
placeholder={Locale.Settings.FontFamily.Placeholder}
|
||||||
|
@ -1337,6 +1464,7 @@ export function Settings() {
|
||||||
subTitle={Locale.Settings.AutoGenerateTitle.SubTitle}
|
subTitle={Locale.Settings.AutoGenerateTitle.SubTitle}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
aria-label={Locale.Settings.AutoGenerateTitle.Title}
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={config.enableAutoGenerateTitle}
|
checked={config.enableAutoGenerateTitle}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
|
@ -1353,6 +1481,7 @@ export function Settings() {
|
||||||
subTitle={Locale.Settings.SendPreviewBubble.SubTitle}
|
subTitle={Locale.Settings.SendPreviewBubble.SubTitle}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
aria-label={Locale.Settings.SendPreviewBubble.Title}
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={config.sendPreviewBubble}
|
checked={config.sendPreviewBubble}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
|
@ -1363,6 +1492,23 @@ export function Settings() {
|
||||||
}
|
}
|
||||||
></input>
|
></input>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
|
||||||
|
<ListItem
|
||||||
|
title={Locale.Mask.Config.Artifacts.Title}
|
||||||
|
subTitle={Locale.Mask.Config.Artifacts.SubTitle}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
aria-label={Locale.Mask.Config.Artifacts.Title}
|
||||||
|
type="checkbox"
|
||||||
|
checked={config.enableArtifacts}
|
||||||
|
onChange={(e) =>
|
||||||
|
updateConfig(
|
||||||
|
(config) =>
|
||||||
|
(config.enableArtifacts = e.currentTarget.checked),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
></input>
|
||||||
|
</ListItem>
|
||||||
</List>
|
</List>
|
||||||
|
|
||||||
<SyncItems />
|
<SyncItems />
|
||||||
|
@ -1373,6 +1519,7 @@ export function Settings() {
|
||||||
subTitle={Locale.Settings.Mask.Splash.SubTitle}
|
subTitle={Locale.Settings.Mask.Splash.SubTitle}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
aria-label={Locale.Settings.Mask.Splash.Title}
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={!config.dontShowMaskSplashScreen}
|
checked={!config.dontShowMaskSplashScreen}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
|
@ -1390,6 +1537,7 @@ export function Settings() {
|
||||||
subTitle={Locale.Settings.Mask.Builtin.SubTitle}
|
subTitle={Locale.Settings.Mask.Builtin.SubTitle}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
aria-label={Locale.Settings.Mask.Builtin.Title}
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={config.hideBuiltinMasks}
|
checked={config.hideBuiltinMasks}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
|
@ -1408,6 +1556,7 @@ export function Settings() {
|
||||||
subTitle={Locale.Settings.Prompt.Disable.SubTitle}
|
subTitle={Locale.Settings.Prompt.Disable.SubTitle}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
aria-label={Locale.Settings.Prompt.Disable.Title}
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={config.disablePromptHint}
|
checked={config.disablePromptHint}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
|
@ -1427,6 +1576,7 @@ export function Settings() {
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<IconButton
|
<IconButton
|
||||||
|
aria={Locale.Settings.Prompt.List + Locale.Settings.Prompt.Edit}
|
||||||
icon={<EditIcon />}
|
icon={<EditIcon />}
|
||||||
text={Locale.Settings.Prompt.Edit}
|
text={Locale.Settings.Prompt.Edit}
|
||||||
onClick={() => setShowPromptModal(true)}
|
onClick={() => setShowPromptModal(true)}
|
||||||
|
@ -1435,6 +1585,7 @@ export function Settings() {
|
||||||
</List>
|
</List>
|
||||||
|
|
||||||
<List id={SlotID.CustomModel}>
|
<List id={SlotID.CustomModel}>
|
||||||
|
{saasStartComponent}
|
||||||
{accessCodeComponent}
|
{accessCodeComponent}
|
||||||
|
|
||||||
{!accessStore.hideUserApiKey && (
|
{!accessStore.hideUserApiKey && (
|
||||||
|
@ -1448,6 +1599,7 @@ export function Settings() {
|
||||||
subTitle={Locale.Settings.Access.Provider.SubTitle}
|
subTitle={Locale.Settings.Access.Provider.SubTitle}
|
||||||
>
|
>
|
||||||
<Select
|
<Select
|
||||||
|
aria-label={Locale.Settings.Access.Provider.Title}
|
||||||
value={accessStore.provider}
|
value={accessStore.provider}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
accessStore.update(
|
accessStore.update(
|
||||||
|
@ -1475,6 +1627,7 @@ export function Settings() {
|
||||||
{tencentConfigComponent}
|
{tencentConfigComponent}
|
||||||
{moonshotConfigComponent}
|
{moonshotConfigComponent}
|
||||||
{stabilityConfigComponent}
|
{stabilityConfigComponent}
|
||||||
|
{lflytekConfigComponent}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
@ -1511,6 +1664,7 @@ export function Settings() {
|
||||||
subTitle={Locale.Settings.Access.CustomModel.SubTitle}
|
subTitle={Locale.Settings.Access.CustomModel.SubTitle}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
aria-label={Locale.Settings.Access.CustomModel.Title}
|
||||||
type="text"
|
type="text"
|
||||||
value={config.customModels}
|
value={config.customModels}
|
||||||
placeholder="model1,model2,model3"
|
placeholder="model1,model2,model3"
|
||||||
|
@ -1538,6 +1692,17 @@ export function Settings() {
|
||||||
<UserPromptModal onClose={() => setShowPromptModal(false)} />
|
<UserPromptModal onClose={() => setShowPromptModal(false)} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<List>
|
||||||
|
<TTSConfigList
|
||||||
|
ttsConfig={config.ttsConfig}
|
||||||
|
updateConfig={(updater) => {
|
||||||
|
const ttsConfig = { ...config.ttsConfig };
|
||||||
|
updater(ttsConfig);
|
||||||
|
config.update((config) => (config.ttsConfig = ttsConfig));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</List>
|
||||||
|
|
||||||
<DangerItems />
|
<DangerItems />
|
||||||
</div>
|
</div>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
|
|
|
@ -7,7 +7,6 @@ import SettingsIcon from "../icons/settings.svg";
|
||||||
import GithubIcon from "../icons/github.svg";
|
import GithubIcon from "../icons/github.svg";
|
||||||
import ChatGptIcon from "../icons/chatgpt.svg";
|
import ChatGptIcon from "../icons/chatgpt.svg";
|
||||||
import AddIcon from "../icons/add.svg";
|
import AddIcon from "../icons/add.svg";
|
||||||
import CloseIcon from "../icons/close.svg";
|
|
||||||
import DeleteIcon from "../icons/delete.svg";
|
import DeleteIcon from "../icons/delete.svg";
|
||||||
import MaskIcon from "../icons/mask.svg";
|
import MaskIcon from "../icons/mask.svg";
|
||||||
import DragIcon from "../icons/drag.svg";
|
import DragIcon from "../icons/drag.svg";
|
||||||
|
@ -254,11 +253,6 @@ export function SideBar(props: { className?: string }) {
|
||||||
{showPluginSelector && (
|
{showPluginSelector && (
|
||||||
<Selector
|
<Selector
|
||||||
items={[
|
items={[
|
||||||
{
|
|
||||||
title: "👇 Please select the plugin you need to use",
|
|
||||||
value: "-",
|
|
||||||
disable: true,
|
|
||||||
},
|
|
||||||
...PLUGINS.map((item) => {
|
...PLUGINS.map((item) => {
|
||||||
return {
|
return {
|
||||||
title: item.name,
|
title: item.name,
|
||||||
|
@ -297,12 +291,20 @@ export function SideBar(props: { className?: string }) {
|
||||||
</div>
|
</div>
|
||||||
<div className={styles["sidebar-action"]}>
|
<div className={styles["sidebar-action"]}>
|
||||||
<Link to={Path.Settings}>
|
<Link to={Path.Settings}>
|
||||||
<IconButton icon={<SettingsIcon />} shadow />
|
<IconButton
|
||||||
|
aria={Locale.Settings.Title}
|
||||||
|
icon={<SettingsIcon />}
|
||||||
|
shadow
|
||||||
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles["sidebar-action"]}>
|
<div className={styles["sidebar-action"]}>
|
||||||
<a href={REPO_URL} target="_blank" rel="noopener noreferrer">
|
<a href={REPO_URL} target="_blank" rel="noopener noreferrer">
|
||||||
<IconButton icon={<GithubIcon />} shadow />
|
<IconButton
|
||||||
|
aria={Locale.Export.MessageFromChatGPT}
|
||||||
|
icon={<GithubIcon />}
|
||||||
|
shadow
|
||||||
|
/>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -0,0 +1,133 @@
|
||||||
|
import { TTSConfig, TTSConfigValidator } from "../store";
|
||||||
|
|
||||||
|
import Locale from "../locales";
|
||||||
|
import { ListItem, Select } from "./ui-lib";
|
||||||
|
import {
|
||||||
|
DEFAULT_TTS_ENGINE,
|
||||||
|
DEFAULT_TTS_ENGINES,
|
||||||
|
DEFAULT_TTS_MODELS,
|
||||||
|
DEFAULT_TTS_VOICES,
|
||||||
|
} from "../constant";
|
||||||
|
import { InputRange } from "./input-range";
|
||||||
|
|
||||||
|
export function TTSConfigList(props: {
|
||||||
|
ttsConfig: TTSConfig;
|
||||||
|
updateConfig: (updater: (config: TTSConfig) => void) => void;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ListItem
|
||||||
|
title={Locale.Settings.TTS.Enable.Title}
|
||||||
|
subTitle={Locale.Settings.TTS.Enable.SubTitle}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={props.ttsConfig.enable}
|
||||||
|
onChange={(e) =>
|
||||||
|
props.updateConfig(
|
||||||
|
(config) => (config.enable = e.currentTarget.checked),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
></input>
|
||||||
|
</ListItem>
|
||||||
|
{/* <ListItem
|
||||||
|
title={Locale.Settings.TTS.Autoplay.Title}
|
||||||
|
subTitle={Locale.Settings.TTS.Autoplay.SubTitle}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={props.ttsConfig.autoplay}
|
||||||
|
onChange={(e) =>
|
||||||
|
props.updateConfig(
|
||||||
|
(config) => (config.autoplay = e.currentTarget.checked),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
></input>
|
||||||
|
</ListItem> */}
|
||||||
|
<ListItem title={Locale.Settings.TTS.Engine}>
|
||||||
|
<Select
|
||||||
|
value={props.ttsConfig.engine}
|
||||||
|
onChange={(e) => {
|
||||||
|
props.updateConfig(
|
||||||
|
(config) =>
|
||||||
|
(config.engine = TTSConfigValidator.engine(
|
||||||
|
e.currentTarget.value,
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{DEFAULT_TTS_ENGINES.map((v, i) => (
|
||||||
|
<option value={v} key={i}>
|
||||||
|
{v}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</ListItem>
|
||||||
|
{props.ttsConfig.engine === DEFAULT_TTS_ENGINE && (
|
||||||
|
<>
|
||||||
|
<ListItem title={Locale.Settings.TTS.Model}>
|
||||||
|
<Select
|
||||||
|
value={props.ttsConfig.model}
|
||||||
|
onChange={(e) => {
|
||||||
|
props.updateConfig(
|
||||||
|
(config) =>
|
||||||
|
(config.model = TTSConfigValidator.model(
|
||||||
|
e.currentTarget.value,
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{DEFAULT_TTS_MODELS.map((v, i) => (
|
||||||
|
<option value={v} key={i}>
|
||||||
|
{v}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</ListItem>
|
||||||
|
<ListItem
|
||||||
|
title={Locale.Settings.TTS.Voice.Title}
|
||||||
|
subTitle={Locale.Settings.TTS.Voice.SubTitle}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
value={props.ttsConfig.voice}
|
||||||
|
onChange={(e) => {
|
||||||
|
props.updateConfig(
|
||||||
|
(config) =>
|
||||||
|
(config.voice = TTSConfigValidator.voice(
|
||||||
|
e.currentTarget.value,
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{DEFAULT_TTS_VOICES.map((v, i) => (
|
||||||
|
<option value={v} key={i}>
|
||||||
|
{v}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</ListItem>
|
||||||
|
<ListItem
|
||||||
|
title={Locale.Settings.TTS.Speed.Title}
|
||||||
|
subTitle={Locale.Settings.TTS.Speed.SubTitle}
|
||||||
|
>
|
||||||
|
<InputRange
|
||||||
|
aria={Locale.Settings.TTS.Speed.Title}
|
||||||
|
value={props.ttsConfig.speed?.toFixed(1)}
|
||||||
|
min="0.3"
|
||||||
|
max="4.0"
|
||||||
|
step="0.1"
|
||||||
|
onChange={(e) => {
|
||||||
|
props.updateConfig(
|
||||||
|
(config) =>
|
||||||
|
(config.speed = TTSConfigValidator.speed(
|
||||||
|
e.currentTarget.valueAsNumber,
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
></InputRange>
|
||||||
|
</ListItem>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,119 @@
|
||||||
|
@import "../styles/animation.scss";
|
||||||
|
.plugin-page {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.plugin-page-body {
|
||||||
|
padding: 20px;
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
.plugin-filter {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
animation: slide-in ease 0.3s;
|
||||||
|
height: 40px;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.search-bar {
|
||||||
|
flex-grow: 1;
|
||||||
|
max-width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-bar:focus {
|
||||||
|
border: 1px solid var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.plugin-filter-lang {
|
||||||
|
height: 100%;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plugin-create {
|
||||||
|
height: 100%;
|
||||||
|
margin-left: 10px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
min-width: 80px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.plugin-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 20px;
|
||||||
|
border: var(--border-in-light);
|
||||||
|
animation: slide-in ease 0.3s;
|
||||||
|
|
||||||
|
&:not(:last-child) {
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
border-top-left-radius: 10px;
|
||||||
|
border-top-right-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom-left-radius: 10px;
|
||||||
|
border-bottom-right-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plugin-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.plugin-icon {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plugin-title {
|
||||||
|
.plugin-name {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.plugin-info {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.plugin-runtime-warning {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #f86c6c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.plugin-actions {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
transition: all ease 0.3s;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 600px) {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
border-radius: 10px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
box-shadow: var(--card-shadow);
|
||||||
|
|
||||||
|
&:not(:last-child) {
|
||||||
|
border-bottom: var(--border-in-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.plugin-actions {
|
||||||
|
width: 100%;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -62,14 +62,14 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.vertical{
|
&.vertical {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: start;
|
align-items: start;
|
||||||
.list-header{
|
.list-header {
|
||||||
.list-item-title{
|
.list-item-title {
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
.list-item-sub-title{
|
.list-item-sub-title {
|
||||||
margin-bottom: 2px;
|
margin-bottom: 2px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -252,6 +252,12 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
max-width: fit-content;
|
max-width: fit-content;
|
||||||
|
|
||||||
|
&.left-align-option {
|
||||||
|
option {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.select-with-icon-select {
|
.select-with-icon-select {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border: var(--border-in-light);
|
border: var(--border-in-light);
|
||||||
|
@ -304,7 +310,7 @@
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
z-index: 999;
|
z-index: 999;
|
||||||
|
|
||||||
.selector-item-disabled{
|
.selector-item-disabled {
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -330,3 +336,4 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,8 +50,8 @@ export function Card(props: { children: JSX.Element[]; className?: string }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ListItem(props: {
|
export function ListItem(props: {
|
||||||
title: string;
|
title?: string;
|
||||||
subTitle?: string;
|
subTitle?: string | JSX.Element;
|
||||||
children?: JSX.Element | JSX.Element[];
|
children?: JSX.Element | JSX.Element[];
|
||||||
icon?: JSX.Element;
|
icon?: JSX.Element;
|
||||||
className?: string;
|
className?: string;
|
||||||
|
@ -265,9 +265,10 @@ export function Input(props: InputProps) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PasswordInput(props: HTMLProps<HTMLInputElement>) {
|
export function PasswordInput(
|
||||||
|
props: HTMLProps<HTMLInputElement> & { aria?: string },
|
||||||
|
) {
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
|
|
||||||
function changeVisibility() {
|
function changeVisibility() {
|
||||||
setVisible(!visible);
|
setVisible(!visible);
|
||||||
}
|
}
|
||||||
|
@ -275,6 +276,7 @@ export function PasswordInput(props: HTMLProps<HTMLInputElement>) {
|
||||||
return (
|
return (
|
||||||
<div className={"password-input-container"}>
|
<div className={"password-input-container"}>
|
||||||
<IconButton
|
<IconButton
|
||||||
|
aria={props.aria}
|
||||||
icon={visible ? <EyeIcon /> : <EyeOffIcon />}
|
icon={visible ? <EyeIcon /> : <EyeOffIcon />}
|
||||||
onClick={changeVisibility}
|
onClick={changeVisibility}
|
||||||
className={"password-eye"}
|
className={"password-eye"}
|
||||||
|
@ -290,13 +292,19 @@ export function PasswordInput(props: HTMLProps<HTMLInputElement>) {
|
||||||
|
|
||||||
export function Select(
|
export function Select(
|
||||||
props: React.DetailedHTMLProps<
|
props: React.DetailedHTMLProps<
|
||||||
React.SelectHTMLAttributes<HTMLSelectElement>,
|
React.SelectHTMLAttributes<HTMLSelectElement> & {
|
||||||
|
align?: "left" | "center";
|
||||||
|
},
|
||||||
HTMLSelectElement
|
HTMLSelectElement
|
||||||
>,
|
>,
|
||||||
) {
|
) {
|
||||||
const { className, children, ...otherProps } = props;
|
const { className, children, align, ...otherProps } = props;
|
||||||
return (
|
return (
|
||||||
<div className={`${styles["select-with-icon"]} ${className}`}>
|
<div
|
||||||
|
className={`${styles["select-with-icon"]} ${
|
||||||
|
align === "left" ? styles["left-align-option"] : ""
|
||||||
|
} ${className}`}
|
||||||
|
>
|
||||||
<select className={styles["select-with-icon-select"]} {...otherProps}>
|
<select className={styles["select-with-icon-select"]} {...otherProps}>
|
||||||
{children}
|
{children}
|
||||||
</select>
|
</select>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import md5 from "spark-md5";
|
import md5 from "spark-md5";
|
||||||
import { DEFAULT_MODELS } from "../constant";
|
import { DEFAULT_MODELS, DEFAULT_GA_ID } from "../constant";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
namespace NodeJS {
|
namespace NodeJS {
|
||||||
|
@ -66,6 +66,11 @@ declare global {
|
||||||
MOONSHOT_URL?: string;
|
MOONSHOT_URL?: string;
|
||||||
MOONSHOT_API_KEY?: string;
|
MOONSHOT_API_KEY?: string;
|
||||||
|
|
||||||
|
// iflytek only
|
||||||
|
IFLYTEK_URL?: string;
|
||||||
|
IFLYTEK_API_KEY?: string;
|
||||||
|
IFLYTEK_API_SECRET?: string;
|
||||||
|
|
||||||
// custom template for preprocessing user input
|
// custom template for preprocessing user input
|
||||||
DEFAULT_INPUT_TEMPLATE?: string;
|
DEFAULT_INPUT_TEMPLATE?: string;
|
||||||
}
|
}
|
||||||
|
@ -114,10 +119,19 @@ export const getServerSideConfig = () => {
|
||||||
|
|
||||||
if (disableGPT4) {
|
if (disableGPT4) {
|
||||||
if (customModels) customModels += ",";
|
if (customModels) customModels += ",";
|
||||||
customModels += DEFAULT_MODELS.filter((m) => m.name.startsWith("gpt-4"))
|
customModels += DEFAULT_MODELS.filter(
|
||||||
|
(m) =>
|
||||||
|
(m.name.startsWith("gpt-4") || m.name.startsWith("chatgpt-4o")) &&
|
||||||
|
!m.name.startsWith("gpt-4o-mini"),
|
||||||
|
)
|
||||||
.map((m) => "-" + m.name)
|
.map((m) => "-" + m.name)
|
||||||
.join(",");
|
.join(",");
|
||||||
if (defaultModel.startsWith("gpt-4")) defaultModel = "";
|
if (
|
||||||
|
(defaultModel.startsWith("gpt-4") ||
|
||||||
|
defaultModel.startsWith("chatgpt-4o")) &&
|
||||||
|
!defaultModel.startsWith("gpt-4o-mini")
|
||||||
|
)
|
||||||
|
defaultModel = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
const isStability = !!process.env.STABILITY_API_KEY;
|
const isStability = !!process.env.STABILITY_API_KEY;
|
||||||
|
@ -131,6 +145,7 @@ export const getServerSideConfig = () => {
|
||||||
const isBytedance = !!process.env.BYTEDANCE_API_KEY;
|
const isBytedance = !!process.env.BYTEDANCE_API_KEY;
|
||||||
const isAlibaba = !!process.env.ALIBABA_API_KEY;
|
const isAlibaba = !!process.env.ALIBABA_API_KEY;
|
||||||
const isMoonshot = !!process.env.MOONSHOT_API_KEY;
|
const isMoonshot = !!process.env.MOONSHOT_API_KEY;
|
||||||
|
const isIflytek = !!process.env.IFLYTEK_API_KEY;
|
||||||
// const apiKeyEnvVar = process.env.OPENAI_API_KEY ?? "";
|
// const apiKeyEnvVar = process.env.OPENAI_API_KEY ?? "";
|
||||||
// const apiKeys = apiKeyEnvVar.split(",").map((v) => v.trim());
|
// const apiKeys = apiKeyEnvVar.split(",").map((v) => v.trim());
|
||||||
// const randomIndex = Math.floor(Math.random() * apiKeys.length);
|
// const randomIndex = Math.floor(Math.random() * apiKeys.length);
|
||||||
|
@ -139,8 +154,8 @@ export const getServerSideConfig = () => {
|
||||||
// `[Server Config] using ${randomIndex + 1} of ${apiKeys.length} api key`,
|
// `[Server Config] using ${randomIndex + 1} of ${apiKeys.length} api key`,
|
||||||
// );
|
// );
|
||||||
|
|
||||||
const allowedWebDevEndpoints = (
|
const allowedWebDavEndpoints = (
|
||||||
process.env.WHITE_WEBDEV_ENDPOINTS ?? ""
|
process.env.WHITE_WEBDAV_ENDPOINTS ?? ""
|
||||||
).split(",");
|
).split(",");
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -188,12 +203,18 @@ export const getServerSideConfig = () => {
|
||||||
moonshotUrl: process.env.MOONSHOT_URL,
|
moonshotUrl: process.env.MOONSHOT_URL,
|
||||||
moonshotApiKey: getApiKey(process.env.MOONSHOT_API_KEY),
|
moonshotApiKey: getApiKey(process.env.MOONSHOT_API_KEY),
|
||||||
|
|
||||||
|
isIflytek,
|
||||||
|
iflytekUrl: process.env.IFLYTEK_URL,
|
||||||
|
iflytekApiKey: process.env.IFLYTEK_API_KEY,
|
||||||
|
iflytekApiSecret: process.env.IFLYTEK_API_SECRET,
|
||||||
|
|
||||||
cloudflareAccountId: process.env.CLOUDFLARE_ACCOUNT_ID,
|
cloudflareAccountId: process.env.CLOUDFLARE_ACCOUNT_ID,
|
||||||
cloudflareKVNamespaceId: process.env.CLOUDFLARE_KV_NAMESPACE_ID,
|
cloudflareKVNamespaceId: process.env.CLOUDFLARE_KV_NAMESPACE_ID,
|
||||||
cloudflareKVApiKey: getApiKey(process.env.CLOUDFLARE_KV_API_KEY),
|
cloudflareKVApiKey: getApiKey(process.env.CLOUDFLARE_KV_API_KEY),
|
||||||
cloudflareKVTTL: process.env.CLOUDFLARE_KV_TTL,
|
cloudflareKVTTL: process.env.CLOUDFLARE_KV_TTL,
|
||||||
|
|
||||||
gtmId: process.env.GTM_ID,
|
gtmId: process.env.GTM_ID,
|
||||||
|
gaId: process.env.GA_ID || DEFAULT_GA_ID,
|
||||||
|
|
||||||
needCode: ACCESS_CODES.size > 0,
|
needCode: ACCESS_CODES.size > 0,
|
||||||
code: process.env.CODE,
|
code: process.env.CODE,
|
||||||
|
@ -208,6 +229,6 @@ export const getServerSideConfig = () => {
|
||||||
disableFastLink: !!process.env.DISABLE_FAST_LINK,
|
disableFastLink: !!process.env.DISABLE_FAST_LINK,
|
||||||
customModels,
|
customModels,
|
||||||
defaultModel,
|
defaultModel,
|
||||||
allowedWebDevEndpoints,
|
allowedWebDavEndpoints,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
export const OWNER = "ChatGPTNextWeb";
|
export const OWNER = "ChatGPTNextWeb";
|
||||||
export const REPO = "ChatGPT-Next-Web";
|
export const REPO = "ChatGPT-Next-Web";
|
||||||
export const REPO_URL = `https://github.com/${OWNER}/${REPO}`;
|
export const REPO_URL = `https://github.com/${OWNER}/${REPO}`;
|
||||||
|
export const PLUGINS_REPO_URL = `https://github.com/${OWNER}/NextChat-Awesome-Plugins`;
|
||||||
export const ISSUE_URL = `https://github.com/${OWNER}/${REPO}/issues`;
|
export const ISSUE_URL = `https://github.com/${OWNER}/${REPO}/issues`;
|
||||||
export const UPDATE_URL = `${REPO_URL}#keep-updated`;
|
export const UPDATE_URL = `${REPO_URL}#keep-updated`;
|
||||||
export const RELEASE_URL = `${REPO_URL}/releases`;
|
export const RELEASE_URL = `${REPO_URL}/releases`;
|
||||||
|
@ -26,6 +27,7 @@ export const ALIBABA_BASE_URL = "https://dashscope.aliyuncs.com/api/";
|
||||||
export const TENCENT_BASE_URL = "https://hunyuan.tencentcloudapi.com";
|
export const TENCENT_BASE_URL = "https://hunyuan.tencentcloudapi.com";
|
||||||
|
|
||||||
export const MOONSHOT_BASE_URL = "https://api.moonshot.cn";
|
export const MOONSHOT_BASE_URL = "https://api.moonshot.cn";
|
||||||
|
export const IFLYTEK_BASE_URL = "https://spark-api-open.xf-yun.com";
|
||||||
|
|
||||||
export const CACHE_URL_PREFIX = "/api/cache";
|
export const CACHE_URL_PREFIX = "/api/cache";
|
||||||
export const UPLOAD_URL = `${CACHE_URL_PREFIX}/upload`;
|
export const UPLOAD_URL = `${CACHE_URL_PREFIX}/upload`;
|
||||||
|
@ -36,10 +38,12 @@ export enum Path {
|
||||||
Settings = "/settings",
|
Settings = "/settings",
|
||||||
NewChat = "/new-chat",
|
NewChat = "/new-chat",
|
||||||
Masks = "/masks",
|
Masks = "/masks",
|
||||||
|
Plugins = "/plugins",
|
||||||
Auth = "/auth",
|
Auth = "/auth",
|
||||||
Sd = "/sd",
|
Sd = "/sd",
|
||||||
SdNew = "/sd-new",
|
SdNew = "/sd-new",
|
||||||
Artifacts = "/artifacts",
|
Artifacts = "/artifacts",
|
||||||
|
SearchChat = "/search-chat",
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ApiPath {
|
export enum ApiPath {
|
||||||
|
@ -53,6 +57,7 @@ export enum ApiPath {
|
||||||
Alibaba = "/api/alibaba",
|
Alibaba = "/api/alibaba",
|
||||||
Tencent = "/api/tencent",
|
Tencent = "/api/tencent",
|
||||||
Moonshot = "/api/moonshot",
|
Moonshot = "/api/moonshot",
|
||||||
|
Iflytek = "/api/iflytek",
|
||||||
Stability = "/api/stability",
|
Stability = "/api/stability",
|
||||||
Artifacts = "/api/artifacts",
|
Artifacts = "/api/artifacts",
|
||||||
}
|
}
|
||||||
|
@ -67,12 +72,9 @@ export enum FileName {
|
||||||
Prompts = "prompts.json",
|
Prompts = "prompts.json",
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum Plugin {
|
|
||||||
Artifacts = "artifacts",
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum StoreKey {
|
export enum StoreKey {
|
||||||
Chat = "chat-next-web-store",
|
Chat = "chat-next-web-store",
|
||||||
|
Plugin = "chat-next-web-plugin",
|
||||||
Access = "access-control",
|
Access = "access-control",
|
||||||
Config = "app-config",
|
Config = "app-config",
|
||||||
Mask = "mask-store",
|
Mask = "mask-store",
|
||||||
|
@ -109,6 +111,7 @@ export enum ServiceProvider {
|
||||||
Tencent = "Tencent",
|
Tencent = "Tencent",
|
||||||
Moonshot = "Moonshot",
|
Moonshot = "Moonshot",
|
||||||
Stability = "Stability",
|
Stability = "Stability",
|
||||||
|
Iflytek = "Iflytek",
|
||||||
}
|
}
|
||||||
|
|
||||||
// Google API safety settings, see https://ai.google.dev/gemini-api/docs/safety-settings
|
// Google API safety settings, see https://ai.google.dev/gemini-api/docs/safety-settings
|
||||||
|
@ -130,6 +133,7 @@ export enum ModelProvider {
|
||||||
Qwen = "Qwen",
|
Qwen = "Qwen",
|
||||||
Hunyuan = "Hunyuan",
|
Hunyuan = "Hunyuan",
|
||||||
Moonshot = "Moonshot",
|
Moonshot = "Moonshot",
|
||||||
|
Iflytek = "Iflytek",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Stability = {
|
export const Stability = {
|
||||||
|
@ -146,6 +150,7 @@ export const Anthropic = {
|
||||||
|
|
||||||
export const OpenaiPath = {
|
export const OpenaiPath = {
|
||||||
ChatPath: "v1/chat/completions",
|
ChatPath: "v1/chat/completions",
|
||||||
|
SpeechPath: "v1/audio/speech",
|
||||||
ImagePath: "v1/images/generations",
|
ImagePath: "v1/images/generations",
|
||||||
UsagePath: "dashboard/billing/usage",
|
UsagePath: "dashboard/billing/usage",
|
||||||
SubsPath: "dashboard/billing/subscription",
|
SubsPath: "dashboard/billing/subscription",
|
||||||
|
@ -206,6 +211,11 @@ export const Moonshot = {
|
||||||
ChatPath: "v1/chat/completions",
|
ChatPath: "v1/chat/completions",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const Iflytek = {
|
||||||
|
ExampleEndpoint: IFLYTEK_BASE_URL,
|
||||||
|
ChatPath: "v1/chat/completions",
|
||||||
|
};
|
||||||
|
|
||||||
export const DEFAULT_INPUT_TEMPLATE = `{{input}}`; // input / time / model / lang
|
export const DEFAULT_INPUT_TEMPLATE = `{{input}}`; // input / time / model / lang
|
||||||
// export const DEFAULT_SYSTEM_TEMPLATE = `
|
// export const DEFAULT_SYSTEM_TEMPLATE = `
|
||||||
// You are ChatGPT, a large language model trained by {{ServiceProvider}}.
|
// You are ChatGPT, a large language model trained by {{ServiceProvider}}.
|
||||||
|
@ -234,15 +244,33 @@ export const KnowledgeCutOffDate: Record<string, string> = {
|
||||||
"gpt-4-turbo-preview": "2023-12",
|
"gpt-4-turbo-preview": "2023-12",
|
||||||
"gpt-4o": "2023-10",
|
"gpt-4o": "2023-10",
|
||||||
"gpt-4o-2024-05-13": "2023-10",
|
"gpt-4o-2024-05-13": "2023-10",
|
||||||
|
"gpt-4o-2024-08-06": "2023-10",
|
||||||
|
"chatgpt-4o-latest": "2023-10",
|
||||||
"gpt-4o-mini": "2023-10",
|
"gpt-4o-mini": "2023-10",
|
||||||
"gpt-4o-mini-2024-07-18": "2023-10",
|
"gpt-4o-mini-2024-07-18": "2023-10",
|
||||||
"gpt-4-vision-preview": "2023-04",
|
"gpt-4-vision-preview": "2023-04",
|
||||||
|
"o1-mini": "2023-10",
|
||||||
|
"o1-preview": "2023-10",
|
||||||
// After improvements,
|
// After improvements,
|
||||||
// it's now easier to add "KnowledgeCutOffDate" instead of stupid hardcoding it, as was done previously.
|
// it's now easier to add "KnowledgeCutOffDate" instead of stupid hardcoding it, as was done previously.
|
||||||
"gemini-pro": "2023-12",
|
"gemini-pro": "2023-12",
|
||||||
"gemini-pro-vision": "2023-12",
|
"gemini-pro-vision": "2023-12",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const DEFAULT_TTS_ENGINE = "OpenAI-TTS";
|
||||||
|
export const DEFAULT_TTS_ENGINES = ["OpenAI-TTS", "Edge-TTS"];
|
||||||
|
export const DEFAULT_TTS_MODEL = "tts-1";
|
||||||
|
export const DEFAULT_TTS_VOICE = "alloy";
|
||||||
|
export const DEFAULT_TTS_MODELS = ["tts-1", "tts-1-hd"];
|
||||||
|
export const DEFAULT_TTS_VOICES = [
|
||||||
|
"alloy",
|
||||||
|
"echo",
|
||||||
|
"fable",
|
||||||
|
"onyx",
|
||||||
|
"nova",
|
||||||
|
"shimmer",
|
||||||
|
];
|
||||||
|
|
||||||
const openaiModels = [
|
const openaiModels = [
|
||||||
"gpt-3.5-turbo",
|
"gpt-3.5-turbo",
|
||||||
"gpt-3.5-turbo-1106",
|
"gpt-3.5-turbo-1106",
|
||||||
|
@ -255,12 +283,16 @@ const openaiModels = [
|
||||||
"gpt-4-turbo-preview",
|
"gpt-4-turbo-preview",
|
||||||
"gpt-4o",
|
"gpt-4o",
|
||||||
"gpt-4o-2024-05-13",
|
"gpt-4o-2024-05-13",
|
||||||
|
"gpt-4o-2024-08-06",
|
||||||
|
"chatgpt-4o-latest",
|
||||||
"gpt-4o-mini",
|
"gpt-4o-mini",
|
||||||
"gpt-4o-mini-2024-07-18",
|
"gpt-4o-mini-2024-07-18",
|
||||||
"gpt-4-vision-preview",
|
"gpt-4-vision-preview",
|
||||||
"gpt-4-turbo-2024-04-09",
|
"gpt-4-turbo-2024-04-09",
|
||||||
"gpt-4-1106-preview",
|
"gpt-4-1106-preview",
|
||||||
"dall-e-3",
|
"dall-e-3",
|
||||||
|
"o1-mini",
|
||||||
|
"o1-preview",
|
||||||
];
|
];
|
||||||
|
|
||||||
const googleModels = [
|
const googleModels = [
|
||||||
|
@ -325,6 +357,14 @@ const tencentModels = [
|
||||||
|
|
||||||
const moonshotModes = ["moonshot-v1-8k", "moonshot-v1-32k", "moonshot-v1-128k"];
|
const moonshotModes = ["moonshot-v1-8k", "moonshot-v1-32k", "moonshot-v1-128k"];
|
||||||
|
|
||||||
|
const iflytekModels = [
|
||||||
|
"general",
|
||||||
|
"generalv3",
|
||||||
|
"pro-128k",
|
||||||
|
"generalv3.5",
|
||||||
|
"4.0Ultra",
|
||||||
|
];
|
||||||
|
|
||||||
let seq = 1000; // 内置的模型序号生成器从1000开始
|
let seq = 1000; // 内置的模型序号生成器从1000开始
|
||||||
export const DEFAULT_MODELS = [
|
export const DEFAULT_MODELS = [
|
||||||
...openaiModels.map((name) => ({
|
...openaiModels.map((name) => ({
|
||||||
|
@ -426,6 +466,17 @@ export const DEFAULT_MODELS = [
|
||||||
sorted: 9,
|
sorted: 9,
|
||||||
},
|
},
|
||||||
})),
|
})),
|
||||||
|
...iflytekModels.map((name) => ({
|
||||||
|
name,
|
||||||
|
available: true,
|
||||||
|
sorted: seq++,
|
||||||
|
provider: {
|
||||||
|
id: "iflytek",
|
||||||
|
providerName: "Iflytek",
|
||||||
|
providerType: "iflytek",
|
||||||
|
sorted: 10,
|
||||||
|
},
|
||||||
|
})),
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export const CHAT_PAGE_SIZE = 15;
|
export const CHAT_PAGE_SIZE = 15;
|
||||||
|
@ -444,4 +495,12 @@ export const internalAllowedWebDavEndpoints = [
|
||||||
"https://app.koofr.net/dav/Koofr",
|
"https://app.koofr.net/dav/Koofr",
|
||||||
];
|
];
|
||||||
|
|
||||||
export const PLUGINS = [{ name: "Stable Diffusion", path: Path.Sd }];
|
export const DEFAULT_GA_ID = "G-89WN60ZK2E";
|
||||||
|
export const PLUGINS = [
|
||||||
|
{ name: "Plugins", path: Path.Plugins },
|
||||||
|
{ name: "Stable Diffusion", path: Path.Sd },
|
||||||
|
{ name: "Search Chat", path: Path.SearchChat },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const SAAS_CHAT_URL = "https://nextchat.dev/chat";
|
||||||
|
export const SAAS_CHAT_UTM_URL = "https://nextchat.dev/chat?utm=github";
|
||||||
|
|
|
@ -21,10 +21,16 @@ declare interface Window {
|
||||||
writeBinaryFile(path: string, data: Uint8Array): Promise<void>;
|
writeBinaryFile(path: string, data: Uint8Array): Promise<void>;
|
||||||
writeTextFile(path: string, data: string): Promise<void>;
|
writeTextFile(path: string, data: string): Promise<void>;
|
||||||
};
|
};
|
||||||
notification:{
|
notification: {
|
||||||
requestPermission(): Promise<Permission>;
|
requestPermission(): Promise<Permission>;
|
||||||
isPermissionGranted(): Promise<boolean>;
|
isPermissionGranted(): Promise<boolean>;
|
||||||
sendNotification(options: string | Options): void;
|
sendNotification(options: string | Options): void;
|
||||||
};
|
};
|
||||||
|
http: {
|
||||||
|
fetch<T>(
|
||||||
|
url: string,
|
||||||
|
options?: Record<string, unknown>,
|
||||||
|
): Promise<Response<T>>;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
<svg class="icon--SJP_d" width="16" height="16" fill="none" viewBox="0 0 16 16" style="min-width: 16px; min-height: 16px;"><g><path data-follow-fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M5.248 14.444a.625.625 0 0 1-.005-.884l5.068-5.12a.625.625 0 0 0 0-.88L5.243 2.44a.625.625 0 1 1 .889-.88l5.067 5.121c.723.73.723 1.907 0 2.638l-5.067 5.12a.625.625 0 0 1-.884.005Z" fill="currentColor"></path></g></svg>
|
After Width: | Height: | Size: 426 B |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><path fill="currentColor" d="M12.832 21.801c3.126-.626 7.168-2.875 7.168-8.69c0-5.291-3.873-8.815-6.658-10.434c-.619-.36-1.342.113-1.342.828v1.828c0 1.442-.606 4.074-2.29 5.169c-.86.559-1.79-.278-1.894-1.298l-.086-.838c-.1-.974-1.092-1.565-1.87-.971C4.461 8.46 3 10.33 3 13.11C3 20.221 8.289 22 10.933 22q.232 0 .484-.015C10.111 21.874 8 21.064 8 18.444c0-2.05 1.495-3.435 2.631-4.11c.306-.18.663.055.663.41v.59c0 .45.175 1.155.59 1.637c.47.546 1.159-.026 1.214-.744c.018-.226.246-.37.442-.256c.641.375 1.46 1.175 1.46 2.473c0 2.048-1.129 2.99-2.168 3.357"/></svg>
|
After Width: | Height: | Size: 648 B |
|
@ -0,0 +1,4 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#333" class="bi bi-badge-hd" viewBox="0 0 16 16">
|
||||||
|
<path d="M7.396 11V5.001H6.209v2.44H3.687V5H2.5v6h1.187V8.43h2.522V11zM8.5 5.001V11h2.188c1.811 0 2.685-1.107 2.685-3.015 0-1.894-.86-2.984-2.684-2.984zm1.187.967h.843c1.112 0 1.622.686 1.622 2.04 0 1.353-.505 2.02-1.622 2.02h-.843z"/>
|
||||||
|
<path d="M14 3a1 1 0 0 1 1 1v8a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1zM2 2a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 514 B |
|
@ -0,0 +1,19 @@
|
||||||
|
<svg width="38.73" height="42" viewBox="0 0 221 240" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect x="160.697" y="38.125" width="65.007" height="145.932" rx="32.503" transform="rotate(21.987 160.697 38.125)" fill="url(#logo_svg__a)"></rect>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="m48.642 79.125-25.92 71.213c-6.139 16.869 2.558 35.52 19.427 41.66 16.868 6.14 35.52-2.558 41.66-19.426L94.23 143.94l-36.658-37.439a32.42 32.42 0 0 1-9.244-23.497c.033-1.326.14-2.62.314-3.879Z" fill="url(#logo_svg__b)"></path>
|
||||||
|
<path d="M172.578 132.787a32.765 32.765 0 0 1 8.981 24.238c-1.458 28.748-36.622 41.778-56.46 20.92l-67.644-71.122a32.763 32.763 0 0 1-8.981-24.238c1.457-28.748 36.622-41.778 56.46-20.92l67.644 71.122Z" fill="url(#logo_svg__c)" fill-opacity="0.96"></path>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="logo_svg__a" x1="215.063" y1="59.628" x2="160.714" y2="157.96" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#3EADFE"></stop>
|
||||||
|
<stop offset="1" stop-color="#2A7AFF"></stop>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="logo_svg__b" x1="105.376" y1="84.416" x2="19.745" y2="131.163" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#01B3FF"></stop>
|
||||||
|
<stop offset="1" stop-color="#59ECFA"></stop>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="logo_svg__c" x1="102.734" y1="136.396" x2="192.577" y2="155.859" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#023BFF" stop-opacity="0.82"></stop>
|
||||||
|
<stop offset="0.88" stop-color="#2D86FF" stop-opacity="0.76"></stop>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
|
@ -0,0 +1,4 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#333" class="bi bi-palette" viewBox="0 0 16 16">
|
||||||
|
<path d="M8 5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3m4 3a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3M5.5 7a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0m.5 6a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3"/>
|
||||||
|
<path d="M16 8c0 3.15-1.866 2.585-3.567 2.07C11.42 9.763 10.465 9.473 10 10c-.603.683-.475 1.819-.351 2.92C9.826 14.495 9.996 16 8 16a8 8 0 1 1 8-8m-8 7c.611 0 .654-.171.655-.176.078-.146.124-.464.07-1.119-.014-.168-.037-.37-.061-.591-.052-.464-.112-1.005-.118-1.462-.01-.707.083-1.61.704-2.314.369-.417.845-.578 1.272-.618.404-.038.812.026 1.16.104.343.077.702.186 1.025.284l.028.008c.346.105.658.199.953.266.653.148.904.083.991.024C14.717 9.38 15 9.161 15 8a7 7 0 1 0-7 7"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 781 B |
|
@ -0,0 +1 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?><svg width="16" height="16" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M42 7H6C4.89543 7 4 7.89543 4 9V37C4 38.1046 4.89543 39 6 39H42C43.1046 39 44 38.1046 44 37V9C44 7.89543 43.1046 7 42 7Z" fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"/><path d="M12 19H14" stroke="#000" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M21 19H23" stroke="#000" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M29 19H36" stroke="#000" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M12 28H36" stroke="#000" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
After Width: | Height: | Size: 734 B |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" width="16" height="16" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4"><path stroke-linecap="round" stroke-linejoin="round" d="M17.25 9.75 19.5 12m0 0 2.25 2.25M19.5 12l2.25-2.25M19.5 12l-2.25 2.25m-10.5-6 4.72-4.72a.75.75 0 0 1 1.28.53v15.88a.75.75 0 0 1-1.28.53l-4.72-4.72H4.51c-.88 0-1.704-.507-1.938-1.354A9.009 9.009 0 0 1 2.25 12c0-.83.112-1.633.322-2.396C2.806 8.756 3.63 8.25 4.51 8.25H6.75Z"></path></svg>
|
After Width: | Height: | Size: 495 B |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" width="16" height="16" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4"><path stroke-linecap="round" stroke-linejoin="round" d="M19.114 5.636a9 9 0 010 12.728M16.463 8.288a5.25 5.25 0 010 7.424M6.75 8.25l4.72-4.72a.75.75 0 011.28.53v15.88a.75.75 0 01-1.28.53l-4.72-4.72H4.51c-.88 0-1.704-.507-1.938-1.354A9.01 9.01 0 012.25 12c0-.83.112-1.633.322-2.396C2.806 8.756 3.63 8.25 4.51 8.25H6.75z"></path></svg>
|
After Width: | Height: | Size: 485 B |
|
@ -0,0 +1,16 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" fill="none" viewBox="0 0 20 20">
|
||||||
|
<defs>
|
||||||
|
<rect id="path_0" width="20" height="20" x="0" y="0" />
|
||||||
|
</defs>
|
||||||
|
<g opacity="1" transform="translate(0 0) rotate(0 8 8)">
|
||||||
|
<mask id="bg-mask-0" fill="#fff">
|
||||||
|
<use xlink:href="#path_0" />
|
||||||
|
</mask>
|
||||||
|
<g mask="url(#bg-mask-0)">
|
||||||
|
<path d="M7 4a3 3 0 016 0v6a3 3 0 11-6 0V4z" fill="#333333">
|
||||||
|
</path>
|
||||||
|
<path d="M5.5 9.643a.75.75 0 00-1.5 0V10c0 3.06 2.29 5.585 5.25 5.954V17.5h-1.5a.75.75 0 000 1.5h4.5a.75.75 0 000-1.5h-1.5v-1.546A6.001 6.001 0 0016 10v-.357a.75.75 0 00-1.5 0V10a4.5 4.5 0 01-9 0v-.357z" fill="#333333">
|
||||||
|
</path>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 708 B |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1.2rem" height="1.2rem" viewBox="0 0 24 24"><g fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></g></svg>
|
After Width: | Height: | Size: 285 B |
|
@ -6,7 +6,7 @@ import { getClientConfig } from "./config/client";
|
||||||
import type { Metadata, Viewport } from "next";
|
import type { Metadata, Viewport } from "next";
|
||||||
import { SpeedInsights } from "@vercel/speed-insights/next";
|
import { SpeedInsights } from "@vercel/speed-insights/next";
|
||||||
import { getServerSideConfig } from "./config/server";
|
import { getServerSideConfig } from "./config/server";
|
||||||
import { GoogleTagManager } from "@next/third-parties/google";
|
import { GoogleTagManager, GoogleAnalytics } from "@next/third-parties/google";
|
||||||
const serverConfig = getServerSideConfig();
|
const serverConfig = getServerSideConfig();
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
|
@ -41,7 +41,11 @@ export default function RootLayout({
|
||||||
name="viewport"
|
name="viewport"
|
||||||
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
|
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
|
||||||
/>
|
/>
|
||||||
<link rel="manifest" href="/site.webmanifest"></link>
|
<link
|
||||||
|
rel="manifest"
|
||||||
|
href="/site.webmanifest"
|
||||||
|
crossOrigin="use-credentials"
|
||||||
|
></link>
|
||||||
<script src="/serviceWorkerRegister.js" defer></script>
|
<script src="/serviceWorkerRegister.js" defer></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
@ -56,6 +60,11 @@ export default function RootLayout({
|
||||||
<GoogleTagManager gtmId={serverConfig.gtmId} />
|
<GoogleTagManager gtmId={serverConfig.gtmId} />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
{serverConfig?.gaId && (
|
||||||
|
<>
|
||||||
|
<GoogleAnalytics gaId={serverConfig.gaId} />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,115 +1,169 @@
|
||||||
import { SubmitKey } from "../store/config";
|
import { SubmitKey } from "../store/config";
|
||||||
import type { PartialLocaleType } from "./index";
|
import type { PartialLocaleType } from "./index";
|
||||||
|
import { getClientConfig } from "../config/client";
|
||||||
|
import { SAAS_CHAT_UTM_URL } from "@/app/constant";
|
||||||
|
const isApp = !!getClientConfig()?.isApp;
|
||||||
|
|
||||||
const ar: PartialLocaleType = {
|
const ar: PartialLocaleType = {
|
||||||
WIP: "قريبًا...",
|
WIP: "قريبًا...",
|
||||||
Error: {
|
Error: {
|
||||||
Unauthorized:
|
Unauthorized: isApp
|
||||||
"غير مصرح بالوصول، يرجى إدخال رمز الوصول [auth](/#/auth) في صفحة المصادقة.",
|
? `😆 واجهت المحادثة بعض المشكلات، لا داعي للقلق:
|
||||||
|
\\ 1️⃣ إذا كنت ترغب في تجربة دون إعداد، [انقر هنا لبدء المحادثة فورًا 🚀](${SAAS_CHAT_UTM_URL})
|
||||||
|
\\ 2️⃣ إذا كنت تريد استخدام موارد OpenAI الخاصة بك، انقر [هنا](/#/settings) لتعديل الإعدادات ⚙️`
|
||||||
|
: `😆 واجهت المحادثة بعض المشكلات، لا داعي للقلق:
|
||||||
|
\ 1️⃣ إذا كنت ترغب في تجربة دون إعداد، [انقر هنا لبدء المحادثة فورًا 🚀](${SAAS_CHAT_UTM_URL})
|
||||||
|
\ 2️⃣ إذا كنت تستخدم إصدار النشر الخاص، انقر [هنا](/#/auth) لإدخال مفتاح الوصول 🔑
|
||||||
|
\ 3️⃣ إذا كنت تريد استخدام موارد OpenAI الخاصة بك، انقر [هنا](/#/settings) لتعديل الإعدادات ⚙️
|
||||||
|
`,
|
||||||
},
|
},
|
||||||
Auth: {
|
Auth: {
|
||||||
Title: "تحتاج إلى رمز الوصول",
|
Title: "تحتاج إلى كلمة مرور",
|
||||||
Tips: "يرجى إدخال رمز الوصول أدناه",
|
Tips: "قام المشرف بتفعيل التحقق بكلمة المرور، يرجى إدخال رمز الوصول أدناه",
|
||||||
SubTips: "أو أدخل مفتاح واجهة برمجة تطبيقات OpenAI الخاص بك",
|
SubTips: "أو إدخال مفتاح API الخاص بـ OpenAI أو Google",
|
||||||
Input: "رمز الوصول",
|
Input: "أدخل رمز الوصول هنا",
|
||||||
Confirm: "تأكيد",
|
Confirm: "تأكيد",
|
||||||
Later: "لاحقًا",
|
Later: "في وقت لاحق",
|
||||||
|
Return: "عودة",
|
||||||
|
SaasTips: "الإعدادات معقدة، أريد استخدامه على الفور",
|
||||||
|
TopTips:
|
||||||
|
"🥳 عرض NextChat AI الأول، افتح الآن OpenAI o1, GPT-4o, Claude-3.5 وأحدث النماذج الكبيرة",
|
||||||
},
|
},
|
||||||
ChatItem: {
|
ChatItem: {
|
||||||
ChatItemCount: (count: number) => `${count} رسائل`,
|
ChatItemCount: (count: number) => `${count} محادثة`,
|
||||||
},
|
},
|
||||||
Chat: {
|
Chat: {
|
||||||
SubTitle: (count: number) => ` ${count} رسائل مع ChatGPT`,
|
SubTitle: (count: number) => `إجمالي ${count} محادثة`,
|
||||||
|
EditMessage: {
|
||||||
|
Title: "تحرير سجل الرسائل",
|
||||||
|
Topic: {
|
||||||
|
Title: "موضوع الدردشة",
|
||||||
|
SubTitle: "تغيير موضوع الدردشة الحالي",
|
||||||
|
},
|
||||||
|
},
|
||||||
Actions: {
|
Actions: {
|
||||||
ChatList: "الانتقال إلى قائمة الدردشة",
|
ChatList: "عرض قائمة الرسائل",
|
||||||
CompressedHistory: "ملخص ضغط ذاكرة التاريخ",
|
CompressedHistory: "عرض التاريخ المضغوط",
|
||||||
Export: "تصدير جميع الرسائل كـ Markdown",
|
Export: "تصدير سجل الدردشة",
|
||||||
Copy: "نسخ",
|
Copy: "نسخ",
|
||||||
Stop: "توقف",
|
Stop: "إيقاف",
|
||||||
Retry: "إعادة المحاولة",
|
Retry: "إعادة المحاولة",
|
||||||
|
Pin: "تثبيت",
|
||||||
|
PinToastContent: "تم تثبيت 1 محادثة في الإشعارات المسبقة",
|
||||||
|
PinToastAction: "عرض",
|
||||||
Delete: "حذف",
|
Delete: "حذف",
|
||||||
|
Edit: "تحرير",
|
||||||
|
RefreshTitle: "تحديث العنوان",
|
||||||
|
RefreshToast: "تم إرسال طلب تحديث العنوان",
|
||||||
|
},
|
||||||
|
Commands: {
|
||||||
|
new: "دردشة جديدة",
|
||||||
|
newm: "إنشاء دردشة من القناع",
|
||||||
|
next: "الدردشة التالية",
|
||||||
|
prev: "الدردشة السابقة",
|
||||||
|
clear: "مسح السياق",
|
||||||
|
del: "حذف الدردشة",
|
||||||
},
|
},
|
||||||
InputActions: {
|
InputActions: {
|
||||||
Stop: "توقف",
|
Stop: "إيقاف الاستجابة",
|
||||||
ToBottom: "إلى آخر",
|
ToBottom: "الانتقال إلى الأحدث",
|
||||||
Theme: {
|
Theme: {
|
||||||
auto: "تلقائي",
|
auto: "موضوع تلقائي",
|
||||||
light: "نمط فاتح",
|
light: "الوضع الفاتح",
|
||||||
dark: "نمط داكن",
|
dark: "الوضع الداكن",
|
||||||
},
|
},
|
||||||
Prompt: "الاقتراحات",
|
Prompt: "الأوامر السريعة",
|
||||||
Masks: "الأقنعة",
|
Masks: "جميع الأقنعة",
|
||||||
Clear: "مسح السياق",
|
Clear: "مسح الدردشة",
|
||||||
Settings: "الإعدادات",
|
Settings: "إعدادات الدردشة",
|
||||||
|
UploadImage: "تحميل صورة",
|
||||||
},
|
},
|
||||||
Rename: "إعادة تسمية الدردشة",
|
Rename: "إعادة تسمية الدردشة",
|
||||||
Typing: "كتابة...",
|
Typing: "يكتب…",
|
||||||
Input: (submitKey: string) => {
|
Input: (submitKey: string) => {
|
||||||
var inputHints = ` اضغط على ${submitKey} للإرسال`;
|
var inputHints = `${submitKey} إرسال`;
|
||||||
if (submitKey === String(SubmitKey.Enter)) {
|
if (submitKey === String(SubmitKey.Enter)) {
|
||||||
inputHints += "، Shift + Enter للإنشاء";
|
inputHints += "، Shift + Enter لإدراج سطر جديد";
|
||||||
}
|
}
|
||||||
return inputHints + "، / للبحث في الاقتراحات";
|
return inputHints + "، / لتفعيل الإكمال التلقائي، : لتفعيل الأوامر";
|
||||||
},
|
},
|
||||||
Send: "إرسال",
|
Send: "إرسال",
|
||||||
Config: {
|
Config: {
|
||||||
Reset: "إعادة التعيين إلى الإعدادات الافتراضية",
|
Reset: "مسح الذاكرة",
|
||||||
SaveAs: "حفظ كأقنعة",
|
SaveAs: "حفظ كقناع",
|
||||||
},
|
},
|
||||||
|
IsContext: "الإشعارات المسبقة",
|
||||||
},
|
},
|
||||||
Export: {
|
Export: {
|
||||||
Title: "تصدير الرسائل",
|
Title: "مشاركة سجل الدردشة",
|
||||||
Copy: "نسخ الكل",
|
Copy: "نسخ الكل",
|
||||||
Download: "تنزيل",
|
Download: "تحميل الملف",
|
||||||
MessageFromYou: "رسالة منك",
|
|
||||||
MessageFromChatGPT: "رسالة من ChatGPT",
|
|
||||||
Share: "مشاركة على ShareGPT",
|
Share: "مشاركة على ShareGPT",
|
||||||
|
MessageFromYou: "المستخدم",
|
||||||
|
MessageFromChatGPT: "ChatGPT",
|
||||||
Format: {
|
Format: {
|
||||||
Title: "صيغة التصدير",
|
Title: "تنسيق التصدير",
|
||||||
SubTitle: "Markdown أو صورة PNG",
|
SubTitle: "يمكنك تصدير النص كـ Markdown أو صورة PNG",
|
||||||
},
|
},
|
||||||
IncludeContext: {
|
IncludeContext: {
|
||||||
Title: "تضمين السياق",
|
Title: "تضمين سياق القناع",
|
||||||
SubTitle: "تصدير اقتراحات السياق في الأقنعة أم لا",
|
SubTitle: "هل تريد عرض سياق القناع في الرسائل",
|
||||||
},
|
},
|
||||||
Steps: {
|
Steps: {
|
||||||
Select: "تحديد",
|
Select: "اختيار",
|
||||||
Preview: "معاينة",
|
Preview: "معاينة",
|
||||||
},
|
},
|
||||||
|
Image: {
|
||||||
|
Toast: "يتم إنشاء لقطة الشاشة",
|
||||||
|
Modal: "اضغط مطولاً أو انقر بزر الماوس الأيمن لحفظ الصورة",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Select: {
|
Select: {
|
||||||
Search: "بحث",
|
Search: "بحث في الرسائل",
|
||||||
All: "تحديد الكل",
|
All: "تحديد الكل",
|
||||||
Latest: "تحديد أحدث",
|
Latest: "أحدث الرسائل",
|
||||||
Clear: "مسح",
|
Clear: "مسح التحديد",
|
||||||
},
|
},
|
||||||
Memory: {
|
Memory: {
|
||||||
Title: "اقتراحات الذاكرة",
|
Title: "ملخص التاريخ",
|
||||||
EmptyContent: "لا شيء حتى الآن.",
|
EmptyContent: "محتوى المحادثة قصير جداً، لا حاجة للتلخيص",
|
||||||
Send: "إرسال الذاكرة",
|
Send: "ضغط تلقائي لسجل الدردشة كـ سياق",
|
||||||
Copy: "نسخ الذاكرة",
|
Copy: "نسخ الملخص",
|
||||||
Reset: "إعادة التعيين",
|
Reset: "[غير مستخدم]",
|
||||||
ResetConfirm:
|
ResetConfirm: "تأكيد مسح ملخص التاريخ؟",
|
||||||
"سيؤدي إعادة التعيين إلى مسح سجل المحادثة الحالي والذاكرة التاريخية. هل أنت متأكد أنك تريد الاستمرار؟",
|
|
||||||
},
|
},
|
||||||
Home: {
|
Home: {
|
||||||
NewChat: "دردشة جديدة",
|
NewChat: "دردشة جديدة",
|
||||||
DeleteChat: "هل تريد تأكيد حذف المحادثة المحددة؟",
|
DeleteChat: "تأكيد حذف المحادثة المحددة؟",
|
||||||
DeleteToast: "تم حذف الدردشة",
|
DeleteToast: "تم حذف المحادثة",
|
||||||
Revert: "التراجع",
|
Revert: "تراجع",
|
||||||
},
|
},
|
||||||
Settings: {
|
Settings: {
|
||||||
Title: "الإعدادات",
|
Title: "الإعدادات",
|
||||||
SubTitle: "جميع الإعدادات",
|
SubTitle: "جميع خيارات الإعدادات",
|
||||||
|
|
||||||
Lang: {
|
Danger: {
|
||||||
Name: "Language", // تنبيه: إذا كنت ترغب في إضافة ترجمة جديدة، يرجى عدم ترجمة هذه القيمة وتركها "Language"
|
Reset: {
|
||||||
All: "كل اللغات",
|
Title: "إعادة تعيين جميع الإعدادات",
|
||||||
|
SubTitle: "إعادة تعيين جميع عناصر الإعدادات إلى القيم الافتراضية",
|
||||||
|
Action: "إعادة التعيين الآن",
|
||||||
|
Confirm: "تأكيد إعادة تعيين جميع الإعدادات؟",
|
||||||
},
|
},
|
||||||
Avatar: "الصورة الرمزية",
|
Clear: {
|
||||||
|
Title: "مسح جميع البيانات",
|
||||||
|
SubTitle: "مسح جميع الدردشات وبيانات الإعدادات",
|
||||||
|
Action: "مسح الآن",
|
||||||
|
Confirm: "تأكيد مسح جميع الدردشات وبيانات الإعدادات؟",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Lang: {
|
||||||
|
Name: "Language", // انتبه: إذا كنت ترغب في إضافة ترجمة جديدة، يرجى عدم ترجمة هذه القيمة، اتركها كما هي "Language"
|
||||||
|
All: "جميع اللغات",
|
||||||
|
},
|
||||||
|
Avatar: "الصورة الشخصية",
|
||||||
FontSize: {
|
FontSize: {
|
||||||
Title: "حجم الخط",
|
Title: "حجم الخط",
|
||||||
SubTitle: "ضبط حجم الخط لمحتوى الدردشة",
|
SubTitle: "حجم الخط في محتوى الدردشة",
|
||||||
},
|
},
|
||||||
FontFamily: {
|
FontFamily: {
|
||||||
Title: "خط الدردشة",
|
Title: "خط الدردشة",
|
||||||
|
@ -117,163 +171,388 @@ const ar: PartialLocaleType = {
|
||||||
Placeholder: "اسم الخط",
|
Placeholder: "اسم الخط",
|
||||||
},
|
},
|
||||||
InjectSystemPrompts: {
|
InjectSystemPrompts: {
|
||||||
Title: "حقن تلميحات النظام",
|
Title: "حقن الرسائل النصية النظامية",
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"قم بإضافة تلميحة نظام محاكاة ChatGPT إلى بداية قائمة الرسائل المُطلَبة في كل طلب",
|
"فرض إضافة رسالة نظامية تحاكي ChatGPT في بداية قائمة الرسائل لكل طلب",
|
||||||
},
|
},
|
||||||
InputTemplate: {
|
InputTemplate: {
|
||||||
Title: "نموذج الإدخال",
|
Title: "معالجة الإدخال من قبل المستخدم",
|
||||||
SubTitle: "سيتم ملء أحدث رسالة في هذا النموذج",
|
SubTitle: "سيتم ملء آخر رسالة من المستخدم في هذا القالب",
|
||||||
},
|
},
|
||||||
|
|
||||||
Update: {
|
Update: {
|
||||||
Version: (x: string) => ` الإصدار: ${x}`,
|
Version: (x: string) => `الإصدار الحالي: ${x}`,
|
||||||
IsLatest: "أحدث إصدار",
|
IsLatest: "أنت على أحدث إصدار",
|
||||||
CheckUpdate: "التحقق من التحديث",
|
CheckUpdate: "التحقق من التحديثات",
|
||||||
IsChecking: "جارٍ التحقق من التحديث...",
|
IsChecking: "جارٍ التحقق من التحديثات...",
|
||||||
FoundUpdate: (x: string) => ` تم العثور على إصدار جديد: ${x}`,
|
FoundUpdate: (x: string) => `تم العثور على إصدار جديد: ${x}`,
|
||||||
GoToUpdate: "التحديث",
|
GoToUpdate: "انتقل للتحديث",
|
||||||
},
|
},
|
||||||
SendKey: "مفتاح الإرسال",
|
SendKey: "زر الإرسال",
|
||||||
Theme: "السمة",
|
Theme: "السمة",
|
||||||
TightBorder: "حدود ضيقة",
|
TightBorder: "وضع بدون حدود",
|
||||||
SendPreviewBubble: {
|
SendPreviewBubble: {
|
||||||
Title: "عرض معاينة الـ Send",
|
Title: "فقاعة المعاينة",
|
||||||
SubTitle: "معاينة Markdown في فقاعة",
|
SubTitle: "معاينة محتوى Markdown في فقاعة المعاينة",
|
||||||
|
},
|
||||||
|
AutoGenerateTitle: {
|
||||||
|
Title: "توليد العنوان تلقائيًا",
|
||||||
|
SubTitle: "توليد عنوان مناسب بناءً على محتوى الدردشة",
|
||||||
|
},
|
||||||
|
Sync: {
|
||||||
|
CloudState: "بيانات السحابة",
|
||||||
|
NotSyncYet: "لم يتم التزامن بعد",
|
||||||
|
Success: "تم التزامن بنجاح",
|
||||||
|
Fail: "فشل التزامن",
|
||||||
|
|
||||||
|
Config: {
|
||||||
|
Modal: {
|
||||||
|
Title: "تكوين التزامن السحابي",
|
||||||
|
Check: "التحقق من التوفر",
|
||||||
|
},
|
||||||
|
SyncType: {
|
||||||
|
Title: "نوع التزامن",
|
||||||
|
SubTitle: "اختر خادم التزامن المفضل",
|
||||||
|
},
|
||||||
|
Proxy: {
|
||||||
|
Title: "تفعيل الوكيل",
|
||||||
|
SubTitle: "يجب تفعيل الوكيل عند التزامن عبر المتصفح لتجنب قيود CORS",
|
||||||
|
},
|
||||||
|
ProxyUrl: {
|
||||||
|
Title: "عنوان الوكيل",
|
||||||
|
SubTitle: "ينطبق فقط على الوكيل المتاح في هذا المشروع",
|
||||||
|
},
|
||||||
|
|
||||||
|
WebDav: {
|
||||||
|
Endpoint: "عنوان WebDAV",
|
||||||
|
UserName: "اسم المستخدم",
|
||||||
|
Password: "كلمة المرور",
|
||||||
|
},
|
||||||
|
|
||||||
|
UpStash: {
|
||||||
|
Endpoint: "رابط UpStash Redis REST",
|
||||||
|
UserName: "اسم النسخ الاحتياطي",
|
||||||
|
Password: "رمز UpStash Redis REST",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
LocalState: "بيانات محلية",
|
||||||
|
Overview: (overview: any) => {
|
||||||
|
return `${overview.chat} دردشة، ${overview.message} رسالة، ${overview.prompt} إشعار، ${overview.mask} قناع`;
|
||||||
|
},
|
||||||
|
ImportFailed: "فشل الاستيراد",
|
||||||
},
|
},
|
||||||
Mask: {
|
Mask: {
|
||||||
Splash: {
|
Splash: {
|
||||||
Title: "شاشة تظهر الأقنعة",
|
Title: "صفحة بدء القناع",
|
||||||
SubTitle: "عرض شاشة تظهر الأقنعة قبل بدء الدردشة الجديدة",
|
SubTitle: "عرض صفحة بدء القناع عند بدء دردشة جديدة",
|
||||||
|
},
|
||||||
|
Builtin: {
|
||||||
|
Title: "إخفاء الأقنعة المدمجة",
|
||||||
|
SubTitle: "إخفاء الأقنعة المدمجة في قائمة الأقنعة",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Prompt: {
|
Prompt: {
|
||||||
Disable: {
|
Disable: {
|
||||||
Title: "تعطيل الاكتمال التلقائي",
|
Title: "تعطيل الإكمال التلقائي للإشعارات",
|
||||||
SubTitle: "اكتب / لتشغيل الاكتمال التلقائي",
|
SubTitle: "استخدم / في بداية مربع النص لتفعيل الإكمال التلقائي",
|
||||||
},
|
},
|
||||||
List: "قائمة الاقتراحات",
|
List: "قائمة الإشعارات المخصصة",
|
||||||
ListCount: (builtin: number, custom: number) => `
|
ListCount: (builtin: number, custom: number) =>
|
||||||
${builtin} مدمجة، ${custom} تم تعريفها من قبل المستخدم`,
|
`مدمج ${builtin} إشعار، مخصص ${custom} إشعار`,
|
||||||
Edit: "تعديل",
|
Edit: "تحرير",
|
||||||
Modal: {
|
Modal: {
|
||||||
Title: "قائمة الاقتراحات",
|
Title: "قائمة الإشعارات",
|
||||||
Add: "إضافة واحدة",
|
Add: "جديد",
|
||||||
Search: "البحث في الاقتراحات",
|
Search: "بحث عن إشعارات",
|
||||||
},
|
},
|
||||||
EditModal: {
|
EditModal: {
|
||||||
Title: "تحرير الاقتراح",
|
Title: "تحرير الإشعارات",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
HistoryCount: {
|
HistoryCount: {
|
||||||
Title: "عدد الرسائل المرفقة",
|
Title: "عدد الرسائل التاريخية المرفقة",
|
||||||
SubTitle: "عدد الرسائل المرسلة المرفقة في كل طلب",
|
SubTitle: "عدد الرسائل التاريخية المرفقة مع كل طلب",
|
||||||
},
|
},
|
||||||
CompressThreshold: {
|
CompressThreshold: {
|
||||||
Title: "حد الضغط للتاريخ",
|
Title: "عتبة ضغط طول الرسائل التاريخية",
|
||||||
SubTitle: "سيتم الضغط إذا تجاوزت طول الرسائل غير المضغوطة الحد المحدد",
|
SubTitle:
|
||||||
|
"عندما يتجاوز طول الرسائل التاريخية غير المضغوطة هذه القيمة، سيتم الضغط",
|
||||||
},
|
},
|
||||||
|
|
||||||
Usage: {
|
Usage: {
|
||||||
Title: "رصيد الحساب",
|
Title: "التحقق من الرصيد",
|
||||||
SubTitle(used: any, total: any) {
|
SubTitle(used: any, total: any) {
|
||||||
return `تم استخدام $${used} من هذا الشهر، الاشتراك ${total}`;
|
return `تم استخدام $${used} هذا الشهر، إجمالي الاشتراك $${total}`;
|
||||||
},
|
},
|
||||||
IsChecking: "جارٍ التحقق...",
|
IsChecking: "جارٍ التحقق...",
|
||||||
Check: "التحقق",
|
Check: "إعادة التحقق",
|
||||||
NoAccess: "أدخل مفتاح API للتحقق من الرصيد",
|
NoAccess: "أدخل مفتاح API أو كلمة مرور للوصول إلى الرصيد",
|
||||||
|
},
|
||||||
|
|
||||||
|
Access: {
|
||||||
|
SaasStart: {
|
||||||
|
Title: "استخدام NextChat AI",
|
||||||
|
Label: "(أفضل حل من حيث التكلفة)",
|
||||||
|
SubTitle:
|
||||||
|
"مدعوم رسميًا من NextChat، جاهز للاستخدام بدون إعداد، يدعم أحدث النماذج الكبيرة مثل OpenAI o1 و GPT-4o و Claude-3.5",
|
||||||
|
ChatNow: "الدردشة الآن",
|
||||||
|
},
|
||||||
|
AccessCode: {
|
||||||
|
Title: "كلمة المرور للوصول",
|
||||||
|
SubTitle: "قام المشرف بتمكين الوصول المشفر",
|
||||||
|
Placeholder: "أدخل كلمة المرور للوصول",
|
||||||
|
},
|
||||||
|
CustomEndpoint: {
|
||||||
|
Title: "واجهة مخصصة",
|
||||||
|
SubTitle: "هل تستخدم خدمة Azure أو OpenAI مخصصة",
|
||||||
|
},
|
||||||
|
Provider: {
|
||||||
|
Title: "موفر الخدمة النموذجية",
|
||||||
|
SubTitle: "التبديل بين مقدمي الخدمة المختلفين",
|
||||||
|
},
|
||||||
|
OpenAI: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "مفتاح API",
|
||||||
|
SubTitle: "استخدم مفتاح OpenAI مخصص لتجاوز قيود كلمة المرور",
|
||||||
|
Placeholder: "مفتاح OpenAI API",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "عنوان الواجهة",
|
||||||
|
SubTitle: "يجب أن يحتوي على http(s):// بخلاف العنوان الافتراضي",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Azure: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "مفتاح الواجهة",
|
||||||
|
SubTitle: "استخدم مفتاح Azure مخصص لتجاوز قيود كلمة المرور",
|
||||||
|
Placeholder: "مفتاح Azure API",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "عنوان الواجهة",
|
||||||
|
SubTitle: "مثال:",
|
||||||
|
},
|
||||||
|
|
||||||
|
ApiVerion: {
|
||||||
|
Title: "إصدار الواجهة (azure api version)",
|
||||||
|
SubTitle: "اختر إصدارًا معينًا",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Anthropic: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "مفتاح الواجهة",
|
||||||
|
SubTitle: "استخدم مفتاح Anthropic مخصص لتجاوز قيود كلمة المرور",
|
||||||
|
Placeholder: "مفتاح Anthropic API",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "عنوان الواجهة",
|
||||||
|
SubTitle: "مثال:",
|
||||||
|
},
|
||||||
|
|
||||||
|
ApiVerion: {
|
||||||
|
Title: "إصدار الواجهة (claude api version)",
|
||||||
|
SubTitle: "اختر إصدار API محدد",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Google: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "مفتاح API",
|
||||||
|
SubTitle: "احصل على مفتاح API الخاص بك من Google AI",
|
||||||
|
Placeholder: "أدخل مفتاح Google AI Studio API",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "عنوان النهاية",
|
||||||
|
SubTitle: "مثال:",
|
||||||
|
},
|
||||||
|
|
||||||
|
ApiVersion: {
|
||||||
|
Title: "إصدار API (مخصص لـ gemini-pro)",
|
||||||
|
SubTitle: "اختر إصدار API معين",
|
||||||
|
},
|
||||||
|
GoogleSafetySettings: {
|
||||||
|
Title: "مستوى تصفية الأمان من Google",
|
||||||
|
SubTitle: "تعيين مستوى تصفية المحتوى",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Baidu: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "مفتاح API",
|
||||||
|
SubTitle: "استخدم مفتاح Baidu API مخصص",
|
||||||
|
Placeholder: "مفتاح Baidu API",
|
||||||
|
},
|
||||||
|
SecretKey: {
|
||||||
|
Title: "المفتاح السري",
|
||||||
|
SubTitle: "استخدم مفتاح Baidu Secret مخصص",
|
||||||
|
Placeholder: "مفتاح Baidu Secret",
|
||||||
|
},
|
||||||
|
Endpoint: {
|
||||||
|
Title: "عنوان الواجهة",
|
||||||
|
SubTitle: "لا يدعم التخصيص، انتقل إلى .env للتكوين",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ByteDance: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "مفتاح الواجهة",
|
||||||
|
SubTitle: "استخدم مفتاح ByteDance API مخصص",
|
||||||
|
Placeholder: "مفتاح ByteDance API",
|
||||||
|
},
|
||||||
|
Endpoint: {
|
||||||
|
Title: "عنوان الواجهة",
|
||||||
|
SubTitle: "مثال:",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Alibaba: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "مفتاح الواجهة",
|
||||||
|
SubTitle: "استخدم مفتاح Alibaba Cloud API مخصص",
|
||||||
|
Placeholder: "مفتاح Alibaba Cloud API",
|
||||||
|
},
|
||||||
|
Endpoint: {
|
||||||
|
Title: "عنوان الواجهة",
|
||||||
|
SubTitle: "مثال:",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CustomModel: {
|
||||||
|
Title: "اسم النموذج المخصص",
|
||||||
|
SubTitle: "أضف خيارات نموذج مخصص، مفصولة بفواصل إنجليزية",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
Model: "النموذج",
|
Model: "النموذج",
|
||||||
|
CompressModel: {
|
||||||
|
Title: "نموذج الضغط",
|
||||||
|
SubTitle: "النموذج المستخدم لضغط السجل التاريخي",
|
||||||
|
},
|
||||||
Temperature: {
|
Temperature: {
|
||||||
Title: "الحرارة",
|
Title: "العشوائية (temperature)",
|
||||||
SubTitle: "قيمة أكبر تجعل الإخراج أكثر عشوائية",
|
SubTitle: "كلما زادت القيمة، زادت العشوائية في الردود",
|
||||||
|
},
|
||||||
|
TopP: {
|
||||||
|
Title: "عينات النواة (top_p)",
|
||||||
|
SubTitle: "مشابه للعشوائية ولكن لا تغيره مع العشوائية",
|
||||||
},
|
},
|
||||||
MaxTokens: {
|
MaxTokens: {
|
||||||
Title: "الحد الأقصى للرموز",
|
Title: "حد أقصى للرموز لكل رد (max_tokens)",
|
||||||
SubTitle: "الحد الأقصى لعدد الرموز المدخلة والرموز المُنشأة",
|
SubTitle: "أقصى عدد للرموز في تفاعل واحد",
|
||||||
},
|
},
|
||||||
PresencePenalty: {
|
PresencePenalty: {
|
||||||
Title: "تأثير الوجود",
|
Title: "تجدد الموضوع (presence_penalty)",
|
||||||
SubTitle: "قيمة أكبر تزيد من احتمالية التحدث عن مواضيع جديدة",
|
SubTitle: "كلما زادت القيمة، زادت احتمالية التوسع في مواضيع جديدة",
|
||||||
},
|
},
|
||||||
FrequencyPenalty: {
|
FrequencyPenalty: {
|
||||||
Title: "تأثير التكرار",
|
Title: "عقوبة التكرار (frequency_penalty)",
|
||||||
SubTitle: "قيمة أكبر تقلل من احتمالية تكرار نفس السطر",
|
SubTitle: "كلما زادت القيمة، زادت احتمالية تقليل تكرار الكلمات",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Store: {
|
Store: {
|
||||||
DefaultTopic: "محادثة جديدة",
|
DefaultTopic: "دردشة جديدة",
|
||||||
BotHello: "مرحبًا! كيف يمكنني مساعدتك اليوم؟",
|
BotHello: "كيف يمكنني مساعدتك؟",
|
||||||
Error: "حدث خطأ ما، يرجى المحاولة مرة أخرى في وقت لاحق.",
|
Error: "حدث خطأ، يرجى المحاولة مرة أخرى لاحقًا",
|
||||||
Prompt: {
|
Prompt: {
|
||||||
History: (content: string) => "هذا ملخص لسجل الدردشة كمراجعة: " + content,
|
History: (content: string) =>
|
||||||
|
"هذا ملخص للدردشة السابقة كنقطة انطلاق: " + content,
|
||||||
Topic:
|
Topic:
|
||||||
"يرجى إنشاء عنوان يتكون من أربع إلى خمس كلمات يلخص محادثتنا دون أي مقدمة أو ترقيم أو علامات ترقيم أو نقاط أو رموز إضافية. قم بإزالة علامات التنصيص المحيطة.",
|
"استخدم أربع إلى خمس كلمات لإرجاع ملخص مختصر لهذه الجملة، بدون شرح، بدون علامات ترقيم، بدون كلمات تعبيرية، بدون نص إضافي، بدون تنسيق عريض، إذا لم يكن هناك موضوع، يرجى العودة إلى 'دردشة عامة'",
|
||||||
Summarize:
|
Summarize:
|
||||||
"قم بتلخيص النقاش بشكل موجز في 200 كلمة أو أقل لاستخدامه كاقتراح للسياق في المستقبل.",
|
"قم بتلخيص محتوى الدردشة باختصار، لاستخدامه كإشارة سياقية لاحقة، اجعلها في حدود 200 كلمة",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Copy: {
|
Copy: {
|
||||||
Success: "تم النسخ إلى الحافظة",
|
Success: "تم الكتابة إلى الحافظة",
|
||||||
Failed: "فشلت عملية النسخ، يرجى منح الإذن للوصول إلى الحافظة",
|
Failed: "فشل النسخ، يرجى منح أذونات الحافظة",
|
||||||
|
},
|
||||||
|
Download: {
|
||||||
|
Success: "تم تنزيل المحتوى إلى مجلدك.",
|
||||||
|
Failed: "فشل التنزيل.",
|
||||||
},
|
},
|
||||||
Context: {
|
Context: {
|
||||||
Toast: (x: any) => `مع ${x} اقتراحًا ذا سياق`,
|
Toast: (x: any) => `يحتوي على ${x} إشعارات مخصصة`,
|
||||||
Edit: "الاقتراحات السياقية والذاكرة",
|
Edit: "إعدادات الدردشة الحالية",
|
||||||
Add: "إضافة اقتراح",
|
Add: "إضافة دردشة جديدة",
|
||||||
Clear: "مسح السياق",
|
Clear: "تم مسح السياق",
|
||||||
Revert: "التراجع",
|
Revert: "استعادة السياق",
|
||||||
},
|
},
|
||||||
Plugin: {
|
Plugin: {
|
||||||
Name: "المكوّن الإضافي",
|
Name: "الإضافات",
|
||||||
},
|
},
|
||||||
FineTuned: {
|
FineTuned: {
|
||||||
Sysmessage: "أنت مساعد ي",
|
Sysmessage: "أنت مساعد",
|
||||||
},
|
},
|
||||||
Mask: {
|
SearchChat: {
|
||||||
Name: "الأقنعة",
|
Name: "بحث",
|
||||||
Page: {
|
Page: {
|
||||||
Title: "قالب الاقتراح",
|
Title: "البحث في سجلات الدردشة",
|
||||||
SubTitle: (count: number) => `${count} قوالب الاقتراح`,
|
Search: "أدخل كلمات البحث",
|
||||||
Search: "البحث في القوالب",
|
NoResult: "لم يتم العثور على نتائج",
|
||||||
Create: "إنشاء",
|
NoData: "لا توجد بيانات",
|
||||||
|
Loading: "جارٍ التحميل",
|
||||||
|
|
||||||
|
SubTitle: (count: number) => `تم العثور على ${count} نتائج`,
|
||||||
},
|
},
|
||||||
Item: {
|
Item: {
|
||||||
Info: (count: number) => `${count} اقتراحات`,
|
View: "عرض",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Mask: {
|
||||||
|
Name: "القناع",
|
||||||
|
Page: {
|
||||||
|
Title: "أقنعة الأدوار المخصصة",
|
||||||
|
SubTitle: (count: number) => `${count} تعريف لدور مخصص`,
|
||||||
|
Search: "بحث عن قناع الدور",
|
||||||
|
Create: "إنشاء جديد",
|
||||||
|
},
|
||||||
|
Item: {
|
||||||
|
Info: (count: number) => `يحتوي على ${count} محادثات مخصصة`,
|
||||||
Chat: "الدردشة",
|
Chat: "الدردشة",
|
||||||
View: "عرض",
|
View: "عرض",
|
||||||
Edit: "تعديل",
|
Edit: "تحرير",
|
||||||
Delete: "حذف",
|
Delete: "حذف",
|
||||||
DeleteConfirm: "تأكيد الحذف؟",
|
DeleteConfirm: "تأكيد الحذف؟",
|
||||||
},
|
},
|
||||||
EditModal: {
|
EditModal: {
|
||||||
Title: (readonly: boolean) => `
|
Title: (readonly: boolean) =>
|
||||||
تعديل قالب الاقتراح ${readonly ? "(للقراءة فقط)" : ""}`,
|
`تحرير القناع المخصص ${readonly ? " (للقراءة فقط)" : ""}`,
|
||||||
Download: "تنزيل",
|
Download: "تنزيل القناع المخصص",
|
||||||
Clone: "استنساخ",
|
Clone: "استنساخ القناع",
|
||||||
},
|
},
|
||||||
Config: {
|
Config: {
|
||||||
Avatar: "صورة الروبوت",
|
Avatar: "صورة الدور",
|
||||||
Name: "اسم الروبوت",
|
Name: "اسم الدور",
|
||||||
Sync: {
|
Sync: {
|
||||||
Title: "استخدام الإعدادات العامة",
|
Title: "استخدام الإعدادات العالمية",
|
||||||
SubTitle: "استخدام الإعدادات العامة في هذه الدردشة",
|
SubTitle: "هل تستخدم الدردشة الحالية الإعدادات العالمية للنموذج",
|
||||||
Confirm: "تأكيد الاستبدال بالإعدادات المخصصة بالإعدادات العامة؟",
|
Confirm:
|
||||||
|
"ستتم الكتابة فوق الإعدادات المخصصة للدردشة الحالية تلقائيًا، تأكيد تفعيل الإعدادات العالمية؟",
|
||||||
},
|
},
|
||||||
HideContext: {
|
HideContext: {
|
||||||
Title: "إخفاء اقتراحات السياق",
|
Title: "إخفاء المحادثات المخصصة",
|
||||||
SubTitle: "عدم عرض اقتراحات السياق في الدردشة",
|
SubTitle: "بعد الإخفاء، لن تظهر المحادثات المخصصة في واجهة الدردشة",
|
||||||
|
},
|
||||||
|
Share: {
|
||||||
|
Title: "مشاركة هذا القناع",
|
||||||
|
SubTitle: "إنشاء رابط مباشر لهذا القناع",
|
||||||
|
Action: "نسخ الرابط",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
NewChat: {
|
NewChat: {
|
||||||
Return: "العودة",
|
Return: "العودة",
|
||||||
Skip: "ابدأ فقط",
|
Skip: "بدء الآن",
|
||||||
Title: "اختيار قناع",
|
|
||||||
SubTitle: "دردشة مع الروح وراء القناع",
|
|
||||||
More: "المزيد",
|
|
||||||
NotShow: "عدم العرض مرة أخرى",
|
NotShow: "عدم العرض مرة أخرى",
|
||||||
ConfirmNoShow: "تأكيد تعطيله؟ يمكنك تمكينه في الإعدادات لاحقًا.",
|
ConfirmNoShow:
|
||||||
|
"تأكيد إلغاء العرض؟ بعد الإلغاء، يمكنك إعادة تفعيله في الإعدادات في أي وقت.",
|
||||||
|
Title: "اختر قناعًا",
|
||||||
|
SubTitle: "ابدأ الآن وتفاعل مع الأفكار خلف القناع",
|
||||||
|
More: "عرض الكل",
|
||||||
|
},
|
||||||
|
|
||||||
|
URLCommand: {
|
||||||
|
Code: "تم الكشف عن رمز وصول في الرابط، هل تريد تعبئته تلقائيًا؟",
|
||||||
|
Settings: "تم الكشف عن إعدادات مسبقة في الرابط، هل تريد تعبئتها تلقائيًا؟",
|
||||||
},
|
},
|
||||||
|
|
||||||
UI: {
|
UI: {
|
||||||
|
@ -281,9 +560,16 @@ ${builtin} مدمجة، ${custom} تم تعريفها من قبل المستخد
|
||||||
Cancel: "إلغاء",
|
Cancel: "إلغاء",
|
||||||
Close: "إغلاق",
|
Close: "إغلاق",
|
||||||
Create: "إنشاء",
|
Create: "إنشاء",
|
||||||
Edit: "تعديل",
|
Edit: "تحرير",
|
||||||
|
Export: "تصدير",
|
||||||
|
Import: "استيراد",
|
||||||
|
Sync: "مزامنة",
|
||||||
|
Config: "تكوين",
|
||||||
},
|
},
|
||||||
Exporter: {
|
Exporter: {
|
||||||
|
Description: {
|
||||||
|
Title: "فقط الرسائل بعد مسح السياق سيتم عرضها",
|
||||||
|
},
|
||||||
Model: "النموذج",
|
Model: "النموذج",
|
||||||
Messages: "الرسائل",
|
Messages: "الرسائل",
|
||||||
Topic: "الموضوع",
|
Topic: "الموضوع",
|
||||||
|
|
|
@ -1,140 +1,169 @@
|
||||||
import { SubmitKey } from "../store/config";
|
import { SubmitKey } from "../store/config";
|
||||||
import { PartialLocaleType } from "./index";
|
import type { PartialLocaleType } from "./index";
|
||||||
|
import { getClientConfig } from "../config/client";
|
||||||
|
import { SAAS_CHAT_UTM_URL } from "@/app/constant";
|
||||||
|
const isApp = !!getClientConfig()?.isApp;
|
||||||
|
|
||||||
const bn: PartialLocaleType = {
|
const bn: PartialLocaleType = {
|
||||||
WIP: "শীঘ্রই আসছে...",
|
WIP: "শীঘ্রই আসছে...",
|
||||||
Error: {
|
Error: {
|
||||||
Unauthorized:
|
Unauthorized: isApp
|
||||||
"অননুমোদিত অ্যাক্সেস, অনুগ্রহ করে [অথোরাইজশন](/#/auth) পৃষ্ঠায় অ্যাক্সেস কোড ইনপুট করুন।",
|
? `😆 কথোপকথনে কিছু সমস্যা হয়েছে, চিন্তার কিছু নেই:
|
||||||
|
\\ 1️⃣ যদি আপনি শূন্য কনফিগারেশনে শুরু করতে চান, তাহলে [এখানে ক্লিক করে অবিলম্বে কথোপকথন শুরু করুন 🚀](${SAAS_CHAT_UTM_URL})
|
||||||
|
\\ 2️⃣ যদি আপনি আপনার নিজস্ব OpenAI সম্পদ ব্যবহার করতে চান, তাহলে [এখানে ক্লিক করুন](/#/settings) সেটিংস পরিবর্তন করতে ⚙️`
|
||||||
|
: `😆 কথোপকথনে কিছু সমস্যা হয়েছে, চিন্তার কিছু নেই:
|
||||||
|
\ 1️⃣ যদি আপনি শূন্য কনফিগারেশনে শুরু করতে চান, তাহলে [এখানে ক্লিক করে অবিলম্বে কথোপকথন শুরু করুন 🚀](${SAAS_CHAT_UTM_URL})
|
||||||
|
\ 2️⃣ যদি আপনি একটি প্রাইভেট ডেপ্লয়মেন্ট সংস্করণ ব্যবহার করেন, তাহলে [এখানে ক্লিক করুন](/#/auth) প্রবেশাধিকার কীগুলি প্রবেশ করতে 🔑
|
||||||
|
\ 3️⃣ যদি আপনি আপনার নিজস্ব OpenAI সম্পদ ব্যবহার করতে চান, তাহলে [এখানে ক্লিক করুন](/#/settings) সেটিংস পরিবর্তন করতে ⚙️
|
||||||
|
`,
|
||||||
},
|
},
|
||||||
Auth: {
|
Auth: {
|
||||||
Title: "একটি অ্যাক্সেস কোড প্রয়োজন",
|
Title: "পাসওয়ার্ড প্রয়োজন",
|
||||||
Tips: "নীচে অ্যাক্সেস কোড ইনপুট করুন",
|
Tips: "অ্যাডমিন পাসওয়ার্ড প্রমাণীকরণ চালু করেছেন, নিচে অ্যাক্সেস কোড প্রবেশ করুন",
|
||||||
SubTips: "অথবা আপনার OpenAI API কী প্রবেশ করুন",
|
SubTips: "অথবা আপনার OpenAI অথবা Google API কী প্রবেশ করান",
|
||||||
Input: "অ্যাক্সেস কোড",
|
Input: "এখানে অ্যাক্সেস কোড লিখুন",
|
||||||
Confirm: "নিশ্চিত করুন",
|
Confirm: "নিশ্চিত করুন",
|
||||||
Later: "পরে",
|
Later: "পরে বলুন",
|
||||||
|
Return: "ফিরে আসা",
|
||||||
|
SaasTips: "কনফিগারেশন খুব কঠিন, আমি অবিলম্বে ব্যবহার করতে চাই",
|
||||||
|
TopTips:
|
||||||
|
"🥳 NextChat AI প্রথম প্রকাশের অফার, এখনই OpenAI o1, GPT-4o, Claude-3.5 এবং সর্বশেষ বড় মডেলগুলি আনলক করুন",
|
||||||
},
|
},
|
||||||
ChatItem: {
|
ChatItem: {
|
||||||
ChatItemCount: (count: number) => `${count} টি বার্তা`,
|
ChatItemCount: (count: number) => `${count} টি চ্যাট`,
|
||||||
},
|
},
|
||||||
Chat: {
|
Chat: {
|
||||||
SubTitle: (count: number) => `${count} টি বার্তা`,
|
SubTitle: (count: number) => `মোট ${count} টি চ্যাট`,
|
||||||
|
EditMessage: {
|
||||||
|
Title: "বার্তাগুলি সম্পাদনা করুন",
|
||||||
|
Topic: {
|
||||||
|
Title: "চ্যাটের বিষয়",
|
||||||
|
SubTitle: "বর্তমান চ্যাটের বিষয় পরিবর্তন করুন",
|
||||||
|
},
|
||||||
|
},
|
||||||
Actions: {
|
Actions: {
|
||||||
ChatList: "চ্যাট তালিকায় যান",
|
ChatList: "বার্তা তালিকা দেখুন",
|
||||||
CompressedHistory: "সংক্ষিপ্ত ইতিহাস মেমোরি প্রম্পট",
|
CompressedHistory: "সংকুচিত ইতিহাস দেখুন",
|
||||||
Export: "সমস্ত বার্তা মার্কডাউন হিসাবে রপ্তানি করুন",
|
Export: "চ্যাট ইতিহাস রপ্তানী করুন",
|
||||||
Copy: "কপি",
|
Copy: "অনুলিপি করুন",
|
||||||
Stop: "বন্ধ করুন",
|
Stop: "থামান",
|
||||||
Retry: "পুনরায় চেষ্টা করুন",
|
Retry: "পুনরায় চেষ্টা করুন",
|
||||||
Pin: "পিন করুন",
|
Pin: "পিন করুন",
|
||||||
PinToastContent: "পিন করা হয়েছে ২টি বার্তা প্রম্পটে",
|
PinToastContent: "1 টি চ্যাট পূর্বনির্ধারিত প্রম্পটে পিন করা হয়েছে",
|
||||||
PinToastAction: "দেখুন",
|
PinToastAction: "দেখুন",
|
||||||
Delete: "মুছে ফেলুন",
|
Delete: "মুছে ফেলুন",
|
||||||
Edit: "সম্পাদন করুন",
|
Edit: "সম্পাদনা করুন",
|
||||||
|
RefreshTitle: "শিরোনাম রিফ্রেশ করুন",
|
||||||
|
RefreshToast: "শিরোনাম রিফ্রেশ অনুরোধ পাঠানো হয়েছে",
|
||||||
},
|
},
|
||||||
Commands: {
|
Commands: {
|
||||||
new: "নতুন চ্যাট শুরু করুন",
|
new: "নতুন চ্যাট",
|
||||||
newm: "মাস্ক সহ নতুন চ্যাট শুরু করুন",
|
newm: "মাস্ক থেকে নতুন চ্যাট",
|
||||||
next: "পরবর্তী চ্যাট",
|
next: "পরবর্তী চ্যাট",
|
||||||
prev: "পূর্ববর্তী চ্যাট",
|
prev: "পূর্ববর্তী চ্যাট",
|
||||||
clear: "সংশ্লিষ্টতাবদ্ধকরণ পরিষ্কার করুন",
|
clear: "প্রসঙ্গ পরিষ্কার করুন",
|
||||||
del: "চ্যাট মুছুন",
|
del: "চ্যাট মুছে ফেলুন",
|
||||||
},
|
},
|
||||||
InputActions: {
|
InputActions: {
|
||||||
Stop: "বন্ধ করুন",
|
Stop: "প্রতিক্রিয়া থামান",
|
||||||
ToBottom: "সর্বশেষতম দিকে",
|
ToBottom: "সর্বশেষে স্ক্রোল করুন",
|
||||||
Theme: {
|
Theme: {
|
||||||
auto: "অটো",
|
auto: "স্বয়ংক্রিয় থিম",
|
||||||
light: "হালকা থিম",
|
light: "আলোর মোড",
|
||||||
dark: "ডার্ক থিম",
|
dark: "অন্ধকার মোড",
|
||||||
},
|
},
|
||||||
Prompt: "প্রম্পটগুলিতে",
|
Prompt: "সংক্ষিপ্ত নির্দেশনা",
|
||||||
Masks: "মাস্কগুলি",
|
Masks: "সমস্ত মাস্ক",
|
||||||
Clear: "সংশ্লিষ্টতাবদ্ধকরণ পরিষ্কার করুন",
|
Clear: "চ্যাট পরিষ্কার করুন",
|
||||||
Settings: "সেটিংস",
|
Settings: "চ্যাট সেটিংস",
|
||||||
|
UploadImage: "চিত্র আপলোড করুন",
|
||||||
},
|
},
|
||||||
Rename: "চ্যাট পুনঃনামকরণ করুন",
|
Rename: "চ্যাট নাম পরিবর্তন করুন",
|
||||||
Typing: "টাইপিং...",
|
Typing: "লিখছে…",
|
||||||
Input: (submitKey: string) => {
|
Input: (submitKey: string) => {
|
||||||
var inputHints = `${submitKey} to send`;
|
var inputHints = `${submitKey} পাঠান`;
|
||||||
if (submitKey === String(SubmitKey.Enter)) {
|
if (submitKey === String(SubmitKey.Enter)) {
|
||||||
inputHints += ", Shift + Enter to wrap";
|
inputHints += ",Shift + Enter নতুন লাইন";
|
||||||
}
|
}
|
||||||
return inputHints + ", / to search prompts, : to use commands";
|
return inputHints + ",/ পূর্ণতা সক্রিয় করুন,: কমান্ড সক্রিয় করুন";
|
||||||
},
|
},
|
||||||
Send: "প্রেরণ করুন",
|
Send: "পাঠান",
|
||||||
Config: {
|
Config: {
|
||||||
Reset: "ডিফল্টে রিসেট করুন",
|
Reset: "মেমরি মুছে ফেলুন",
|
||||||
SaveAs: "মাস্ক হিসাবে সংরক্ষণ করুন",
|
SaveAs: "মাস্ক হিসাবে সংরক্ষণ করুন",
|
||||||
},
|
},
|
||||||
|
IsContext: "পূর্বনির্ধারিত প্রম্পট",
|
||||||
},
|
},
|
||||||
Export: {
|
Export: {
|
||||||
Title: "বার্তা রপ্তানিকরণ",
|
Title: "চ্যাট ইতিহাস শেয়ার করুন",
|
||||||
Copy: "সমস্তটি কপি করুন",
|
Copy: "সবকিছু কপি করুন",
|
||||||
Download: "ডাউনলোড করুন",
|
Download: "ফাইল ডাউনলোড করুন",
|
||||||
MessageFromYou: "আপনার বার্তা",
|
Share: "ShareGPT তে শেয়ার করুন",
|
||||||
MessageFromChatGPT: "চ্যাটজিপিটির বার্তা",
|
MessageFromYou: "ব্যবহারকারী",
|
||||||
Share: "শেয়ার করুন শেয়ারজিপিটি তে",
|
MessageFromChatGPT: "ChatGPT",
|
||||||
Format: {
|
Format: {
|
||||||
Title: "রপ্তানি ফরম্যাট",
|
Title: "রপ্তানির ফর্ম্যাট",
|
||||||
SubTitle: "মার্কডাউন বা পিএনজি চিত্র",
|
SubTitle: "Markdown টেক্সট বা PNG চিত্র রপ্তানি করা যাবে",
|
||||||
},
|
},
|
||||||
IncludeContext: {
|
IncludeContext: {
|
||||||
Title: "মাস্ক অন্তর্ভুক্ত করুন",
|
Title: "মাস্ক প্রসঙ্গ অন্তর্ভুক্ত করুন",
|
||||||
SubTitle: "মাস্কগুলি সংরক্ষণ করবেন না কি",
|
SubTitle: "বার্তায় মাস্ক প্রসঙ্গ প্রদর্শন করা হবে কি না",
|
||||||
},
|
},
|
||||||
Steps: {
|
Steps: {
|
||||||
Select: "নির্বাচন করুন",
|
Select: "নির্বাচন করুন",
|
||||||
Preview: "প্রিভিউ করুন",
|
Preview: "পূর্বরূপ দেখুন",
|
||||||
|
},
|
||||||
|
Image: {
|
||||||
|
Toast: "স্ক্রীনশট তৈরি করা হচ্ছে",
|
||||||
|
Modal: "ছবি সংরক্ষণ করতে দীর্ঘ প্রেস করুন অথবা রাইট ক্লিক করুন",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Select: {
|
Select: {
|
||||||
Search: "অনুসন্ধান করুন",
|
Search: "বার্তা অনুসন্ধান করুন",
|
||||||
All: "সমস্তটি নির্বাচন করুন",
|
All: "সবকিছু নির্বাচন করুন",
|
||||||
Latest: "সর্বশেষতমটি নির্বাচন করুন",
|
Latest: "সর্বশেষ কিছু",
|
||||||
Clear: "পরিষ্কার করুন",
|
Clear: "নির্বাচন পরিষ্কার করুন",
|
||||||
},
|
},
|
||||||
Memory: {
|
Memory: {
|
||||||
Title: "মেমোরি প্রম্পট",
|
Title: "ইতিহাস সারাংশ",
|
||||||
EmptyContent: "এখনও কিছুই নেই।",
|
EmptyContent: "চ্যাটের বিষয়বস্তু খুব সংক্ষিপ্ত, সারাংশ প্রয়োজন নেই",
|
||||||
Send: "মেমোরি প্রেরণ করুন",
|
Send: "অটোমেটিক চ্যাট ইতিহাস সংকুচিত করুন এবং প্রসঙ্গ হিসেবে পাঠান",
|
||||||
Copy: "মেমোরি কপি করুন",
|
Copy: "সারাংশ কপি করুন",
|
||||||
Reset: "পুনরায় নিশ্চিত করুন",
|
Reset: "[unused]",
|
||||||
ResetConfirm:
|
ResetConfirm: "ইতিহাস সারাংশ মুছে ফেলার নিশ্চিত করুন?",
|
||||||
"রিসেট করলে বর্তমান চ্যাট ইতিহাস এবং ঐতিহাসিক মেমোরি মুছে যাবে। পুনরায় নির্দিষ্ট করতে চান তা নিশ্চিত করতে চান?",
|
|
||||||
},
|
},
|
||||||
Home: {
|
Home: {
|
||||||
NewChat: "নতুন চ্যাট",
|
NewChat: "নতুন চ্যাট",
|
||||||
DeleteChat: "নির্বাচিত সংলাপটি মুছতে নিশ্চিত করুন?",
|
DeleteChat: "নির্বাচিত চ্যাট মুছে ফেলার নিশ্চিত করুন?",
|
||||||
DeleteToast: "চ্যাটটি মুছেছেন",
|
DeleteToast: "চ্যাট মুছে ফেলা হয়েছে",
|
||||||
Revert: "পুনরায়",
|
Revert: "পূর্বাবস্থায় ফেরান",
|
||||||
},
|
},
|
||||||
Settings: {
|
Settings: {
|
||||||
Title: "সেটিংস",
|
Title: "সেটিংস",
|
||||||
SubTitle: "সমস্ত সেটিংস",
|
SubTitle: "সমস্ত সেটিংস অপশন",
|
||||||
|
|
||||||
Danger: {
|
Danger: {
|
||||||
Reset: {
|
Reset: {
|
||||||
Title: "সমস্ত সেটিংস পুনঃনির্দেশ দিন",
|
Title: "সমস্ত সেটিংস পুনরায় সেট করুন",
|
||||||
SubTitle: "সকল সেটিংস ডিফল্টে পুনঃনির্দেশ দিতে",
|
SubTitle: "সমস্ত সেটিংস বিকল্পগুলিকে ডিফল্ট মানে পুনরায় সেট করুন",
|
||||||
Action: "পুনঃনির্দেশ দিন",
|
Action: "এখনই পুনরায় সেট করুন",
|
||||||
Confirm: "সমস্ত সেটিংস ডিফল্টে পুনঃনির্দেশ করতে নিশ্চিত করতে?",
|
Confirm: "সমস্ত সেটিংস পুনরায় সেট করার নিশ্চিত করুন?",
|
||||||
},
|
},
|
||||||
Clear: {
|
Clear: {
|
||||||
Title: "সমস্ত তথ্য মুছুন",
|
Title: "সমস্ত তথ্য মুছে ফেলুন",
|
||||||
SubTitle: "সমস্ত বার্তা এবং সেটিংস মুছুন",
|
SubTitle: "সমস্ত চ্যাট এবং সেটিংস ডেটা মুছে ফেলুন",
|
||||||
Action: "মুছুন",
|
Action: "এখনই মুছে ফেলুন",
|
||||||
Confirm: "সমস্ত বার্তা এবং সেটিংস মুছে ফেলতে নিশ্চিত করতে?",
|
Confirm: "সমস্ত চ্যাট এবং সেটিংস ডেটা মুছে ফেলানোর নিশ্চিত করুন?",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Lang: {
|
Lang: {
|
||||||
Name: "বাংলা", // ATTENTION: if you wanna add a new translation, please do not translate this value, leave it as `Language`
|
Name: "Language", // ATTENTION: if you wanna add a new translation, please do not translate this value, leave it as `Language`
|
||||||
All: "সমস্ত ভাষা",
|
All: "সমস্ত ভাষা",
|
||||||
},
|
},
|
||||||
Avatar: "অবতার",
|
Avatar: "অভিনেতা",
|
||||||
FontSize: {
|
FontSize: {
|
||||||
Title: "ফন্ট সাইজ",
|
Title: "ফন্ট সাইজ",
|
||||||
SubTitle: "চ্যাট সামগ্রীর ফন্ট সাইজ সংশোধন করুন",
|
SubTitle: "চ্যাট কনটেন্টের ফন্ট সাইজ",
|
||||||
},
|
},
|
||||||
FontFamily: {
|
FontFamily: {
|
||||||
Title: "চ্যাট ফন্ট",
|
Title: "চ্যাট ফন্ট",
|
||||||
|
@ -143,196 +172,417 @@ const bn: PartialLocaleType = {
|
||||||
Placeholder: "ফন্টের নাম",
|
Placeholder: "ফন্টের নাম",
|
||||||
},
|
},
|
||||||
InjectSystemPrompts: {
|
InjectSystemPrompts: {
|
||||||
Title: "حقن تلميحات النظام",
|
Title: "সিস্টেম-লেভেল প্রম্পট যোগ করুন",
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"قم بإضافة تلميحة نظام محاكاة ChatGPT إلى بداية قائمة الرسائل المُطلَبة في كل طلب",
|
"প্রত্যেক বার্তায় একটি সিস্টেম প্রম্পট যোগ করুন যা ChatGPT এর অনুকরণ করবে",
|
||||||
},
|
},
|
||||||
InputTemplate: {
|
InputTemplate: {
|
||||||
Title: "ইনপুট টেমপ্লেট",
|
Title: "ব্যবহারকারীর ইনপুট প্রিপ্রসেসিং",
|
||||||
SubTitle: "নতুনতম বার্তা এই টেমপ্লেটে পূরণ হবে",
|
SubTitle: "ব্যবহারকারীর সর্বশেষ বার্তা এই টেমপ্লেটে পূরণ করা হবে",
|
||||||
},
|
},
|
||||||
|
|
||||||
Update: {
|
Update: {
|
||||||
Version: (x: string) => `Version: ${x}`,
|
Version: (x: string) => `বর্তমান সংস্করণ: ${x}`,
|
||||||
IsLatest: "Latest version",
|
IsLatest: "এটি সর্বশেষ সংস্করণ",
|
||||||
CheckUpdate: "Check Update",
|
CheckUpdate: "আপডেট পরীক্ষা করুন",
|
||||||
IsChecking: "Checking update...",
|
IsChecking: "আপডেট পরীক্ষা করা হচ্ছে...",
|
||||||
FoundUpdate: (x: string) => `Found new version: ${x}`,
|
FoundUpdate: (x: string) => `নতুন সংস্করণ পাওয়া গিয়েছে: ${x}`,
|
||||||
GoToUpdate: "Update",
|
GoToUpdate: "আপডেট করতে যান",
|
||||||
},
|
},
|
||||||
SendKey: "প্রেরণ চাবি",
|
SendKey: "পাঠানোর কী",
|
||||||
Theme: "থিম",
|
Theme: "থিম",
|
||||||
TightBorder: "সঙ্গতি সীমা",
|
TightBorder: "বর্ডার-বিহীন মোড",
|
||||||
SendPreviewBubble: {
|
SendPreviewBubble: {
|
||||||
Title: "প্রিভিউ বুলবুল প্রেরণ করুন",
|
Title: "প্রিভিউ বুদবুদ",
|
||||||
SubTitle: "বুলবুলে মার্কডাউন প্রিভিউ করুন",
|
SubTitle: "প্রিভিউ বুদবুদে Markdown কনটেন্ট প্রিভিউ করুন",
|
||||||
|
},
|
||||||
|
AutoGenerateTitle: {
|
||||||
|
Title: "স্বয়ংক্রিয় শিরোনাম জেনারেশন",
|
||||||
|
SubTitle: "চ্যাট কনটেন্টের ভিত্তিতে উপযুক্ত শিরোনাম তৈরি করুন",
|
||||||
|
},
|
||||||
|
Sync: {
|
||||||
|
CloudState: "ক্লাউড ডেটা",
|
||||||
|
NotSyncYet: "এখনো সিঙ্ক করা হয়নি",
|
||||||
|
Success: "সিঙ্ক সফল",
|
||||||
|
Fail: "সিঙ্ক ব্যর্থ",
|
||||||
|
|
||||||
|
Config: {
|
||||||
|
Modal: {
|
||||||
|
Title: "ক্লাউড সিঙ্ক কনফিগার করুন",
|
||||||
|
Check: "পরীক্ষা করুন",
|
||||||
|
},
|
||||||
|
SyncType: {
|
||||||
|
Title: "সিঙ্ক টাইপ",
|
||||||
|
SubTitle: "পছন্দসই সিঙ্ক সার্ভার নির্বাচন করুন",
|
||||||
|
},
|
||||||
|
Proxy: {
|
||||||
|
Title: "প্রক্সি সক্রিয় করুন",
|
||||||
|
SubTitle:
|
||||||
|
"ব্রাউজারে সিঙ্ক করার সময়, ক্রস-অরিজিন সীমাবদ্ধতা এড়াতে প্রক্সি সক্রিয় করতে হবে",
|
||||||
|
},
|
||||||
|
ProxyUrl: {
|
||||||
|
Title: "প্রক্সি ঠিকানা",
|
||||||
|
SubTitle:
|
||||||
|
"এটি শুধুমাত্র প্রকল্পের সাথে সরবরাহিত ক্রস-অরিজিন প্রক্সির জন্য প্রযোজ্য",
|
||||||
|
},
|
||||||
|
|
||||||
|
WebDav: {
|
||||||
|
Endpoint: "WebDAV ঠিকানা",
|
||||||
|
UserName: "ব্যবহারকারীর নাম",
|
||||||
|
Password: "পাসওয়ার্ড",
|
||||||
|
},
|
||||||
|
|
||||||
|
UpStash: {
|
||||||
|
Endpoint: "UpStash Redis REST URL",
|
||||||
|
UserName: "ব্যাকআপ নাম",
|
||||||
|
Password: "UpStash Redis REST টোকেন",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
LocalState: "স্থানীয় ডেটা",
|
||||||
|
Overview: (overview: any) => {
|
||||||
|
return `${overview.chat} বার চ্যাট, ${overview.message} বার্তা, ${overview.prompt} প্রম্পট, ${overview.mask} মাস্ক`;
|
||||||
|
},
|
||||||
|
ImportFailed: "আমদানি ব্যর্থ",
|
||||||
},
|
},
|
||||||
Mask: {
|
Mask: {
|
||||||
Splash: {
|
Splash: {
|
||||||
Title: "মাস্ক স্প্ল্যাশ স্ক্রিন",
|
Title: "মাস্ক লঞ্চ পেজ",
|
||||||
SubTitle:
|
SubTitle: "নতুন চ্যাট শুরু করার সময় মাস্ক লঞ্চ পেজ প্রদর্শন করুন",
|
||||||
"নতুন চ্যাট শুরু করার আগে মাস্ক স্প্ল্যাশ স্ক্রিন প্রদর্শন করুন",
|
|
||||||
},
|
},
|
||||||
Builtin: {
|
Builtin: {
|
||||||
Title: "মূলত মাস্ক গোপন করুন",
|
Title: "ইনবিল্ট মাস্ক লুকান",
|
||||||
SubTitle: "মাস্ক তালিকা থেকে মূলত মাস্কগুলি লুকান",
|
SubTitle: "সমস্ত মাস্ক তালিকায় ইনবিল্ট মাস্ক লুকান",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Prompt: {
|
Prompt: {
|
||||||
Disable: {
|
Disable: {
|
||||||
Title: "অটো-সম্পূর্ণতা নিষ্ক্রিয় করুন",
|
Title: "প্রম্পট অটো-কমপ্লিশন নিষ্ক্রিয় করুন",
|
||||||
SubTitle: "অটো-সম্পূর্ণতা চালু করতে / ইনপুট করুন",
|
SubTitle: "ইনপুট বক্সের শুরুতে / টাইপ করলে অটো-কমপ্লিশন সক্রিয় হবে",
|
||||||
},
|
},
|
||||||
List: "প্রম্পট তালিকা",
|
List: "স্বনির্ধারিত প্রম্পট তালিকা",
|
||||||
ListCount: (builtin: number, custom: number) =>
|
ListCount: (builtin: number, custom: number) =>
|
||||||
`${builtin} built-in, ${custom} user-defined`,
|
`ইনবিল্ট ${builtin} টি, ব্যবহারকারী সংজ্ঞায়িত ${custom} টি`,
|
||||||
Edit: "সম্পাদন করুন",
|
Edit: "সম্পাদনা করুন",
|
||||||
Modal: {
|
Modal: {
|
||||||
Title: "প্রম্পট তালিকা",
|
Title: "প্রম্পট তালিকা",
|
||||||
Add: "একটি যোগ করুন",
|
Add: "নতুন করুন",
|
||||||
Search: "সন্ধান প্রম্পট",
|
Search: "প্রম্পট অনুসন্ধান করুন",
|
||||||
},
|
},
|
||||||
EditModal: {
|
EditModal: {
|
||||||
Title: "সম্পাদন করুন প্রম্পট",
|
Title: "প্রম্পট সম্পাদনা করুন",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
HistoryCount: {
|
HistoryCount: {
|
||||||
Title: "সংযুক্ত বার্তা সংখ্যা",
|
Title: "সংযুক্ত ইতিহাস বার্তার সংখ্যা",
|
||||||
SubTitle: "প্রতি অনুরোধে প্রেরণ করা গেলে প্রেরণ করা হবে",
|
SubTitle: "প্রতিটি অনুরোধে সংযুক্ত ইতিহাস বার্তার সংখ্যা",
|
||||||
},
|
},
|
||||||
CompressThreshold: {
|
CompressThreshold: {
|
||||||
Title: "ইতিহাস সঙ্কুচিত করার সীমা",
|
Title: "ইতিহাস বার্তা দৈর্ঘ্য সংকুচিত থ্রেশহোল্ড",
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"নকুল বার্তা দৈর্ঘ্য সীমা অতিক্রান্ত হলে ঐ বার্তাটি সঙ্কুচিত হবে",
|
"যখন সংকুচিত ইতিহাস বার্তা এই মান ছাড়িয়ে যায়, তখন সংকুচিত করা হবে",
|
||||||
},
|
},
|
||||||
|
|
||||||
Usage: {
|
Usage: {
|
||||||
Title: "একাউন্ট ব্যালেন্স",
|
Title: "ব্যালেন্স চেক",
|
||||||
SubTitle(used: any, total: any) {
|
SubTitle(used: any, total: any) {
|
||||||
return `এই মাসে ব্যবহৃত $${used}, সাবস্ক্রিপশন $${total}`;
|
return `এই মাসে ব্যবহৃত $${used}, সাবস্ক্রিপশন মোট $${total}`;
|
||||||
},
|
},
|
||||||
IsChecking: "চেক করা হচ্ছে...",
|
IsChecking: "পরীক্ষা করা হচ্ছে…",
|
||||||
Check: "চেক",
|
Check: "পুনরায় পরীক্ষা করুন",
|
||||||
NoAccess: "ব্যালেন্স চেক করতে অ্যাপি কী ইনপুট করুন",
|
NoAccess: "ব্যালেন্স দেখতে API কী অথবা অ্যাক্সেস পাসওয়ার্ড প্রবেশ করুন",
|
||||||
},
|
},
|
||||||
|
|
||||||
Model: "মডেল",
|
Access: {
|
||||||
|
SaasStart: {
|
||||||
|
Title: "NextChat AI ব্যবহার করুন",
|
||||||
|
Label: "(সেরা মূল্যসাশ্রয়ী সমাধান)",
|
||||||
|
SubTitle:
|
||||||
|
"NextChat কর্তৃক অফিসিয়াল রক্ষণাবেক্ষণ, শূন্য কনফিগারেশন ব্যবহার শুরু করুন, OpenAI o1, GPT-4o, Claude-3.5 সহ সর্বশেষ বড় মডেলগুলি সমর্থন করে",
|
||||||
|
ChatNow: "এখনই চ্যাট করুন",
|
||||||
|
},
|
||||||
|
|
||||||
|
AccessCode: {
|
||||||
|
Title: "অ্যাক্সেস পাসওয়ার্ড",
|
||||||
|
SubTitle: "অ্যাডমিন এনক্রিপ্টেড অ্যাক্সেস সক্রিয় করেছেন",
|
||||||
|
Placeholder: "অ্যাক্সেস পাসওয়ার্ড প্রবেশ করুন",
|
||||||
|
},
|
||||||
|
CustomEndpoint: {
|
||||||
|
Title: "স্বনির্ধারিত ইন্টারফেস",
|
||||||
|
SubTitle: "স্বনির্ধারিত Azure বা OpenAI সার্ভিস ব্যবহার করবেন কি?",
|
||||||
|
},
|
||||||
|
Provider: {
|
||||||
|
Title: "মডেল পরিষেবা প্রদানকারী",
|
||||||
|
SubTitle: "বিভিন্ন পরিষেবা প্রদানকারীতে স্যুইচ করুন",
|
||||||
|
},
|
||||||
|
OpenAI: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "API কী",
|
||||||
|
SubTitle:
|
||||||
|
"পাসওয়ার্ড অ্যাক্সেস সীমাবদ্ধতা এড়াতে স্বনির্ধারিত OpenAI কী ব্যবহার করুন",
|
||||||
|
Placeholder: "OpenAI API কী",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "ইন্টারফেস ঠিকানা",
|
||||||
|
SubTitle: "ডিফল্ট ঠিকানা বাদে, http(s):// অন্তর্ভুক্ত করতে হবে",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Azure: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "ইন্টারফেস কী",
|
||||||
|
SubTitle:
|
||||||
|
"পাসওয়ার্ড অ্যাক্সেস সীমাবদ্ধতা এড়াতে স্বনির্ধারিত Azure কী ব্যবহার করুন",
|
||||||
|
Placeholder: "Azure API কী",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "ইন্টারফেস ঠিকানা",
|
||||||
|
SubTitle: "উদাহরণ:",
|
||||||
|
},
|
||||||
|
|
||||||
|
ApiVerion: {
|
||||||
|
Title: "ইন্টারফেস সংস্করণ (azure api version)",
|
||||||
|
SubTitle: "নির্দিষ্ট সংস্করণ নির্বাচন করুন",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Anthropic: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "ইন্টারফেস কী",
|
||||||
|
SubTitle:
|
||||||
|
"পাসওয়ার্ড অ্যাক্সেস সীমাবদ্ধতা এড়াতে স্বনির্ধারিত Anthropic কী ব্যবহার করুন",
|
||||||
|
Placeholder: "Anthropic API কী",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "ইন্টারফেস ঠিকানা",
|
||||||
|
SubTitle: "উদাহরণ:",
|
||||||
|
},
|
||||||
|
|
||||||
|
ApiVerion: {
|
||||||
|
Title: "ইন্টারফেস সংস্করণ (claude api version)",
|
||||||
|
SubTitle: "নির্দিষ্ট API সংস্করণ প্রবেশ করুন",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Google: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "API কী",
|
||||||
|
SubTitle: "Google AI থেকে আপনার API কী পান",
|
||||||
|
Placeholder: "আপনার Google AI Studio API কী প্রবেশ করুন",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "টার্মিনাল ঠিকানা",
|
||||||
|
SubTitle: "উদাহরণ:",
|
||||||
|
},
|
||||||
|
|
||||||
|
ApiVersion: {
|
||||||
|
Title: "API সংস্করণ (শুধুমাত্র gemini-pro)",
|
||||||
|
SubTitle: "একটি নির্দিষ্ট API সংস্করণ নির্বাচন করুন",
|
||||||
|
},
|
||||||
|
GoogleSafetySettings: {
|
||||||
|
Title: "Google সেফটি ফিল্টার স্তর",
|
||||||
|
SubTitle: "বিষয়বস্তু ফিল্টার স্তর সেট করুন",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Baidu: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "API কী",
|
||||||
|
SubTitle: "স্বনির্ধারিত Baidu API কী ব্যবহার করুন",
|
||||||
|
Placeholder: "Baidu API কী",
|
||||||
|
},
|
||||||
|
SecretKey: {
|
||||||
|
Title: "সিক্রেট কী",
|
||||||
|
SubTitle: "স্বনির্ধারিত Baidu সিক্রেট কী ব্যবহার করুন",
|
||||||
|
Placeholder: "Baidu সিক্রেট কী",
|
||||||
|
},
|
||||||
|
Endpoint: {
|
||||||
|
Title: "ইন্টারফেস ঠিকানা",
|
||||||
|
SubTitle: "স্বনির্ধারিত সমর্থিত নয়, .env কনফিগারেশনে চলে যান",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ByteDance: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "ইন্টারফেস কী",
|
||||||
|
SubTitle: "স্বনির্ধারিত ByteDance API কী ব্যবহার করুন",
|
||||||
|
Placeholder: "ByteDance API কী",
|
||||||
|
},
|
||||||
|
Endpoint: {
|
||||||
|
Title: "ইন্টারফেস ঠিকানা",
|
||||||
|
SubTitle: "উদাহরণ:",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Alibaba: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "ইন্টারফেস কী",
|
||||||
|
SubTitle: "স্বনির্ধারিত আলিবাবা ক্লাউড API কী ব্যবহার করুন",
|
||||||
|
Placeholder: "Alibaba Cloud API কী",
|
||||||
|
},
|
||||||
|
Endpoint: {
|
||||||
|
Title: "ইন্টারফেস ঠিকানা",
|
||||||
|
SubTitle: "উদাহরণ:",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CustomModel: {
|
||||||
|
Title: "স্বনির্ধারিত মডেল নাম",
|
||||||
|
SubTitle:
|
||||||
|
"স্বনির্ধারিত মডেল বিকল্পগুলি যুক্ত করুন, ইংরেজি কমা দ্বারা আলাদা করুন",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
Model: "মডেল (model)",
|
||||||
|
CompressModel: {
|
||||||
|
Title: "সংকোচন মডেল",
|
||||||
|
SubTitle: "ইতিহাস সংকুচিত করার জন্য ব্যবহৃত মডেল",
|
||||||
|
},
|
||||||
Temperature: {
|
Temperature: {
|
||||||
Title: "তাপমাত্রা",
|
Title: "যাদুকরিতা (temperature)",
|
||||||
SubTitle: "আরতি মান বেশি করলে বেশি এলোমেলো আউটপুট হবে",
|
SubTitle: "মান বাড়ালে উত্তর বেশি এলোমেলো হবে",
|
||||||
},
|
},
|
||||||
TopP: {
|
TopP: {
|
||||||
Title: "শীর্ষ পি",
|
Title: "নিউক্লিয়ার স্যাম্পলিং (top_p)",
|
||||||
SubTitle: "তাপমাত্রা সঙ্গে এই মান পরিবর্তন করবেন না",
|
SubTitle: "যাদুকরিতা মত, কিন্তু একসাথে পরিবর্তন করবেন না",
|
||||||
},
|
},
|
||||||
MaxTokens: {
|
MaxTokens: {
|
||||||
Title: "সর্বাধিক টোকেন",
|
Title: "একটি উত্তর সীমা (max_tokens)",
|
||||||
SubTitle: "ইনপুট টোকেন এবং উৎপাদিত টোকেনের সর্বাধিক দৈর্ঘ্য",
|
SubTitle: "প্রতি ইন্টারঅ্যাকশনে সর্বাধিক টোকেন সংখ্যা",
|
||||||
},
|
},
|
||||||
PresencePenalty: {
|
PresencePenalty: {
|
||||||
Title: "উপস্থিতির জরিমানা",
|
Title: "বিষয়বস্তু তাজা (presence_penalty)",
|
||||||
SubTitle: "আরতি মান বেশি করলে নতুন বিষয়গুলি সম্ভাব্যতা বাড়াতে পারে",
|
SubTitle: "মান বাড়ালে নতুন বিষয়ে প্রসারিত হওয়ার সম্ভাবনা বেশি",
|
||||||
},
|
},
|
||||||
FrequencyPenalty: {
|
FrequencyPenalty: {
|
||||||
Title: "ফ্রিকুয়েন্সি জরিমানা",
|
Title: "ফ্রিকোয়েন্সি পেনাল্টি (frequency_penalty)",
|
||||||
SubTitle:
|
SubTitle: "মান বাড়ালে পুনরাবৃত্তি শব্দ কমানোর সম্ভাবনা বেশি",
|
||||||
"আরতি মান বাড়ালে একই লাইন পুনরায় ব্যাবহার করার সম্ভাবনা হ্রাস পায়",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Store: {
|
Store: {
|
||||||
DefaultTopic: "নতুন সংলাপ",
|
DefaultTopic: "নতুন চ্যাট",
|
||||||
BotHello: "হ্যালো! আজকে আপনাকে কিভাবে সাহায্য করতে পারি?",
|
BotHello: "আপনার জন্য কিছু করতে পারি?",
|
||||||
Error: "কিছু নিয়ে ভুল হয়েছে, পরে আবার চেষ্টা করুন।",
|
Error: "একটি ত্রুটি ঘটেছে, পরে আবার চেষ্টা করুন",
|
||||||
Prompt: {
|
Prompt: {
|
||||||
History: (content: string) =>
|
History: (content: string) =>
|
||||||
"এটি চ্যাট ইতিহাসের সংক্ষিপ্ত সংকলনের মতো: " + content,
|
"এটি পূর্বের চ্যাটের সারাংশ হিসেবে ব্যবহৃত হবে: " + content,
|
||||||
Topic:
|
Topic:
|
||||||
"আমাদের সংলাপটির চার থেকে পাঁচ শব্দের একটি শিরোনাম তৈরি করুন যা আমাদের আলাপের সংক্ষিপ্তসার হিসাবে যোগ হবে না, যেমন অভিবৃত্তি, বিন্যাস, উদ্ধৃতি, পূর্বচালক চিহ্ন, পূর্বরোবক্তির যেকোনো চিহ্ন বা অতিরিক্ত পাঠ। মেয়াদশেষ উদ্ধৃতি চেষ্টা করুন।",
|
"চার থেকে পাঁচটি শব্দ ব্যবহার করে এই বাক্যের সংক্ষিপ্ত থিম দিন, ব্যাখ্যা, বিরাম চিহ্ন, ভাষা, অতিরিক্ত টেক্সট বা বোল্ড না ব্যবহার করুন। যদি কোনো থিম না থাকে তবে সরাসরি 'বেকার' বলুন",
|
||||||
Summarize:
|
Summarize:
|
||||||
"২০০ শব্দের লম্বা হয়ে মুহূর্তে আলোচনা সংক্ষেপের রপ্তানি করুন, যেটি ভবিষ্যতের প্রম্পট হিসাবে ব্যবহার করবেন।",
|
"আলোচনার বিষয়বস্তু সংক্ষিপ্তভাবে সারাংশ করুন, পরবর্তী কনটেক্সট প্রম্পট হিসেবে ব্যবহারের জন্য, ২০০ শব্দের মধ্যে সীমাবদ্ধ রাখুন",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Copy: {
|
Copy: {
|
||||||
Success: "ক্লিপবোর্ডে কপি করা হয়েছে",
|
Success: "ক্লিপবোর্ডে লেখা হয়েছে",
|
||||||
Failed: "কপি ব্যর্থ, অনুমতি প্রদান করার জন্য অনুমতি প্রদান করুন",
|
Failed: "কপি ব্যর্থ হয়েছে, দয়া করে ক্লিপবোর্ড অনুমতি প্রদান করুন",
|
||||||
|
},
|
||||||
|
Download: {
|
||||||
|
Success: "বিষয়বস্তু আপনার ডিরেক্টরিতে ডাউনলোড করা হয়েছে।",
|
||||||
|
Failed: "ডাউনলোড ব্যর্থ হয়েছে।",
|
||||||
},
|
},
|
||||||
Context: {
|
Context: {
|
||||||
Toast: (x: any) => `With ${x} contextual prompts`,
|
Toast: (x: any) => `${x}টি পূর্বনির্ধারিত প্রম্পট অন্তর্ভুক্ত`,
|
||||||
Edit: "বর্তমান চ্যাট সেটিংস",
|
Edit: "বর্তমান চ্যাট সেটিংস",
|
||||||
Add: "একটি প্রম্পট যোগ করুন",
|
Add: "একটি নতুন চ্যাট যোগ করুন",
|
||||||
Clear: "সঙ্গতি পরিস্কার করুন",
|
Clear: "কনটেক্সট পরিষ্কার করা হয়েছে",
|
||||||
Revert: "পূর্ববর্তী অবস্থানে ফিরে যান",
|
Revert: "কনটেক্সট পুনরুদ্ধার করুন",
|
||||||
},
|
},
|
||||||
Plugin: {
|
Plugin: {
|
||||||
Name: "প্লাগইন",
|
Name: "প্লাগইন",
|
||||||
},
|
},
|
||||||
FineTuned: {
|
FineTuned: {
|
||||||
Sysmessage: "আপনি একটি সহকারী যা",
|
Sysmessage: "আপনি একজন সহকারী",
|
||||||
|
},
|
||||||
|
SearchChat: {
|
||||||
|
Name: "অনুসন্ধান",
|
||||||
|
Page: {
|
||||||
|
Title: "চ্যাট রেকর্ড অনুসন্ধান করুন",
|
||||||
|
Search: "অনুসন্ধান কীওয়ার্ড লিখুন",
|
||||||
|
NoResult: "কোন ফলাফল পাওয়া যায়নি",
|
||||||
|
NoData: "কোন তথ্য নেই",
|
||||||
|
Loading: "লোড হচ্ছে",
|
||||||
|
|
||||||
|
SubTitle: (count: number) => `${count} টি ফলাফল পাওয়া গেছে`,
|
||||||
|
},
|
||||||
|
Item: {
|
||||||
|
View: "দেখুন",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Mask: {
|
Mask: {
|
||||||
Name: "মাস্ক",
|
Name: "মাস্ক",
|
||||||
Page: {
|
Page: {
|
||||||
Title: "প্রম্পট টেমপ্লেট",
|
Title: "পূর্বনির্ধারিত চরিত্র মাস্ক",
|
||||||
SubTitle: (count: number) => `${count} টি প্রম্পট টেমপ্লেট`,
|
SubTitle: (count: number) => `${count}টি পূর্বনির্ধারিত চরিত্র সংজ্ঞা`,
|
||||||
Search: "টেমপ্লেট অনুসন্ধান করুন",
|
Search: "চরিত্র মাস্ক অনুসন্ধান করুন",
|
||||||
Create: "তৈরি করুন",
|
Create: "নতুন তৈরি করুন",
|
||||||
},
|
},
|
||||||
Item: {
|
Item: {
|
||||||
Info: (count: number) => `${count} প্রম্পট`,
|
Info: (count: number) => `ভিতরে ${count}টি পূর্বনির্ধারিত চ্যাট রয়েছে`,
|
||||||
Chat: "চ্যাট",
|
Chat: "চ্যাট",
|
||||||
View: "দেখুন",
|
View: "দেখুন",
|
||||||
Edit: "সম্পাদন করুন",
|
Edit: "সম্পাদনা করুন",
|
||||||
Delete: "মুছে ফেলুন",
|
Delete: "মুছে ফেলুন",
|
||||||
DeleteConfirm: "মুছে ফেলতে নিশ্চিত করুন?",
|
DeleteConfirm: "মুছে ফেলার জন্য নিশ্চিত করুন?",
|
||||||
},
|
},
|
||||||
EditModal: {
|
EditModal: {
|
||||||
Title: (readonly: boolean) =>
|
Title: (readonly: boolean) =>
|
||||||
`প্রম্পট টেমপ্লেট সম্পাদন করুন ${readonly ? "(readonly)" : ""}`,
|
`পূর্বনির্ধারিত মাস্ক সম্পাদনা ${readonly ? "(পঠনযোগ্য)" : ""}`,
|
||||||
Download: "ডাউনলোড করুন",
|
Download: "পূর্বনির্ধারিত ডাউনলোড করুন",
|
||||||
Clone: "ক্লোন করুন",
|
Clone: "পূর্বনির্ধারিত ক্লোন করুন",
|
||||||
},
|
},
|
||||||
Config: {
|
Config: {
|
||||||
Avatar: "বট অবতার",
|
Avatar: "চরিত্রের চিত্র",
|
||||||
Name: "বটের নাম",
|
Name: "চরিত্রের নাম",
|
||||||
Sync: {
|
Sync: {
|
||||||
Title: "গ্লোবাল কনফিগ ব্যবহার করুন",
|
Title: "গ্লোবাল সেটিংস ব্যবহার করুন",
|
||||||
SubTitle: "এই চ্যাটে গ্লোবাল কনফিগ ব্যবহার করুন",
|
SubTitle: "বর্তমান চ্যাট গ্লোবাল মডেল সেটিংস ব্যবহার করছে কি না",
|
||||||
Confirm:
|
Confirm:
|
||||||
"গ্লোবাল কনফিগ দ্বারা কাস্টম কনফিগ ওভাররাইড করতে নিশ্চিত করতে?",
|
"বর্তমান চ্যাটের কাস্টম সেটিংস স্বয়ংক্রিয়ভাবে ওভাররাইট হবে, গ্লোবাল সেটিংস সক্রিয় করতে নিশ্চিত?",
|
||||||
},
|
},
|
||||||
HideContext: {
|
HideContext: {
|
||||||
Title: "সংশ্লিষ্টতা প্রম্পটগুলি লুকান",
|
Title: "পূর্বনির্ধারিত চ্যাট লুকান",
|
||||||
SubTitle: "চ্যাটে সংশ্লিষ্টতা প্রম্পটগুলি দেখাবেন না",
|
SubTitle:
|
||||||
|
"লুকানোর পরে পূর্বনির্ধারিত চ্যাট চ্যাট ইন্টারফেসে প্রদর্শিত হবে না",
|
||||||
},
|
},
|
||||||
Share: {
|
Share: {
|
||||||
Title: "এই মাস্কটি শেয়ার করুন",
|
Title: "এই মাস্ক শেয়ার করুন",
|
||||||
SubTitle: "এই মাস্কের একটি লিঙ্ক তৈরি করুন",
|
SubTitle: "এই মাস্কের সরাসরি লিঙ্ক তৈরি করুন",
|
||||||
Action: "লিঙ্ক কপি করুন",
|
Action: "লিঙ্ক কপি করুন",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
NewChat: {
|
NewChat: {
|
||||||
Return: "ফিরে যান",
|
Return: "ফিরে যান",
|
||||||
Skip: "শুরু করুন",
|
Skip: "ডাইরেক্ট শুরু করুন",
|
||||||
Title: "মাস্ক নির্বাচন করুন",
|
NotShow: "আবার প্রদর্শন করবেন না",
|
||||||
SubTitle: "মাস্কের পিছনে আত্মার সঙ্গে চ্যাট করুন",
|
|
||||||
More: "আরো খুঁজুন",
|
|
||||||
NotShow: "এখনও দেখাবেন না",
|
|
||||||
ConfirmNoShow:
|
ConfirmNoShow:
|
||||||
"নিষ্ক্রিয় করতে নিশ্চিত করুন? পরে আপনি এটি সেটিংসে সক্ষম করতে পারবেন।",
|
"নিশ্চিত যে নিষ্ক্রিয় করবেন? নিষ্ক্রিয় করার পরে সেটিংসে পুনরায় সক্রিয় করা যাবে।",
|
||||||
|
Title: "একটি মাস্ক নির্বাচন করুন",
|
||||||
|
SubTitle: "এখন শুরু করুন, মাস্কের পিছনের চিন্তা প্রতিক্রিয়া করুন",
|
||||||
|
More: "সব দেখুন",
|
||||||
|
},
|
||||||
|
|
||||||
|
URLCommand: {
|
||||||
|
Code: "লিঙ্কে অ্যাক্সেস কোড ইতিমধ্যে অন্তর্ভুক্ত রয়েছে, অটো পূরণ করতে চান?",
|
||||||
|
Settings:
|
||||||
|
"লিঙ্কে প্রাক-নির্ধারিত সেটিংস অন্তর্ভুক্ত রয়েছে, অটো পূরণ করতে চান?",
|
||||||
},
|
},
|
||||||
|
|
||||||
UI: {
|
UI: {
|
||||||
Confirm: "নিশ্চিত করুন",
|
Confirm: "নিশ্চিত করুন",
|
||||||
Cancel: "বাতিল করুন",
|
Cancel: "বাতিল করুন",
|
||||||
Close: "বন্ধ করুন",
|
Close: "বন্ধ করুন",
|
||||||
Create: "তৈরি করুন",
|
Create: "নতুন তৈরি করুন",
|
||||||
Edit: "সম্পাদন করুন",
|
Edit: "সম্পাদনা করুন",
|
||||||
|
Export: "রপ্তানি করুন",
|
||||||
|
Import: "আমদানি করুন",
|
||||||
|
Sync: "সিঙ্ক",
|
||||||
|
Config: "কনফিগারেশন",
|
||||||
},
|
},
|
||||||
Exporter: {
|
Exporter: {
|
||||||
|
Description: {
|
||||||
|
Title: "শুধুমাত্র কনটেক্সট পরিষ্কার করার পরে বার্তাগুলি প্রদর্শিত হবে",
|
||||||
|
},
|
||||||
Model: "মডেল",
|
Model: "মডেল",
|
||||||
Messages: "বার্তা",
|
Messages: "বার্তা",
|
||||||
Topic: "টপিক",
|
Topic: "থিম",
|
||||||
Time: "সময়",
|
Time: "সময়",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { getClientConfig } from "../config/client";
|
import { getClientConfig } from "../config/client";
|
||||||
import { SubmitKey } from "../store/config";
|
import { SubmitKey } from "../store/config";
|
||||||
|
import { SAAS_CHAT_UTM_URL } from "@/app/constant";
|
||||||
|
|
||||||
const isApp = !!getClientConfig()?.isApp;
|
const isApp = !!getClientConfig()?.isApp;
|
||||||
|
|
||||||
|
@ -7,16 +8,26 @@ const cn = {
|
||||||
WIP: "该功能仍在开发中……",
|
WIP: "该功能仍在开发中……",
|
||||||
Error: {
|
Error: {
|
||||||
Unauthorized: isApp
|
Unauthorized: isApp
|
||||||
? "检测到无效 API Key,请前往[设置](/#/settings)页检查 API Key 是否配置正确。"
|
? `😆 对话遇到了一些问题,不用慌:
|
||||||
: "访问密码不正确或为空,请前往[登录](/#/auth)页输入正确的访问密码,或者在[设置](/#/settings)页填入你自己的 OpenAI API Key。",
|
\\ 1️⃣ 想要零配置开箱即用,[点击这里立刻开启对话 🚀](${SAAS_CHAT_UTM_URL})
|
||||||
|
\\ 2️⃣ 如果你想消耗自己的 OpenAI 资源,点击[这里](/#/settings)修改设置 ⚙️`
|
||||||
|
: `😆 对话遇到了一些问题,不用慌:
|
||||||
|
\ 1️⃣ 想要零配置开箱即用,[点击这里立刻开启对话 🚀](${SAAS_CHAT_UTM_URL})
|
||||||
|
\ 2️⃣ 如果你正在使用私有部署版本,点击[这里](/#/auth)输入访问秘钥 🔑
|
||||||
|
\ 3️⃣ 如果你想消耗自己的 OpenAI 资源,点击[这里](/#/settings)修改设置 ⚙️
|
||||||
|
`,
|
||||||
},
|
},
|
||||||
Auth: {
|
Auth: {
|
||||||
|
Return: "返回",
|
||||||
Title: "需要密码",
|
Title: "需要密码",
|
||||||
Tips: "管理员开启了密码验证,请在下方填入访问码",
|
Tips: "管理员开启了密码验证,请在下方填入访问码",
|
||||||
SubTips: "或者输入你的 OpenAI 或 Google API 密钥",
|
SubTips: "或者输入你的 OpenAI 或 Google AI 密钥",
|
||||||
Input: "在此处填写访问码",
|
Input: "在此处填写访问码",
|
||||||
Confirm: "确认",
|
Confirm: "确认",
|
||||||
Later: "稍后再说",
|
Later: "稍后再说",
|
||||||
|
SaasTips: "配置太麻烦,想要立即使用",
|
||||||
|
TopTips:
|
||||||
|
"🥳 NextChat AI 首发优惠,立刻解锁 OpenAI o1, GPT-4o, Claude-3.5 等最新大模型",
|
||||||
},
|
},
|
||||||
ChatItem: {
|
ChatItem: {
|
||||||
ChatItemCount: (count: number) => `${count} 条对话`,
|
ChatItemCount: (count: number) => `${count} 条对话`,
|
||||||
|
@ -42,6 +53,11 @@ const cn = {
|
||||||
PinToastAction: "查看",
|
PinToastAction: "查看",
|
||||||
Delete: "删除",
|
Delete: "删除",
|
||||||
Edit: "编辑",
|
Edit: "编辑",
|
||||||
|
FullScreen: "全屏",
|
||||||
|
RefreshTitle: "刷新标题",
|
||||||
|
RefreshToast: "已发送刷新标题请求",
|
||||||
|
Speech: "朗读",
|
||||||
|
StopSpeech: "停止",
|
||||||
},
|
},
|
||||||
Commands: {
|
Commands: {
|
||||||
new: "新建聊天",
|
new: "新建聊天",
|
||||||
|
@ -49,6 +65,7 @@ const cn = {
|
||||||
next: "下一个聊天",
|
next: "下一个聊天",
|
||||||
prev: "上一个聊天",
|
prev: "上一个聊天",
|
||||||
clear: "清除上下文",
|
clear: "清除上下文",
|
||||||
|
fork: "复制聊天",
|
||||||
del: "删除聊天",
|
del: "删除聊天",
|
||||||
},
|
},
|
||||||
InputActions: {
|
InputActions: {
|
||||||
|
@ -75,11 +92,21 @@ const cn = {
|
||||||
return inputHints + ",/ 触发补全,: 触发命令";
|
return inputHints + ",/ 触发补全,: 触发命令";
|
||||||
},
|
},
|
||||||
Send: "发送",
|
Send: "发送",
|
||||||
|
StartSpeak: "说话",
|
||||||
|
StopSpeak: "停止",
|
||||||
Config: {
|
Config: {
|
||||||
Reset: "清除记忆",
|
Reset: "清除记忆",
|
||||||
SaveAs: "存为面具",
|
SaveAs: "存为面具",
|
||||||
},
|
},
|
||||||
IsContext: "预设提示词",
|
IsContext: "预设提示词",
|
||||||
|
ShortcutKey: {
|
||||||
|
Title: "键盘快捷方式",
|
||||||
|
newChat: "打开新聊天",
|
||||||
|
focusInput: "聚焦输入框",
|
||||||
|
copyLastMessage: "复制最后一个回复",
|
||||||
|
copyLastCode: "复制最后一个代码块",
|
||||||
|
showShortcutKey: "显示快捷方式",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Export: {
|
Export: {
|
||||||
Title: "分享聊天记录",
|
Title: "分享聊天记录",
|
||||||
|
@ -132,6 +159,7 @@ const cn = {
|
||||||
Settings: {
|
Settings: {
|
||||||
Title: "设置",
|
Title: "设置",
|
||||||
SubTitle: "所有设置选项",
|
SubTitle: "所有设置选项",
|
||||||
|
ShowPassword: "显示密码",
|
||||||
|
|
||||||
Danger: {
|
Danger: {
|
||||||
Reset: {
|
Reset: {
|
||||||
|
@ -280,6 +308,13 @@ const cn = {
|
||||||
},
|
},
|
||||||
|
|
||||||
Access: {
|
Access: {
|
||||||
|
SaasStart: {
|
||||||
|
Title: "使用 NextChat AI",
|
||||||
|
Label: "(性价比最高的方案)",
|
||||||
|
SubTitle:
|
||||||
|
"由 NextChat 官方维护, 零配置开箱即用,支持 OpenAI o1, GPT-4o, Claude-3.5 等最新大模型",
|
||||||
|
ChatNow: "立刻对话",
|
||||||
|
},
|
||||||
AccessCode: {
|
AccessCode: {
|
||||||
Title: "访问密码",
|
Title: "访问密码",
|
||||||
SubTitle: "管理员已开启加密访问",
|
SubTitle: "管理员已开启加密访问",
|
||||||
|
@ -343,7 +378,7 @@ const cn = {
|
||||||
ApiKey: {
|
ApiKey: {
|
||||||
Title: "API 密钥",
|
Title: "API 密钥",
|
||||||
SubTitle: "从 Google AI 获取您的 API 密钥",
|
SubTitle: "从 Google AI 获取您的 API 密钥",
|
||||||
Placeholder: "输入您的 Google AI Studio API 密钥",
|
Placeholder: "Google AI API KEY",
|
||||||
},
|
},
|
||||||
|
|
||||||
Endpoint: {
|
Endpoint: {
|
||||||
|
@ -436,6 +471,22 @@ const cn = {
|
||||||
SubTitle: "样例:",
|
SubTitle: "样例:",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Iflytek: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "ApiKey",
|
||||||
|
SubTitle: "从讯飞星火控制台获取的 APIKey",
|
||||||
|
Placeholder: "APIKey",
|
||||||
|
},
|
||||||
|
ApiSecret: {
|
||||||
|
Title: "ApiSecret",
|
||||||
|
SubTitle: "从讯飞星火控制台获取的 APISecret",
|
||||||
|
Placeholder: "APISecret",
|
||||||
|
},
|
||||||
|
Endpoint: {
|
||||||
|
Title: "接口地址",
|
||||||
|
SubTitle: "样例:",
|
||||||
|
},
|
||||||
|
},
|
||||||
CustomModel: {
|
CustomModel: {
|
||||||
Title: "自定义模型名",
|
Title: "自定义模型名",
|
||||||
SubTitle: "增加自定义模型可选项,使用英文逗号隔开",
|
SubTitle: "增加自定义模型可选项,使用英文逗号隔开",
|
||||||
|
@ -443,6 +494,10 @@ const cn = {
|
||||||
},
|
},
|
||||||
|
|
||||||
Model: "模型 (model)",
|
Model: "模型 (model)",
|
||||||
|
CompressModel: {
|
||||||
|
Title: "压缩模型",
|
||||||
|
SubTitle: "用于压缩历史记录的模型",
|
||||||
|
},
|
||||||
Temperature: {
|
Temperature: {
|
||||||
Title: "随机性 (temperature)",
|
Title: "随机性 (temperature)",
|
||||||
SubTitle: "值越大,回复越随机",
|
SubTitle: "值越大,回复越随机",
|
||||||
|
@ -463,6 +518,26 @@ const cn = {
|
||||||
Title: "频率惩罚度 (frequency_penalty)",
|
Title: "频率惩罚度 (frequency_penalty)",
|
||||||
SubTitle: "值越大,越有可能降低重复字词",
|
SubTitle: "值越大,越有可能降低重复字词",
|
||||||
},
|
},
|
||||||
|
TTS: {
|
||||||
|
Enable: {
|
||||||
|
Title: "启用文本转语音",
|
||||||
|
SubTitle: "启用文本生成语音服务",
|
||||||
|
},
|
||||||
|
Autoplay: {
|
||||||
|
Title: "启用自动朗读",
|
||||||
|
SubTitle: "自动生成语音并播放,需先开启文本转语音开关",
|
||||||
|
},
|
||||||
|
Model: "模型",
|
||||||
|
Engine: "转换引擎",
|
||||||
|
Voice: {
|
||||||
|
Title: "声音",
|
||||||
|
SubTitle: "生成语音时使用的声音",
|
||||||
|
},
|
||||||
|
Speed: {
|
||||||
|
Title: "速度",
|
||||||
|
SubTitle: "生成语音的速度",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Store: {
|
Store: {
|
||||||
DefaultTopic: "新的聊天",
|
DefaultTopic: "新的聊天",
|
||||||
|
@ -477,8 +552,8 @@ const cn = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Copy: {
|
Copy: {
|
||||||
Success: "已写入剪切板",
|
Success: "已写入剪贴板",
|
||||||
Failed: "复制失败,请赋予剪切板权限",
|
Failed: "复制失败,请赋予剪贴板权限",
|
||||||
},
|
},
|
||||||
Download: {
|
Download: {
|
||||||
Success: "内容已下载到您的目录。",
|
Success: "内容已下载到您的目录。",
|
||||||
|
@ -491,16 +566,67 @@ const cn = {
|
||||||
Clear: "上下文已清除",
|
Clear: "上下文已清除",
|
||||||
Revert: "恢复上下文",
|
Revert: "恢复上下文",
|
||||||
},
|
},
|
||||||
Plugin: {
|
|
||||||
Name: "插件",
|
|
||||||
Artifacts: "Artifacts",
|
|
||||||
},
|
|
||||||
Discovery: {
|
Discovery: {
|
||||||
Name: "发现",
|
Name: "发现",
|
||||||
},
|
},
|
||||||
FineTuned: {
|
FineTuned: {
|
||||||
Sysmessage: "你是一个助手",
|
Sysmessage: "你是一个助手",
|
||||||
},
|
},
|
||||||
|
SearchChat: {
|
||||||
|
Name: "搜索",
|
||||||
|
Page: {
|
||||||
|
Title: "搜索聊天记录",
|
||||||
|
Search: "输入搜索关键词",
|
||||||
|
NoResult: "没有找到结果",
|
||||||
|
NoData: "没有数据",
|
||||||
|
Loading: "加载中",
|
||||||
|
|
||||||
|
SubTitle: (count: number) => `搜索到 ${count} 条结果`,
|
||||||
|
},
|
||||||
|
Item: {
|
||||||
|
View: "查看",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Plugin: {
|
||||||
|
Name: "插件",
|
||||||
|
Page: {
|
||||||
|
Title: "插件",
|
||||||
|
SubTitle: (count: number) => `${count} 个插件`,
|
||||||
|
Search: "搜索插件",
|
||||||
|
Create: "新建",
|
||||||
|
Find: "您可以在Github上找到优秀的插件:",
|
||||||
|
},
|
||||||
|
Item: {
|
||||||
|
Info: (count: number) => `${count} 方法`,
|
||||||
|
View: "查看",
|
||||||
|
Edit: "编辑",
|
||||||
|
Delete: "删除",
|
||||||
|
DeleteConfirm: "确认删除?",
|
||||||
|
},
|
||||||
|
Auth: {
|
||||||
|
None: "不需要授权",
|
||||||
|
Basic: "Basic",
|
||||||
|
Bearer: "Bearer",
|
||||||
|
Custom: "自定义",
|
||||||
|
CustomHeader: "自定义参数名称",
|
||||||
|
Token: "Token",
|
||||||
|
Proxy: "使用代理",
|
||||||
|
ProxyDescription: "使用代理解决 CORS 错误",
|
||||||
|
Location: "位置",
|
||||||
|
LocationHeader: "Header",
|
||||||
|
LocationQuery: "Query",
|
||||||
|
LocationBody: "Body",
|
||||||
|
},
|
||||||
|
EditModal: {
|
||||||
|
Title: (readonly: boolean) => `编辑插件 ${readonly ? "(只读)" : ""}`,
|
||||||
|
Download: "下载",
|
||||||
|
Auth: "授权方式",
|
||||||
|
Content: "OpenAPI Schema",
|
||||||
|
Load: "从网页加载",
|
||||||
|
Method: "方法",
|
||||||
|
Error: "格式错误",
|
||||||
|
},
|
||||||
|
},
|
||||||
Mask: {
|
Mask: {
|
||||||
Name: "面具",
|
Name: "面具",
|
||||||
Page: {
|
Page: {
|
||||||
|
@ -535,6 +661,10 @@ const cn = {
|
||||||
Title: "隐藏预设对话",
|
Title: "隐藏预设对话",
|
||||||
SubTitle: "隐藏后预设对话不会出现在聊天界面",
|
SubTitle: "隐藏后预设对话不会出现在聊天界面",
|
||||||
},
|
},
|
||||||
|
Artifacts: {
|
||||||
|
Title: "启用Artifacts",
|
||||||
|
SubTitle: "启用之后可以直接渲染HTML页面",
|
||||||
|
},
|
||||||
Share: {
|
Share: {
|
||||||
Title: "分享此面具",
|
Title: "分享此面具",
|
||||||
SubTitle: "生成此面具的直达链接",
|
SubTitle: "生成此面具的直达链接",
|
||||||
|
|
|
@ -1,75 +1,169 @@
|
||||||
import { SubmitKey } from "../store/config";
|
import { SubmitKey } from "../store/config";
|
||||||
import type { PartialLocaleType } from "./index";
|
import type { PartialLocaleType } from "./index";
|
||||||
|
import { getClientConfig } from "../config/client";
|
||||||
|
import { SAAS_CHAT_UTM_URL } from "@/app/constant";
|
||||||
|
const isApp = !!getClientConfig()?.isApp;
|
||||||
|
|
||||||
const cs: PartialLocaleType = {
|
const cs: PartialLocaleType = {
|
||||||
WIP: "V přípravě...",
|
WIP: "V přípravě...",
|
||||||
Error: {
|
Error: {
|
||||||
Unauthorized:
|
Unauthorized: isApp
|
||||||
"Neoprávněný přístup, zadejte přístupový kód na [stránce](/#/auth) nastavení.",
|
? `😆 Rozhovor narazil na nějaké problémy, nebojte se:
|
||||||
|
\\ 1️⃣ Pokud chcete začít bez konfigurace, [klikněte sem pro okamžitý začátek chatu 🚀](${SAAS_CHAT_UTM_URL})
|
||||||
|
\\ 2️⃣ Pokud chcete využít své vlastní zdroje OpenAI, klikněte [sem](/#/settings) a upravte nastavení ⚙️`
|
||||||
|
: `😆 Rozhovor narazil na nějaké problémy, nebojte se:
|
||||||
|
\ 1️⃣ Pokud chcete začít bez konfigurace, [klikněte sem pro okamžitý začátek chatu 🚀](${SAAS_CHAT_UTM_URL})
|
||||||
|
\ 2️⃣ Pokud používáte verzi soukromého nasazení, klikněte [sem](/#/auth) a zadejte přístupový klíč 🔑
|
||||||
|
\ 3️⃣ Pokud chcete využít své vlastní zdroje OpenAI, klikněte [sem](/#/settings) a upravte nastavení ⚙️
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
Auth: {
|
||||||
|
Title: "Potřebné heslo",
|
||||||
|
Tips: "Administrátor povolil ověření heslem, prosím zadejte přístupový kód níže",
|
||||||
|
SubTips: "nebo zadejte svůj OpenAI nebo Google API klíč",
|
||||||
|
Input: "Zadejte přístupový kód zde",
|
||||||
|
Confirm: "Potvrdit",
|
||||||
|
Later: "Později",
|
||||||
|
Return: "Návrat",
|
||||||
|
SaasTips: "Konfigurace je příliš složitá, chci okamžitě začít používat",
|
||||||
|
TopTips:
|
||||||
|
"🥳 Uvítací nabídka NextChat AI, okamžitě odemkněte OpenAI o1, GPT-4o, Claude-3.5 a nejnovější velké modely",
|
||||||
},
|
},
|
||||||
ChatItem: {
|
ChatItem: {
|
||||||
ChatItemCount: (count: number) => `${count} zpráv`,
|
ChatItemCount: (count: number) => `${count} konverzací`,
|
||||||
},
|
},
|
||||||
Chat: {
|
Chat: {
|
||||||
SubTitle: (count: number) => `${count} zpráv s ChatGPT`,
|
SubTitle: (count: number) => `Celkem ${count} konverzací`,
|
||||||
|
EditMessage: {
|
||||||
|
Title: "Upravit zprávy",
|
||||||
|
Topic: {
|
||||||
|
Title: "Téma konverzace",
|
||||||
|
SubTitle: "Změnit aktuální téma konverzace",
|
||||||
|
},
|
||||||
|
},
|
||||||
Actions: {
|
Actions: {
|
||||||
ChatList: "Přejít na seznam chatů",
|
ChatList: "Zobrazit seznam zpráv",
|
||||||
CompressedHistory: "Pokyn z komprimované paměti historie",
|
CompressedHistory: "Zobrazit komprimovanou historii Prompt",
|
||||||
Export: "Exportovat všechny zprávy jako Markdown",
|
Export: "Exportovat konverzace",
|
||||||
Copy: "Kopírovat",
|
Copy: "Kopírovat",
|
||||||
Stop: "Zastavit",
|
Stop: "Zastavit",
|
||||||
Retry: "Zopakovat",
|
Retry: "Zkusit znovu",
|
||||||
|
Pin: "Připnout",
|
||||||
|
PinToastContent: "1 konverzace byla připnuta k přednastaveným promptům",
|
||||||
|
PinToastAction: "Zobrazit",
|
||||||
Delete: "Smazat",
|
Delete: "Smazat",
|
||||||
|
Edit: "Upravit",
|
||||||
|
RefreshTitle: "Obnovit název",
|
||||||
|
RefreshToast: "Požadavek na obnovení názvu byl odeslán",
|
||||||
},
|
},
|
||||||
Rename: "Přejmenovat chat",
|
Commands: {
|
||||||
Typing: "Píše...",
|
new: "Nová konverzace",
|
||||||
|
newm: "Nová konverzace z masky",
|
||||||
|
next: "Další konverzace",
|
||||||
|
prev: "Předchozí konverzace",
|
||||||
|
clear: "Vymazat kontext",
|
||||||
|
del: "Smazat konverzaci",
|
||||||
|
},
|
||||||
|
InputActions: {
|
||||||
|
Stop: "Zastavit odpověď",
|
||||||
|
ToBottom: "Přejít na nejnovější",
|
||||||
|
Theme: {
|
||||||
|
auto: "Automatické téma",
|
||||||
|
light: "Světelný režim",
|
||||||
|
dark: "Tmavý režim",
|
||||||
|
},
|
||||||
|
Prompt: "Rychlé příkazy",
|
||||||
|
Masks: "Všechny masky",
|
||||||
|
Clear: "Vymazat konverzaci",
|
||||||
|
Settings: "Nastavení konverzace",
|
||||||
|
UploadImage: "Nahrát obrázek",
|
||||||
|
},
|
||||||
|
Rename: "Přejmenovat konverzaci",
|
||||||
|
Typing: "Píše se…",
|
||||||
Input: (submitKey: string) => {
|
Input: (submitKey: string) => {
|
||||||
var inputHints = `${submitKey} pro odeslání`;
|
var inputHints = `${submitKey} odeslat`;
|
||||||
if (submitKey === String(SubmitKey.Enter)) {
|
if (submitKey === String(SubmitKey.Enter)) {
|
||||||
inputHints += ", Shift + Enter pro řádkování";
|
inputHints += ",Shift + Enter pro nový řádek";
|
||||||
}
|
}
|
||||||
return inputHints + ", / pro vyhledávání pokynů";
|
return inputHints + ",/ pro doplnění, : pro příkaz";
|
||||||
},
|
},
|
||||||
Send: "Odeslat",
|
Send: "Odeslat",
|
||||||
Config: {
|
Config: {
|
||||||
Reset: "Obnovit výchozí",
|
Reset: "Vymazat paměť",
|
||||||
SaveAs: "Uložit jako Masku",
|
SaveAs: "Uložit jako masku",
|
||||||
},
|
},
|
||||||
|
IsContext: "Přednastavené prompty",
|
||||||
},
|
},
|
||||||
Export: {
|
Export: {
|
||||||
Title: "Všechny zprávy",
|
Title: "Sdílet konverzace",
|
||||||
Copy: "Kopírovat vše",
|
Copy: "Kopírovat vše",
|
||||||
Download: "Stáhnout",
|
Download: "Stáhnout soubor",
|
||||||
MessageFromYou: "Zpráva od vás",
|
Share: "Sdílet na ShareGPT",
|
||||||
MessageFromChatGPT: "Zpráva z ChatGPT",
|
MessageFromYou: "Uživatel",
|
||||||
|
MessageFromChatGPT: "ChatGPT",
|
||||||
|
Format: {
|
||||||
|
Title: "Formát exportu",
|
||||||
|
SubTitle: "Lze exportovat jako Markdown text nebo PNG obrázek",
|
||||||
|
},
|
||||||
|
IncludeContext: {
|
||||||
|
Title: "Zahrnout kontext masky",
|
||||||
|
SubTitle: "Zobrazit kontext masky ve zprávách",
|
||||||
|
},
|
||||||
|
Steps: {
|
||||||
|
Select: "Vybrat",
|
||||||
|
Preview: "Náhled",
|
||||||
|
},
|
||||||
|
Image: {
|
||||||
|
Toast: "Generování screenshotu",
|
||||||
|
Modal: "Dlouhým stiskem nebo pravým tlačítkem myši uložte obrázek",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Select: {
|
||||||
|
Search: "Hledat zprávy",
|
||||||
|
All: "Vybrat vše",
|
||||||
|
Latest: "Několik posledních",
|
||||||
|
Clear: "Zrušit výběr",
|
||||||
},
|
},
|
||||||
Memory: {
|
Memory: {
|
||||||
Title: "Pokyn z paměti",
|
Title: "Historie shrnutí",
|
||||||
EmptyContent: "Zatím nic.",
|
EmptyContent: "Obsah konverzace je příliš krátký, není třeba shrnovat",
|
||||||
Send: "Odeslat paměť",
|
Send: "Automaticky komprimovat konverzace a odeslat jako kontext",
|
||||||
Copy: "Kopírovat paměť",
|
Copy: "Kopírovat shrnutí",
|
||||||
Reset: "Obnovit relaci",
|
Reset: "[nepoužívá se]",
|
||||||
ResetConfirm:
|
ResetConfirm: "Opravdu chcete vymazat historii shrnutí?",
|
||||||
"Resetováním se vymaže historie aktuálních konverzací i paměť historie pokynů. Opravdu chcete provést obnovu?",
|
|
||||||
},
|
},
|
||||||
Home: {
|
Home: {
|
||||||
NewChat: "Nový chat",
|
NewChat: "Nová konverzace",
|
||||||
DeleteChat: "Potvrzujete smazání vybrané konverzace?",
|
DeleteChat: "Opravdu chcete smazat vybranou konverzaci?",
|
||||||
DeleteToast: "Chat smazán",
|
DeleteToast: "Konverzace byla smazána",
|
||||||
Revert: "Zvrátit",
|
Revert: "Vrátit",
|
||||||
},
|
},
|
||||||
Settings: {
|
Settings: {
|
||||||
Title: "Nastavení",
|
Title: "Nastavení",
|
||||||
SubTitle: "Všechna nastavení",
|
SubTitle: "Všechny možnosti nastavení",
|
||||||
|
|
||||||
|
Danger: {
|
||||||
|
Reset: {
|
||||||
|
Title: "Obnovit všechna nastavení",
|
||||||
|
SubTitle: "Obnovit všechny nastavení na výchozí hodnoty",
|
||||||
|
Action: "Okamžitě obnovit",
|
||||||
|
Confirm: "Opravdu chcete obnovit všechna nastavení?",
|
||||||
|
},
|
||||||
|
Clear: {
|
||||||
|
Title: "Smazat všechna data",
|
||||||
|
SubTitle: "Smazat všechny chaty a nastavení",
|
||||||
|
Action: "Okamžitě smazat",
|
||||||
|
Confirm: "Opravdu chcete smazat všechny chaty a nastavení?",
|
||||||
|
},
|
||||||
|
},
|
||||||
Lang: {
|
Lang: {
|
||||||
Name: "Language", // ATTENTION: if you wanna add a new translation, please do not translate this value, leave it as `Language`
|
Name: "Language", // POZOR: pokud chcete přidat nový překlad, prosím, nechte tuto hodnotu jako `Language`
|
||||||
All: "Všechny jazyky",
|
All: "Všechny jazyky",
|
||||||
},
|
},
|
||||||
Avatar: "Avatar",
|
Avatar: "Profilový obrázek",
|
||||||
FontSize: {
|
FontSize: {
|
||||||
Title: "Velikost písma",
|
Title: "Velikost písma",
|
||||||
SubTitle: "Nastavení velikosti písma obsahu chatu",
|
SubTitle: "Velikost písma pro obsah chatu",
|
||||||
},
|
},
|
||||||
FontFamily: {
|
FontFamily: {
|
||||||
Title: "Chatové Písmo",
|
Title: "Chatové Písmo",
|
||||||
|
@ -78,162 +172,415 @@ const cs: PartialLocaleType = {
|
||||||
Placeholder: "Název Písma",
|
Placeholder: "Název Písma",
|
||||||
},
|
},
|
||||||
InjectSystemPrompts: {
|
InjectSystemPrompts: {
|
||||||
Title: "Vložit systémové prompty",
|
Title: "Vložit systémové výzvy",
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Vynutit přidání simulovaného systémového promptu ChatGPT na začátek seznamu zpráv každého požadavku",
|
"Automaticky přidat systémovou výzvu simulující ChatGPT na začátek seznamu zpráv pro každý požadavek",
|
||||||
},
|
},
|
||||||
|
InputTemplate: {
|
||||||
|
Title: "Předzpracování uživatelského vstupu",
|
||||||
|
SubTitle: "Nejnovější zpráva uživatele bude vyplněna do této šablony",
|
||||||
|
},
|
||||||
|
|
||||||
Update: {
|
Update: {
|
||||||
Version: (x: string) => `Verze: ${x}`,
|
Version: (x: string) => `Aktuální verze: ${x}`,
|
||||||
IsLatest: "Aktuální verze",
|
IsLatest: "Jste na nejnovější verzi",
|
||||||
CheckUpdate: "Zkontrolovat aktualizace",
|
CheckUpdate: "Zkontrolovat aktualizace",
|
||||||
IsChecking: "Kontrola aktualizace...",
|
IsChecking: "Kontrola aktualizací...",
|
||||||
FoundUpdate: (x: string) => `Nalezena nová verze: ${x}`,
|
FoundUpdate: (x: string) => `Nalezena nová verze: ${x}`,
|
||||||
GoToUpdate: "Aktualizovat",
|
GoToUpdate: "Přejít na aktualizaci",
|
||||||
},
|
},
|
||||||
SendKey: "Odeslat klíč",
|
SendKey: "Klávesa pro odeslání",
|
||||||
Theme: "Téma",
|
Theme: "Téma",
|
||||||
TightBorder: "Těsné ohraničení",
|
TightBorder: "Režim bez okrajů",
|
||||||
SendPreviewBubble: {
|
SendPreviewBubble: {
|
||||||
Title: "Odesílat chatovací bublinu s náhledem",
|
Title: "Náhledová bublina",
|
||||||
SubTitle: "Zobrazit v náhledu bubliny",
|
SubTitle: "Náhled Markdown obsahu v náhledové bublině",
|
||||||
|
},
|
||||||
|
AutoGenerateTitle: {
|
||||||
|
Title: "Automatické generování názvu",
|
||||||
|
SubTitle: "Generovat vhodný název na základě obsahu konverzace",
|
||||||
|
},
|
||||||
|
Sync: {
|
||||||
|
CloudState: "Data na cloudu",
|
||||||
|
NotSyncYet: "Ještě nebylo synchronizováno",
|
||||||
|
Success: "Synchronizace úspěšná",
|
||||||
|
Fail: "Synchronizace selhala",
|
||||||
|
|
||||||
|
Config: {
|
||||||
|
Modal: {
|
||||||
|
Title: "Nastavení cloudové synchronizace",
|
||||||
|
Check: "Zkontrolovat dostupnost",
|
||||||
|
},
|
||||||
|
SyncType: {
|
||||||
|
Title: "Typ synchronizace",
|
||||||
|
SubTitle: "Vyberte oblíbený synchronizační server",
|
||||||
|
},
|
||||||
|
Proxy: {
|
||||||
|
Title: "Povolit proxy",
|
||||||
|
SubTitle:
|
||||||
|
"Při synchronizaci v prohlížeči musí být proxy povolena, aby se předešlo problémům s CORS",
|
||||||
|
},
|
||||||
|
ProxyUrl: {
|
||||||
|
Title: "Adresa proxy",
|
||||||
|
SubTitle: "Pouze pro interní proxy",
|
||||||
|
},
|
||||||
|
|
||||||
|
WebDav: {
|
||||||
|
Endpoint: "WebDAV adresa",
|
||||||
|
UserName: "Uživatelské jméno",
|
||||||
|
Password: "Heslo",
|
||||||
|
},
|
||||||
|
|
||||||
|
UpStash: {
|
||||||
|
Endpoint: "UpStash Redis REST URL",
|
||||||
|
UserName: "Název zálohy",
|
||||||
|
Password: "UpStash Redis REST Token",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
LocalState: "Lokální data",
|
||||||
|
Overview: (overview: any) => {
|
||||||
|
return `${overview.chat} konverzací, ${overview.message} zpráv, ${overview.prompt} promptů, ${overview.mask} masek`;
|
||||||
|
},
|
||||||
|
ImportFailed: "Import selhal",
|
||||||
},
|
},
|
||||||
Mask: {
|
Mask: {
|
||||||
Splash: {
|
Splash: {
|
||||||
Title: "Úvodní obrazovka Masek",
|
Title: "Úvodní stránka masky",
|
||||||
SubTitle: "Před zahájením nového chatu zobrazte úvodní obrazovku Masek",
|
SubTitle: "Při zahájení nové konverzace zobrazit úvodní stránku masky",
|
||||||
|
},
|
||||||
|
Builtin: {
|
||||||
|
Title: "Skrýt vestavěné masky",
|
||||||
|
SubTitle: "Skrýt vestavěné masky v seznamu všech masek",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Prompt: {
|
Prompt: {
|
||||||
Disable: {
|
Disable: {
|
||||||
Title: "Deaktivovat automatické dokončování",
|
Title: "Zakázat automatické doplňování promptů",
|
||||||
SubTitle: "Zadejte / pro spuštění automatického dokončování",
|
SubTitle:
|
||||||
|
"Automatické doplňování se aktivuje zadáním / na začátku textového pole",
|
||||||
},
|
},
|
||||||
List: "Seznam pokynů",
|
List: "Seznam vlastních promptů",
|
||||||
ListCount: (builtin: number, custom: number) =>
|
ListCount: (builtin: number, custom: number) =>
|
||||||
`${builtin} vestavěných, ${custom} uživatelských`,
|
`Vestavěné ${builtin} položek, uživatelsky definované ${custom} položek`,
|
||||||
Edit: "Upravit",
|
Edit: "Upravit",
|
||||||
Modal: {
|
Modal: {
|
||||||
Title: "Seznam pokynů",
|
Title: "Seznam promptů",
|
||||||
Add: "Přidat pokyn",
|
Add: "Nový",
|
||||||
Search: "Hledat pokyny",
|
Search: "Hledat prompty",
|
||||||
},
|
},
|
||||||
EditModal: {
|
EditModal: {
|
||||||
Title: "Editovat pokyn",
|
Title: "Upravit prompt",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
HistoryCount: {
|
HistoryCount: {
|
||||||
Title: "Počet připojených zpráv",
|
Title: "Počet historických zpráv",
|
||||||
SubTitle: "Počet odeslaných připojených zpráv na žádost",
|
SubTitle: "Počet historických zpráv zahrnutých v každém požadavku",
|
||||||
},
|
},
|
||||||
CompressThreshold: {
|
CompressThreshold: {
|
||||||
Title: "Práh pro kompresi historie",
|
Title: "Prahová hodnota komprese historických zpráv",
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Komprese proběhne, pokud délka nekomprimovaných zpráv přesáhne tuto hodnotu",
|
"Když nekomprimované historické zprávy překročí tuto hodnotu, dojde ke kompresi",
|
||||||
},
|
},
|
||||||
|
|
||||||
Usage: {
|
Usage: {
|
||||||
Title: "Stav účtu",
|
Title: "Kontrola zůstatku",
|
||||||
SubTitle(used: any, total: any) {
|
SubTitle(used: any, total: any) {
|
||||||
return `Použito tento měsíc $${used}, předplaceno $${total}`;
|
return `Tento měsíc použito $${used}, celkový předplatný objem $${total}`;
|
||||||
},
|
},
|
||||||
IsChecking: "Kontroluji...",
|
IsChecking: "Probíhá kontrola…",
|
||||||
Check: "Zkontrolovat",
|
Check: "Znovu zkontrolovat",
|
||||||
NoAccess: "Pro kontrolu zůstatku zadejte klíč API",
|
NoAccess: "Zadejte API Key nebo přístupové heslo pro zobrazení zůstatku",
|
||||||
},
|
},
|
||||||
|
|
||||||
Model: "Model",
|
Access: {
|
||||||
|
SaasStart: {
|
||||||
|
Title: "Použití NextChat AI",
|
||||||
|
Label: "(Nejlepší nákladově efektivní řešení)",
|
||||||
|
SubTitle:
|
||||||
|
"Oficiálně udržováno NextChat, připraveno k použití bez konfigurace, podporuje nejnovější velké modely jako OpenAI o1, GPT-4o, Claude-3.5",
|
||||||
|
ChatNow: "Začněte chatovat nyní",
|
||||||
|
},
|
||||||
|
|
||||||
|
AccessCode: {
|
||||||
|
Title: "Přístupový kód",
|
||||||
|
SubTitle: "Administrátor aktivoval šifrovaný přístup",
|
||||||
|
Placeholder: "Zadejte přístupový kód",
|
||||||
|
},
|
||||||
|
CustomEndpoint: {
|
||||||
|
Title: "Vlastní rozhraní",
|
||||||
|
SubTitle: "Použít vlastní Azure nebo OpenAI službu",
|
||||||
|
},
|
||||||
|
Provider: {
|
||||||
|
Title: "Poskytovatel modelu",
|
||||||
|
SubTitle: "Přepnout mezi různými poskytovateli",
|
||||||
|
},
|
||||||
|
OpenAI: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "API Key",
|
||||||
|
SubTitle:
|
||||||
|
"Použijte vlastní OpenAI Key k obejití přístupového omezení",
|
||||||
|
Placeholder: "OpenAI API Key",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Adresa rozhraní",
|
||||||
|
SubTitle: "Kromě výchozí adresy musí obsahovat http(s)://",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Azure: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "Rozhraní klíč",
|
||||||
|
SubTitle: "Použijte vlastní Azure Key k obejití přístupového omezení",
|
||||||
|
Placeholder: "Azure API Key",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Adresa rozhraní",
|
||||||
|
SubTitle: "Příklad:",
|
||||||
|
},
|
||||||
|
|
||||||
|
ApiVerion: {
|
||||||
|
Title: "Verze rozhraní (azure api version)",
|
||||||
|
SubTitle: "Vyberte konkrétní verzi",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Anthropic: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "Rozhraní klíč",
|
||||||
|
SubTitle:
|
||||||
|
"Použijte vlastní Anthropic Key k obejití přístupového omezení",
|
||||||
|
Placeholder: "Anthropic API Key",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Adresa rozhraní",
|
||||||
|
SubTitle: "Příklad:",
|
||||||
|
},
|
||||||
|
|
||||||
|
ApiVerion: {
|
||||||
|
Title: "Verze rozhraní (claude api version)",
|
||||||
|
SubTitle: "Vyberte konkrétní verzi API",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Google: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "API klíč",
|
||||||
|
SubTitle: "Získejte svůj API klíč od Google AI",
|
||||||
|
Placeholder: "Zadejte svůj Google AI Studio API klíč",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Konečná adresa",
|
||||||
|
SubTitle: "Příklad:",
|
||||||
|
},
|
||||||
|
|
||||||
|
ApiVersion: {
|
||||||
|
Title: "Verze API (pouze pro gemini-pro)",
|
||||||
|
SubTitle: "Vyberte konkrétní verzi API",
|
||||||
|
},
|
||||||
|
GoogleSafetySettings: {
|
||||||
|
Title: "Úroveň bezpečnostního filtrování Google",
|
||||||
|
SubTitle: "Nastavit úroveň filtrování obsahu",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Baidu: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "API Key",
|
||||||
|
SubTitle: "Použijte vlastní Baidu API Key",
|
||||||
|
Placeholder: "Baidu API Key",
|
||||||
|
},
|
||||||
|
SecretKey: {
|
||||||
|
Title: "Secret Key",
|
||||||
|
SubTitle: "Použijte vlastní Baidu Secret Key",
|
||||||
|
Placeholder: "Baidu Secret Key",
|
||||||
|
},
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Adresa rozhraní",
|
||||||
|
SubTitle:
|
||||||
|
"Nepodporuje vlastní nastavení, přejděte na .env konfiguraci",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ByteDance: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "Rozhraní klíč",
|
||||||
|
SubTitle: "Použijte vlastní ByteDance API Key",
|
||||||
|
Placeholder: "ByteDance API Key",
|
||||||
|
},
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Adresa rozhraní",
|
||||||
|
SubTitle: "Příklad:",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Alibaba: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "Rozhraní klíč",
|
||||||
|
SubTitle: "Použijte vlastní Alibaba Cloud API Key",
|
||||||
|
Placeholder: "Alibaba Cloud API Key",
|
||||||
|
},
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Adresa rozhraní",
|
||||||
|
SubTitle: "Příklad:",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CustomModel: {
|
||||||
|
Title: "Vlastní názvy modelů",
|
||||||
|
SubTitle: "Přidejte možnosti vlastních modelů, oddělené čárkami",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
Model: "Model (model)",
|
||||||
|
CompressModel: {
|
||||||
|
Title: "Kompresní model",
|
||||||
|
SubTitle: "Model používaný pro kompresi historie",
|
||||||
|
},
|
||||||
Temperature: {
|
Temperature: {
|
||||||
Title: "Teplota",
|
Title: "Náhodnost (temperature)",
|
||||||
SubTitle: "Větší hodnota činí výstup náhodnějším",
|
SubTitle: "Čím vyšší hodnota, tím náhodnější odpovědi",
|
||||||
|
},
|
||||||
|
TopP: {
|
||||||
|
Title: "Jádrové vzorkování (top_p)",
|
||||||
|
SubTitle: "Podobné náhodnosti, ale neměňte spolu s náhodností",
|
||||||
},
|
},
|
||||||
MaxTokens: {
|
MaxTokens: {
|
||||||
Title: "Max. počet tokenů",
|
Title: "Omezení odpovědi (max_tokens)",
|
||||||
SubTitle: "Maximální délka vstupního tokenu a generovaných tokenů",
|
SubTitle: "Maximální počet Tokenů použitých v jednom interakci",
|
||||||
},
|
},
|
||||||
PresencePenalty: {
|
PresencePenalty: {
|
||||||
Title: "Přítomnostní korekce",
|
Title: "Čerstvost témat (presence_penalty)",
|
||||||
SubTitle: "Větší hodnota zvyšuje pravděpodobnost nových témat.",
|
SubTitle:
|
||||||
|
"Čím vyšší hodnota, tím větší pravděpodobnost rozšíření na nová témata",
|
||||||
},
|
},
|
||||||
FrequencyPenalty: {
|
FrequencyPenalty: {
|
||||||
Title: "Frekvenční penalizace",
|
Title: "Penalizace frekvence (frequency_penalty)",
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Větší hodnota snižující pravděpodobnost opakování stejného řádku",
|
"Čím vyšší hodnota, tím větší pravděpodobnost snížení opakování slov",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Store: {
|
Store: {
|
||||||
DefaultTopic: "Nová konverzace",
|
DefaultTopic: "Nový chat",
|
||||||
BotHello: "Ahoj! Jak mohu dnes pomoci?",
|
BotHello: "Jak vám mohu pomoci?",
|
||||||
Error: "Něco se pokazilo, zkuste to prosím později.",
|
Error: "Došlo k chybě, zkuste to prosím znovu později.",
|
||||||
Prompt: {
|
Prompt: {
|
||||||
History: (content: string) =>
|
History: (content: string) =>
|
||||||
"Toto je shrnutí historie chatu mezi umělou inteligencí a uživatelem v podobě rekapitulace: " +
|
"Toto je shrnutí historie chatu jako kontext: " + content,
|
||||||
content,
|
|
||||||
Topic:
|
Topic:
|
||||||
"Vytvořte prosím název o čtyřech až pěti slovech vystihující průběh našeho rozhovoru bez jakýchkoli úvodních slov, interpunkčních znamének, uvozovek, teček, symbolů nebo dalšího textu. Odstraňte uvozovky.",
|
"Použijte čtyři až pět slov pro stručné téma této věty, bez vysvětlení, interpunkce, citoslovcí, nadbytečného textu, bez tučného písma. Pokud téma neexistuje, vraťte pouze 'neformální chat'.",
|
||||||
Summarize:
|
Summarize:
|
||||||
"Krátce shrň naši diskusi v rozsahu do 200 slov a použij ji jako podnět pro budoucí kontext.",
|
"Stručně shrňte obsah konverzace jako kontextový prompt pro budoucí použití, do 200 slov",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Copy: {
|
Copy: {
|
||||||
Success: "Zkopírováno do schránky",
|
Success: "Zkopírováno do schránky",
|
||||||
Failed: "Kopírování selhalo, prosím, povolte přístup ke schránce",
|
Failed: "Kopírování selhalo, prosím, povolte přístup ke schránce",
|
||||||
},
|
},
|
||||||
|
Download: {
|
||||||
|
Success: "Obsah byl stažen do vašeho adresáře.",
|
||||||
|
Failed: "Stahování selhalo.",
|
||||||
|
},
|
||||||
Context: {
|
Context: {
|
||||||
Toast: (x: any) => `Použití ${x} kontextových pokynů`,
|
Toast: (x: any) => `Obsahuje ${x} přednastavených promptů`,
|
||||||
Edit: "Kontextové a paměťové pokyny",
|
Edit: "Nastavení aktuální konverzace",
|
||||||
Add: "Přidat pokyn",
|
Add: "Přidat novou konverzaci",
|
||||||
|
Clear: "Kontext byl vymazán",
|
||||||
|
Revert: "Obnovit kontext",
|
||||||
},
|
},
|
||||||
Plugin: {
|
Plugin: {
|
||||||
Name: "Plugin",
|
Name: "Plugin",
|
||||||
},
|
},
|
||||||
FineTuned: {
|
FineTuned: {
|
||||||
Sysmessage: "Jste asistent, který",
|
Sysmessage: "Jste asistent",
|
||||||
|
},
|
||||||
|
SearchChat: {
|
||||||
|
Name: "Hledat",
|
||||||
|
Page: {
|
||||||
|
Title: "Hledat v historii chatu",
|
||||||
|
Search: "Zadejte hledané klíčové slovo",
|
||||||
|
NoResult: "Nebyly nalezeny žádné výsledky",
|
||||||
|
NoData: "Žádná data",
|
||||||
|
Loading: "Načítání",
|
||||||
|
|
||||||
|
SubTitle: (count: number) => `Nalezeno ${count} výsledků`,
|
||||||
|
},
|
||||||
|
Item: {
|
||||||
|
View: "Zobrazit",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Mask: {
|
Mask: {
|
||||||
Name: "Maska",
|
Name: "Maska",
|
||||||
Page: {
|
Page: {
|
||||||
Title: "Šablona pokynu",
|
Title: "Přednastavené role masky",
|
||||||
SubTitle: (count: number) => `${count} šablon pokynů`,
|
SubTitle: (count: number) => `${count} definovaných rolí`,
|
||||||
Search: "Hledat v šablonách",
|
Search: "Hledat role masky",
|
||||||
Create: "Vytvořit",
|
Create: "Nový",
|
||||||
},
|
},
|
||||||
Item: {
|
Item: {
|
||||||
Info: (count: number) => `${count} pokynů`,
|
Info: (count: number) => `Obsahuje ${count} přednastavených konverzací`,
|
||||||
Chat: "Chat",
|
Chat: "Chat",
|
||||||
View: "Zobrazit",
|
View: "Zobrazit",
|
||||||
Edit: "Upravit",
|
Edit: "Upravit",
|
||||||
Delete: "Smazat",
|
Delete: "Smazat",
|
||||||
DeleteConfirm: "Potvrdit smazání?",
|
DeleteConfirm: "Opravdu chcete smazat?",
|
||||||
},
|
},
|
||||||
EditModal: {
|
EditModal: {
|
||||||
Title: (readonly: boolean) =>
|
Title: (readonly: boolean) =>
|
||||||
`Editovat šablonu pokynu ${readonly ? "(pouze ke čtení)" : ""}`,
|
`Upravit přednastavenou masku ${readonly ? " (jen pro čtení)" : ""}`,
|
||||||
Download: "Stáhnout",
|
Download: "Stáhnout přednastavení",
|
||||||
Clone: "Duplikovat",
|
Clone: "Klonovat přednastavení",
|
||||||
},
|
},
|
||||||
Config: {
|
Config: {
|
||||||
Avatar: "Avatar Bota",
|
Avatar: "Profilový obrázek",
|
||||||
Name: "Jméno Bota",
|
Name: "Název role",
|
||||||
|
Sync: {
|
||||||
|
Title: "Použít globální nastavení",
|
||||||
|
SubTitle: "Použít globální modelová nastavení pro aktuální konverzaci",
|
||||||
|
Confirm:
|
||||||
|
"Vaše vlastní nastavení konverzace bude automaticky přepsáno, opravdu chcete použít globální nastavení?",
|
||||||
|
},
|
||||||
|
HideContext: {
|
||||||
|
Title: "Skrýt přednastavené konverzace",
|
||||||
|
SubTitle:
|
||||||
|
"Po skrytí se přednastavené konverzace nebudou zobrazovat v chatovém rozhraní",
|
||||||
|
},
|
||||||
|
Share: {
|
||||||
|
Title: "Sdílet tuto masku",
|
||||||
|
SubTitle: "Generovat přímý odkaz na tuto masku",
|
||||||
|
Action: "Kopírovat odkaz",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
NewChat: {
|
NewChat: {
|
||||||
Return: "Zpět",
|
Return: "Zpět",
|
||||||
Skip: "Přeskočit",
|
Skip: "Začít hned",
|
||||||
Title: "Vyberte Masku",
|
NotShow: "Zobrazit už nikdy",
|
||||||
SubTitle: "Chatovat s duší za Maskou",
|
ConfirmNoShow:
|
||||||
More: "Najít více",
|
"Opravdu chcete zakázat? Zakázání můžete kdykoli znovu povolit v nastavení.",
|
||||||
NotShow: "Nezobrazovat znovu",
|
Title: "Vyberte masku",
|
||||||
ConfirmNoShow: "Potvrdit zakázání?Můžete jej povolit později v nastavení.",
|
SubTitle: "Začněte nyní a konfrontujte se s myslí za maskou",
|
||||||
|
More: "Zobrazit vše",
|
||||||
|
},
|
||||||
|
|
||||||
|
URLCommand: {
|
||||||
|
Code: "Byl detekován přístupový kód v odkazu, chcete jej automaticky vyplnit?",
|
||||||
|
Settings:
|
||||||
|
"Byla detekována přednastavená nastavení v odkazu, chcete je automaticky vyplnit?",
|
||||||
},
|
},
|
||||||
|
|
||||||
UI: {
|
UI: {
|
||||||
Confirm: "Potvrdit",
|
Confirm: "Potvrdit",
|
||||||
Cancel: "Zrušit",
|
Cancel: "Zrušit",
|
||||||
Close: "Zavřít",
|
Close: "Zavřít",
|
||||||
Create: "Vytvořit",
|
Create: "Nový",
|
||||||
Edit: "Upravit",
|
Edit: "Upravit",
|
||||||
|
Export: "Exportovat",
|
||||||
|
Import: "Importovat",
|
||||||
|
Sync: "Synchronizovat",
|
||||||
|
Config: "Konfigurovat",
|
||||||
},
|
},
|
||||||
Exporter: {
|
Exporter: {
|
||||||
|
Description: {
|
||||||
|
Title: "Pouze zprávy po vymazání kontextu budou zobrazeny",
|
||||||
|
},
|
||||||
Model: "Model",
|
Model: "Model",
|
||||||
Messages: "Zprávy",
|
Messages: "Zprávy",
|
||||||
Topic: "Téma",
|
Topic: "Téma",
|
||||||
|
|
|
@ -1,75 +1,172 @@
|
||||||
import { SubmitKey } from "../store/config";
|
import { SubmitKey } from "../store/config";
|
||||||
import type { PartialLocaleType } from "./index";
|
import type { PartialLocaleType } from "./index";
|
||||||
|
import { getClientConfig } from "../config/client";
|
||||||
|
import { SAAS_CHAT_UTM_URL } from "@/app/constant";
|
||||||
|
const isApp = !!getClientConfig()?.isApp;
|
||||||
|
|
||||||
const de: PartialLocaleType = {
|
const de: PartialLocaleType = {
|
||||||
WIP: "In Bearbeitung...",
|
WIP: "In Bearbeitung...",
|
||||||
Error: {
|
Error: {
|
||||||
Unauthorized:
|
Unauthorized: isApp
|
||||||
"Unbefugter Zugriff, bitte geben Sie den Zugangscode auf der [Einstellungsseite](/#/auth) ein.",
|
? `😆 Das Gespräch hatte einige Probleme, keine Sorge:
|
||||||
|
\\ 1️⃣ Wenn du ohne Konfiguration sofort starten möchtest, [klicke hier, um sofort zu chatten 🚀](${SAAS_CHAT_UTM_URL})
|
||||||
|
\\ 2️⃣ Wenn du deine eigenen OpenAI-Ressourcen verwenden möchtest, klicke [hier](/#/settings), um die Einstellungen zu ändern ⚙️`
|
||||||
|
: `😆 Das Gespräch hatte einige Probleme, keine Sorge:
|
||||||
|
\ 1️⃣ Wenn du ohne Konfiguration sofort starten möchtest, [klicke hier, um sofort zu chatten 🚀](${SAAS_CHAT_UTM_URL})
|
||||||
|
\ 2️⃣ Wenn du eine private Bereitstellung verwendest, klicke [hier](/#/auth), um den Zugriffsschlüssel einzugeben 🔑
|
||||||
|
\ 3️⃣ Wenn du deine eigenen OpenAI-Ressourcen verwenden möchtest, klicke [hier](/#/settings), um die Einstellungen zu ändern ⚙️
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
Auth: {
|
||||||
|
Title: "Passwort erforderlich",
|
||||||
|
Tips: "Der Administrator hat die Passwortüberprüfung aktiviert. Bitte geben Sie den Zugangscode unten ein.",
|
||||||
|
SubTips: "Oder geben Sie Ihren OpenAI oder Google API-Schlüssel ein.",
|
||||||
|
Input: "Geben Sie hier den Zugangscode ein",
|
||||||
|
Confirm: "Bestätigen",
|
||||||
|
Later: "Später",
|
||||||
|
Return: "Zurück",
|
||||||
|
SaasTips:
|
||||||
|
"Die Konfiguration ist zu kompliziert, ich möchte es sofort nutzen",
|
||||||
|
TopTips:
|
||||||
|
"🥳 NextChat AI Einführungsangebot, schalte jetzt OpenAI o1, GPT-4o, Claude-3.5 und die neuesten großen Modelle frei",
|
||||||
},
|
},
|
||||||
ChatItem: {
|
ChatItem: {
|
||||||
ChatItemCount: (count: number) => `${count} Nachrichten`,
|
ChatItemCount: (count: number) => `${count} Gespräche`,
|
||||||
},
|
},
|
||||||
Chat: {
|
Chat: {
|
||||||
SubTitle: (count: number) => `${count} Nachrichten mit ChatGPT`,
|
SubTitle: (count: number) => `Insgesamt ${count} Gespräche`,
|
||||||
Actions: {
|
EditMessage: {
|
||||||
ChatList: "Zur Chat-Liste gehen",
|
Title: "Nachricht bearbeiten",
|
||||||
CompressedHistory: "Komprimierter Gedächtnis-Prompt",
|
Topic: {
|
||||||
Export: "Alle Nachrichten als Markdown exportieren",
|
Title: "Chat-Thema",
|
||||||
Copy: "Kopieren",
|
SubTitle: "Ändern Sie das aktuelle Chat-Thema",
|
||||||
Stop: "Stop",
|
|
||||||
Retry: "Wiederholen",
|
|
||||||
Delete: "Delete",
|
|
||||||
},
|
},
|
||||||
Rename: "Chat umbenennen",
|
},
|
||||||
Typing: "Tippen...",
|
Actions: {
|
||||||
|
ChatList: "Nachrichtliste anzeigen",
|
||||||
|
CompressedHistory: "Komprimierte Historie anzeigen",
|
||||||
|
Export: "Chatverlauf exportieren",
|
||||||
|
Copy: "Kopieren",
|
||||||
|
Stop: "Stoppen",
|
||||||
|
Retry: "Erneut versuchen",
|
||||||
|
Pin: "Anheften",
|
||||||
|
PinToastContent: "1 Gespräch an den voreingestellten Prompt angeheftet",
|
||||||
|
PinToastAction: "Ansehen",
|
||||||
|
Delete: "Löschen",
|
||||||
|
Edit: "Bearbeiten",
|
||||||
|
RefreshTitle: "Titel aktualisieren",
|
||||||
|
RefreshToast: "Anfrage zur Titelaktualisierung gesendet",
|
||||||
|
},
|
||||||
|
Commands: {
|
||||||
|
new: "Neues Gespräch",
|
||||||
|
newm: "Neues Gespräch aus Maske erstellen",
|
||||||
|
next: "Nächstes Gespräch",
|
||||||
|
prev: "Vorheriges Gespräch",
|
||||||
|
clear: "Kontext löschen",
|
||||||
|
del: "Gespräch löschen",
|
||||||
|
},
|
||||||
|
InputActions: {
|
||||||
|
Stop: "Antwort stoppen",
|
||||||
|
ToBottom: "Zum neuesten Beitrag",
|
||||||
|
Theme: {
|
||||||
|
auto: "Automatisches Thema",
|
||||||
|
light: "Helles Thema",
|
||||||
|
dark: "Dunkles Thema",
|
||||||
|
},
|
||||||
|
Prompt: "Schnellbefehle",
|
||||||
|
Masks: "Alle Masken",
|
||||||
|
Clear: "Chat löschen",
|
||||||
|
Settings: "Gesprächseinstellungen",
|
||||||
|
UploadImage: "Bild hochladen",
|
||||||
|
},
|
||||||
|
Rename: "Gespräch umbenennen",
|
||||||
|
Typing: "Tippt…",
|
||||||
Input: (submitKey: string) => {
|
Input: (submitKey: string) => {
|
||||||
var inputHints = `${submitKey} um zu Senden`;
|
var inputHints = `${submitKey} senden`;
|
||||||
if (submitKey === String(SubmitKey.Enter)) {
|
if (submitKey === String(SubmitKey.Enter)) {
|
||||||
inputHints += ", Umschalt + Eingabe für Zeilenumbruch";
|
inputHints += ",Shift + Enter für Zeilenumbruch";
|
||||||
}
|
}
|
||||||
return inputHints + ", / zum Durchsuchen von Prompts";
|
return inputHints + ",/ für Autovervollständigung, : für Befehle";
|
||||||
},
|
},
|
||||||
Send: "Senden",
|
Send: "Senden",
|
||||||
Config: {
|
Config: {
|
||||||
Reset: "Reset to Default",
|
Reset: "Erinnerung löschen",
|
||||||
SaveAs: "Save as Mask",
|
SaveAs: "Als Maske speichern",
|
||||||
},
|
},
|
||||||
|
IsContext: "Voreingestellter Prompt",
|
||||||
},
|
},
|
||||||
Export: {
|
Export: {
|
||||||
Title: "Alle Nachrichten",
|
Title: "Chatverlauf teilen",
|
||||||
Copy: "Alles kopieren",
|
Copy: "Alles kopieren",
|
||||||
Download: "Herunterladen",
|
Download: "Datei herunterladen",
|
||||||
MessageFromYou: "Deine Nachricht",
|
Share: "Auf ShareGPT teilen",
|
||||||
MessageFromChatGPT: "Nachricht von ChatGPT",
|
MessageFromYou: "Benutzer",
|
||||||
|
MessageFromChatGPT: "ChatGPT",
|
||||||
|
Format: {
|
||||||
|
Title: "Exportformat",
|
||||||
|
SubTitle: "Kann als Markdown-Text oder PNG-Bild exportiert werden",
|
||||||
|
},
|
||||||
|
IncludeContext: {
|
||||||
|
Title: "Maske Kontext einbeziehen",
|
||||||
|
SubTitle: "Soll der Maskenkontext in den Nachrichten angezeigt werden?",
|
||||||
|
},
|
||||||
|
Steps: {
|
||||||
|
Select: "Auswählen",
|
||||||
|
Preview: "Vorschau",
|
||||||
|
},
|
||||||
|
Image: {
|
||||||
|
Toast: "Screenshot wird erstellt",
|
||||||
|
Modal: "Lang drücken oder Rechtsklick, um Bild zu speichern",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Select: {
|
||||||
|
Search: "Nachrichten suchen",
|
||||||
|
All: "Alles auswählen",
|
||||||
|
Latest: "Neueste",
|
||||||
|
Clear: "Auswahl aufheben",
|
||||||
},
|
},
|
||||||
Memory: {
|
Memory: {
|
||||||
Title: "Gedächtnis-Prompt",
|
Title: "Historische Zusammenfassung",
|
||||||
EmptyContent: "Noch nichts.",
|
EmptyContent:
|
||||||
Send: "Gedächtnis senden",
|
"Gesprächsinhalte sind zu kurz, keine Zusammenfassung erforderlich",
|
||||||
Copy: "Gedächtnis kopieren",
|
Send: "Chatverlauf automatisch komprimieren und als Kontext senden",
|
||||||
Reset: "Sitzung zurücksetzen",
|
Copy: "Zusammenfassung kopieren",
|
||||||
ResetConfirm:
|
Reset: "[nicht verwendet]",
|
||||||
"Das Zurücksetzen löscht den aktuellen Gesprächsverlauf und das Langzeit-Gedächtnis. Möchten Sie wirklich zurücksetzen?",
|
ResetConfirm: "Zusammenfassung löschen bestätigen?",
|
||||||
},
|
},
|
||||||
Home: {
|
Home: {
|
||||||
NewChat: "Neuer Chat",
|
NewChat: "Neues Gespräch",
|
||||||
DeleteChat: "Bestätigen Sie, um das ausgewählte Gespräch zu löschen?",
|
DeleteChat: "Bestätigen Sie das Löschen des ausgewählten Gesprächs?",
|
||||||
DeleteToast: "Chat gelöscht",
|
DeleteToast: "Gespräch gelöscht",
|
||||||
Revert: "Zurücksetzen",
|
Revert: "Rückgängig machen",
|
||||||
},
|
},
|
||||||
Settings: {
|
Settings: {
|
||||||
Title: "Einstellungen",
|
Title: "Einstellungen",
|
||||||
SubTitle: "Alle Einstellungen",
|
SubTitle: "Alle Einstellungsmöglichkeiten",
|
||||||
|
|
||||||
|
Danger: {
|
||||||
|
Reset: {
|
||||||
|
Title: "Alle Einstellungen zurücksetzen",
|
||||||
|
SubTitle: "Setzt alle Einstellungen auf die Standardwerte zurück",
|
||||||
|
Action: "Jetzt zurücksetzen",
|
||||||
|
Confirm: "Bestätigen Sie das Zurücksetzen aller Einstellungen?",
|
||||||
|
},
|
||||||
|
Clear: {
|
||||||
|
Title: "Alle Daten löschen",
|
||||||
|
SubTitle: "Löscht alle Chats und Einstellungsdaten",
|
||||||
|
Action: "Jetzt löschen",
|
||||||
|
Confirm:
|
||||||
|
"Bestätigen Sie das Löschen aller Chats und Einstellungsdaten?",
|
||||||
|
},
|
||||||
|
},
|
||||||
Lang: {
|
Lang: {
|
||||||
Name: "Language", // ATTENTION: if you wanna add a new translation, please do not translate this value, leave it as `Language`
|
Name: "Sprache", // ACHTUNG: Wenn Sie eine neue Übersetzung hinzufügen möchten, übersetzen Sie diesen Wert bitte nicht, lassen Sie ihn als `Sprache`
|
||||||
All: "Alle Sprachen",
|
All: "Alle Sprachen",
|
||||||
},
|
},
|
||||||
Avatar: "Avatar",
|
Avatar: "Avatar",
|
||||||
FontSize: {
|
FontSize: {
|
||||||
Title: "Schriftgröße",
|
Title: "Schriftgröße",
|
||||||
SubTitle: "Schriftgröße des Chat-Inhalts anpassen",
|
SubTitle: "Schriftgröße des Chat-Inhalts",
|
||||||
},
|
},
|
||||||
FontFamily: {
|
FontFamily: {
|
||||||
Title: "Chat-Schriftart",
|
Title: "Chat-Schriftart",
|
||||||
|
@ -78,164 +175,431 @@ const de: PartialLocaleType = {
|
||||||
Placeholder: "Schriftartname",
|
Placeholder: "Schriftartname",
|
||||||
},
|
},
|
||||||
InjectSystemPrompts: {
|
InjectSystemPrompts: {
|
||||||
Title: "System-Prompts einfügen",
|
Title: "Systemweite Eingabeaufforderungen einfügen",
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Erzwingt das Hinzufügen eines simulierten systemweiten Prompts von ChatGPT am Anfang der Nachrichtenliste bei jeder Anfrage",
|
"Fügt jeder Nachricht am Anfang der Nachrichtenliste eine simulierte ChatGPT-Systemaufforderung hinzu",
|
||||||
},
|
},
|
||||||
|
InputTemplate: {
|
||||||
|
Title: "Benutzer-Eingabeverarbeitung",
|
||||||
|
SubTitle:
|
||||||
|
"Die neueste Nachricht des Benutzers wird in diese Vorlage eingefügt",
|
||||||
|
},
|
||||||
|
|
||||||
Update: {
|
Update: {
|
||||||
Version: (x: string) => `Version: ${x}`,
|
Version: (x: string) => `Aktuelle Version: ${x}`,
|
||||||
IsLatest: "Neueste Version",
|
IsLatest: "Bereits die neueste Version",
|
||||||
CheckUpdate: "Update prüfen",
|
CheckUpdate: "Auf Updates überprüfen",
|
||||||
IsChecking: "Update wird geprüft...",
|
IsChecking: "Überprüfe auf Updates...",
|
||||||
FoundUpdate: (x: string) => `Neue Version gefunden: ${x}`,
|
FoundUpdate: (x: string) => `Neue Version gefunden: ${x}`,
|
||||||
GoToUpdate: "Aktualisieren",
|
GoToUpdate: "Zum Update gehen",
|
||||||
},
|
},
|
||||||
SendKey: "Senden-Taste",
|
SendKey: "Sende-Taste",
|
||||||
Theme: "Erscheinungsbild",
|
Theme: "Thema",
|
||||||
TightBorder: "Enger Rahmen",
|
TightBorder: "Randloser Modus",
|
||||||
SendPreviewBubble: {
|
SendPreviewBubble: {
|
||||||
Title: "Vorschau-Bubble senden",
|
Title: "Vorschau-Bubble",
|
||||||
SubTitle: "Preview markdown in bubble",
|
SubTitle: "Markdown-Inhalt in der Vorschau-Bubble anzeigen",
|
||||||
|
},
|
||||||
|
AutoGenerateTitle: {
|
||||||
|
Title: "Titel automatisch generieren",
|
||||||
|
SubTitle:
|
||||||
|
"Basierend auf dem Chat-Inhalt einen passenden Titel generieren",
|
||||||
|
},
|
||||||
|
Sync: {
|
||||||
|
CloudState: "Cloud-Daten",
|
||||||
|
NotSyncYet: "Noch nicht synchronisiert",
|
||||||
|
Success: "Synchronisation erfolgreich",
|
||||||
|
Fail: "Synchronisation fehlgeschlagen",
|
||||||
|
|
||||||
|
Config: {
|
||||||
|
Modal: {
|
||||||
|
Title: "Cloud-Synchronisation konfigurieren",
|
||||||
|
Check: "Verfügbarkeit überprüfen",
|
||||||
|
},
|
||||||
|
SyncType: {
|
||||||
|
Title: "Synchronisationstyp",
|
||||||
|
SubTitle: "Wählen Sie den bevorzugten Synchronisationsserver aus",
|
||||||
|
},
|
||||||
|
Proxy: {
|
||||||
|
Title: "Proxy aktivieren",
|
||||||
|
SubTitle:
|
||||||
|
"Beim Synchronisieren im Browser muss ein Proxy aktiviert werden, um Cross-Origin-Beschränkungen zu vermeiden",
|
||||||
|
},
|
||||||
|
ProxyUrl: {
|
||||||
|
Title: "Proxy-Adresse",
|
||||||
|
SubTitle: "Nur für projektinterne Cross-Origin-Proxy",
|
||||||
|
},
|
||||||
|
|
||||||
|
WebDav: {
|
||||||
|
Endpoint: "WebDAV-Adresse",
|
||||||
|
UserName: "Benutzername",
|
||||||
|
Password: "Passwort",
|
||||||
|
},
|
||||||
|
|
||||||
|
UpStash: {
|
||||||
|
Endpoint: "UpStash Redis REST-Url",
|
||||||
|
UserName: "Sicherungsname",
|
||||||
|
Password: "UpStash Redis REST-Token",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
LocalState: "Lokale Daten",
|
||||||
|
Overview: (overview: any) => {
|
||||||
|
return `${overview.chat} Chats, ${overview.message} Nachrichten, ${overview.prompt} Eingabeaufforderungen, ${overview.mask} Masken`;
|
||||||
|
},
|
||||||
|
ImportFailed: "Import fehlgeschlagen",
|
||||||
},
|
},
|
||||||
Mask: {
|
Mask: {
|
||||||
Splash: {
|
Splash: {
|
||||||
Title: "Mask Splash Screen",
|
Title: "Masken-Startseite",
|
||||||
SubTitle: "Show a mask splash screen before starting new chat",
|
SubTitle:
|
||||||
|
"Zeige die Masken-Startseite beim Erstellen eines neuen Chats",
|
||||||
|
},
|
||||||
|
Builtin: {
|
||||||
|
Title: "Eingebaute Masken ausblenden",
|
||||||
|
SubTitle: "Blendet eingebaute Masken in allen Maskenlisten aus",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Prompt: {
|
Prompt: {
|
||||||
Disable: {
|
Disable: {
|
||||||
Title: "Autovervollständigung deaktivieren",
|
Title: "Automatische Eingabeaufforderung deaktivieren",
|
||||||
SubTitle: "Autovervollständigung mit / starten",
|
SubTitle:
|
||||||
|
"Geben Sie am Anfang des Eingabefelds / ein, um die automatische Vervollständigung auszulösen",
|
||||||
},
|
},
|
||||||
List: "Prompt-Liste",
|
List: "Benutzerdefinierte Eingabeaufforderungsliste",
|
||||||
ListCount: (builtin: number, custom: number) =>
|
ListCount: (builtin: number, custom: number) =>
|
||||||
`${builtin} integriert, ${custom} benutzerdefiniert`,
|
`Eingebaut ${builtin} Stück, Benutzerdefiniert ${custom} Stück`,
|
||||||
Edit: "Bearbeiten",
|
Edit: "Bearbeiten",
|
||||||
Modal: {
|
Modal: {
|
||||||
Title: "Prompt List",
|
Title: "Eingabeaufforderungsliste",
|
||||||
Add: "Add One",
|
Add: "Neu erstellen",
|
||||||
Search: "Search Prompts",
|
Search: "Eingabeaufforderungen suchen",
|
||||||
},
|
},
|
||||||
EditModal: {
|
EditModal: {
|
||||||
Title: "Edit Prompt",
|
Title: "Eingabeaufforderung bearbeiten",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
HistoryCount: {
|
HistoryCount: {
|
||||||
Title: "Anzahl der angehängten Nachrichten",
|
Title: "Anzahl der historischen Nachrichten",
|
||||||
SubTitle: "Anzahl der pro Anfrage angehängten gesendeten Nachrichten",
|
SubTitle:
|
||||||
|
"Anzahl der historischen Nachrichten, die bei jeder Anfrage mitgesendet werden",
|
||||||
},
|
},
|
||||||
CompressThreshold: {
|
CompressThreshold: {
|
||||||
Title: "Schwellenwert für Verlaufskomprimierung",
|
Title: "Komprimierungsschwelle für historische Nachrichtenlänge",
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Komprimierung, wenn die Länge der unkomprimierten Nachrichten den Wert überschreitet",
|
"Wenn die unkomprimierten historischen Nachrichten diesen Wert überschreiten, wird komprimiert",
|
||||||
},
|
},
|
||||||
|
|
||||||
Usage: {
|
Usage: {
|
||||||
Title: "Kontostand",
|
Title: "Guthabenabfrage",
|
||||||
SubTitle(used: any, total: any) {
|
SubTitle(used: any, total: any) {
|
||||||
return `Diesen Monat ausgegeben $${used}, Abonnement $${total}`;
|
return `In diesem Monat verwendet $${used}, Abonnement insgesamt $${total}`;
|
||||||
},
|
},
|
||||||
IsChecking: "Wird überprüft...",
|
IsChecking: "Wird überprüft…",
|
||||||
Check: "Erneut prüfen",
|
Check: "Erneut überprüfen",
|
||||||
NoAccess: "API-Schlüssel eingeben, um den Kontostand zu überprüfen",
|
NoAccess:
|
||||||
|
"Geben Sie API-Schlüssel oder Zugangspasswort ein, um das Guthaben einzusehen",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Access: {
|
||||||
|
SaasStart: {
|
||||||
|
Title: "NextChat AI verwenden",
|
||||||
|
Label: "(Die kosteneffektivste Lösung)",
|
||||||
|
SubTitle:
|
||||||
|
"Offiziell von NextChat verwaltet, sofort einsatzbereit ohne Konfiguration, unterstützt die neuesten großen Modelle wie OpenAI o1, GPT-4o und Claude-3.5",
|
||||||
|
ChatNow: "Jetzt chatten",
|
||||||
|
},
|
||||||
|
|
||||||
|
AccessCode: {
|
||||||
|
Title: "Zugangscode",
|
||||||
|
SubTitle:
|
||||||
|
"Der Administrator hat die verschlüsselte Zugriffskontrolle aktiviert",
|
||||||
|
Placeholder: "Geben Sie den Zugangscode ein",
|
||||||
|
},
|
||||||
|
CustomEndpoint: {
|
||||||
|
Title: "Benutzerdefinierte Schnittstelle",
|
||||||
|
SubTitle: "Benutzerdefinierte Azure- oder OpenAI-Dienste verwenden",
|
||||||
|
},
|
||||||
|
Provider: {
|
||||||
|
Title: "Modellanbieter",
|
||||||
|
SubTitle: "Wechseln Sie zu verschiedenen Anbietern",
|
||||||
|
},
|
||||||
|
OpenAI: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "API-Schlüssel",
|
||||||
|
SubTitle:
|
||||||
|
"Verwenden Sie benutzerdefinierten OpenAI-Schlüssel, um Passwortzugangsbeschränkungen zu umgehen",
|
||||||
|
Placeholder: "OpenAI API-Schlüssel",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Schnittstellenadresse",
|
||||||
|
SubTitle: "Neben der Standardadresse muss http(s):// enthalten sein",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Azure: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "Schnittstellenschlüssel",
|
||||||
|
SubTitle:
|
||||||
|
"Verwenden Sie benutzerdefinierten Azure-Schlüssel, um Passwortzugangsbeschränkungen zu umgehen",
|
||||||
|
Placeholder: "Azure API-Schlüssel",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Schnittstellenadresse",
|
||||||
|
SubTitle: "Beispiel:",
|
||||||
|
},
|
||||||
|
|
||||||
|
ApiVerion: {
|
||||||
|
Title: "Schnittstellenversion (azure api version)",
|
||||||
|
SubTitle: "Wählen Sie eine spezifische Teilversion aus",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Anthropic: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "Schnittstellenschlüssel",
|
||||||
|
SubTitle:
|
||||||
|
"Verwenden Sie benutzerdefinierten Anthropic-Schlüssel, um Passwortzugangsbeschränkungen zu umgehen",
|
||||||
|
Placeholder: "Anthropic API-Schlüssel",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Schnittstellenadresse",
|
||||||
|
SubTitle: "Beispiel:",
|
||||||
|
},
|
||||||
|
|
||||||
|
ApiVerion: {
|
||||||
|
Title: "Schnittstellenversion (claude api version)",
|
||||||
|
SubTitle: "Wählen Sie eine spezifische API-Version aus",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Google: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "API-Schlüssel",
|
||||||
|
SubTitle: "Holen Sie sich Ihren API-Schlüssel von Google AI",
|
||||||
|
Placeholder: "Geben Sie Ihren Google AI Studio API-Schlüssel ein",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Endpunktadresse",
|
||||||
|
SubTitle: "Beispiel:",
|
||||||
|
},
|
||||||
|
|
||||||
|
ApiVersion: {
|
||||||
|
Title: "API-Version (nur für gemini-pro)",
|
||||||
|
SubTitle: "Wählen Sie eine spezifische API-Version aus",
|
||||||
|
},
|
||||||
|
GoogleSafetySettings: {
|
||||||
|
Title: "Google Sicherheitsfilterstufe",
|
||||||
|
SubTitle: "Inhaltfilterstufe einstellen",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Baidu: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "API-Schlüssel",
|
||||||
|
SubTitle: "Verwenden Sie benutzerdefinierten Baidu API-Schlüssel",
|
||||||
|
Placeholder: "Baidu API-Schlüssel",
|
||||||
|
},
|
||||||
|
SecretKey: {
|
||||||
|
Title: "Geheimschlüssel",
|
||||||
|
SubTitle: "Verwenden Sie benutzerdefinierten Baidu Geheimschlüssel",
|
||||||
|
Placeholder: "Baidu Geheimschlüssel",
|
||||||
|
},
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Schnittstellenadresse",
|
||||||
|
SubTitle:
|
||||||
|
"Keine benutzerdefinierten Adressen unterstützen, konfigurieren Sie in .env",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ByteDance: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "Schnittstellenschlüssel",
|
||||||
|
SubTitle: "Verwenden Sie benutzerdefinierten ByteDance API-Schlüssel",
|
||||||
|
Placeholder: "ByteDance API-Schlüssel",
|
||||||
|
},
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Schnittstellenadresse",
|
||||||
|
SubTitle: "Beispiel:",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Alibaba: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "Schnittstellenschlüssel",
|
||||||
|
SubTitle:
|
||||||
|
"Verwenden Sie benutzerdefinierten Alibaba Cloud API-Schlüssel",
|
||||||
|
Placeholder: "Alibaba Cloud API-Schlüssel",
|
||||||
|
},
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Schnittstellenadresse",
|
||||||
|
SubTitle: "Beispiel:",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CustomModel: {
|
||||||
|
Title: "Benutzerdefinierter Modellname",
|
||||||
|
SubTitle:
|
||||||
|
"Fügen Sie benutzerdefinierte Modelloptionen hinzu, getrennt durch Kommas",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
Model: "Modell",
|
Model: "Modell",
|
||||||
|
CompressModel: {
|
||||||
|
Title: "Kompressionsmodell",
|
||||||
|
SubTitle: "Modell zur Komprimierung des Verlaufs",
|
||||||
|
},
|
||||||
Temperature: {
|
Temperature: {
|
||||||
Title: "Temperature", //Temperatur
|
Title: "Zufälligkeit (temperature)",
|
||||||
SubTitle: "Ein größerer Wert führt zu zufälligeren Antworten",
|
SubTitle: "Je höher der Wert, desto zufälliger die Antwort",
|
||||||
|
},
|
||||||
|
TopP: {
|
||||||
|
Title: "Kern-Sampling (top_p)",
|
||||||
|
SubTitle:
|
||||||
|
"Ähnlich der Zufälligkeit, aber nicht zusammen mit Zufälligkeit ändern",
|
||||||
},
|
},
|
||||||
MaxTokens: {
|
MaxTokens: {
|
||||||
Title: "Max Tokens", //Maximale Token
|
Title: "Maximale Token-Anzahl pro Antwort",
|
||||||
SubTitle: "Maximale Anzahl der Anfrage- plus Antwort-Token",
|
SubTitle: "Maximale Anzahl der Tokens pro Interaktion",
|
||||||
},
|
},
|
||||||
PresencePenalty: {
|
PresencePenalty: {
|
||||||
Title: "Presence Penalty", //Anwesenheitsstrafe
|
Title: "Themenfrische (presence_penalty)",
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Ein größerer Wert erhöht die Wahrscheinlichkeit, dass über neue Themen gesprochen wird",
|
"Je höher der Wert, desto wahrscheinlicher wird auf neue Themen eingegangen",
|
||||||
},
|
},
|
||||||
FrequencyPenalty: {
|
FrequencyPenalty: {
|
||||||
Title: "Frequency Penalty", // HäufigkeitStrafe
|
Title: "Häufigkeitsstrafe (frequency_penalty)",
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Ein größerer Wert, der die Wahrscheinlichkeit verringert, dass dieselbe Zeile wiederholt wird",
|
"Je höher der Wert, desto wahrscheinlicher werden wiederholte Wörter reduziert",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Store: {
|
Store: {
|
||||||
DefaultTopic: "Neues Gespräch",
|
DefaultTopic: "Neuer Chat",
|
||||||
BotHello: "Hallo! Wie kann ich Ihnen heute helfen?",
|
BotHello: "Wie kann ich Ihnen helfen?",
|
||||||
Error:
|
Error:
|
||||||
"Etwas ist schief gelaufen, bitte versuchen Sie es später noch einmal.",
|
"Ein Fehler ist aufgetreten, bitte versuchen Sie es später noch einmal",
|
||||||
Prompt: {
|
Prompt: {
|
||||||
History: (content: string) =>
|
History: (content: string) =>
|
||||||
"Dies ist eine Zusammenfassung des Chatverlaufs zwischen dem KI und dem Benutzer als Rückblick: " +
|
"Dies ist eine Zusammenfassung des bisherigen Chats als Hintergrundinformation: " +
|
||||||
content,
|
content,
|
||||||
Topic:
|
Topic:
|
||||||
"Bitte erstellen Sie einen vier- bis fünfwörtigen Titel, der unser Gespräch zusammenfasst, ohne Einleitung, Zeichensetzung, Anführungszeichen, Punkte, Symbole oder zusätzlichen Text. Entfernen Sie Anführungszeichen.",
|
"Geben Sie ein kurzes Thema in vier bis fünf Wörtern zurück, ohne Erklärungen, ohne Satzzeichen, ohne Füllwörter, ohne zusätzliche Texte und ohne Fettdruck. Wenn kein Thema vorhanden ist, geben Sie bitte „Allgemeines Gespräch“ zurück.",
|
||||||
Summarize:
|
Summarize:
|
||||||
"Fassen Sie unsere Diskussion kurz in 200 Wörtern oder weniger zusammen, um sie als Pronpt für zukünftige Gespräche zu verwenden.",
|
"Fassen Sie den Gesprächsinhalt zusammen, um als Kontextaufforderung für den nächsten Schritt zu dienen, halten Sie es unter 200 Zeichen",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Copy: {
|
Copy: {
|
||||||
Success: "In die Zwischenablage kopiert",
|
Success: "In die Zwischenablage geschrieben",
|
||||||
Failed:
|
Failed:
|
||||||
"Kopieren fehlgeschlagen, bitte geben Sie die Berechtigung zum Zugriff auf die Zwischenablage frei",
|
"Kopieren fehlgeschlagen, bitte erlauben Sie Zugriff auf die Zwischenablage",
|
||||||
|
},
|
||||||
|
Download: {
|
||||||
|
Success: "Inhalt wurde in Ihrem Verzeichnis heruntergeladen.",
|
||||||
|
Failed: "Download fehlgeschlagen.",
|
||||||
},
|
},
|
||||||
Context: {
|
Context: {
|
||||||
Toast: (x: any) => `Mit ${x} Kontext-Prompts`,
|
Toast: (x: any) => `Beinhaltet ${x} vordefinierte Eingabeaufforderungen`,
|
||||||
Edit: "Kontext- und Gedächtnis-Prompts",
|
Edit: "Aktuelle Gesprächseinstellungen",
|
||||||
Add: "Hinzufügen",
|
Add: "Neues Gespräch hinzufügen",
|
||||||
|
Clear: "Kontext gelöscht",
|
||||||
|
Revert: "Kontext wiederherstellen",
|
||||||
},
|
},
|
||||||
Plugin: {
|
Plugin: {
|
||||||
Name: "Plugin",
|
Name: "Plugins",
|
||||||
},
|
},
|
||||||
FineTuned: {
|
FineTuned: {
|
||||||
Sysmessage: "Du bist ein Assistent, der",
|
Sysmessage: "Du bist ein Assistent",
|
||||||
},
|
},
|
||||||
Mask: {
|
SearchChat: {
|
||||||
Name: "Mask",
|
Name: "Suche",
|
||||||
Page: {
|
Page: {
|
||||||
Title: "Prompt Template",
|
Title: "Chatverlauf durchsuchen",
|
||||||
SubTitle: (count: number) => `${count} prompt templates`,
|
Search: "Suchbegriff eingeben",
|
||||||
Search: "Search Templates",
|
NoResult: "Keine Ergebnisse gefunden",
|
||||||
Create: "Create",
|
NoData: "Keine Daten",
|
||||||
|
Loading: "Laden",
|
||||||
|
|
||||||
|
SubTitle: (count: number) => `${count} Ergebnisse gefunden`,
|
||||||
},
|
},
|
||||||
Item: {
|
Item: {
|
||||||
Info: (count: number) => `${count} prompts`,
|
View: "Ansehen",
|
||||||
Chat: "Chat",
|
},
|
||||||
View: "View",
|
},
|
||||||
Edit: "Edit",
|
Mask: {
|
||||||
Delete: "Delete",
|
Name: "Masken",
|
||||||
DeleteConfirm: "Confirm to delete?",
|
Page: {
|
||||||
|
Title: "Vordefinierte Rollenmasken",
|
||||||
|
SubTitle: (count: number) =>
|
||||||
|
`${count} vordefinierte Rollenbeschreibungen`,
|
||||||
|
Search: "Rollenmasken suchen",
|
||||||
|
Create: "Neu erstellen",
|
||||||
|
},
|
||||||
|
Item: {
|
||||||
|
Info: (count: number) => `Beinhaltet ${count} vordefinierte Gespräche`,
|
||||||
|
Chat: "Gespräch",
|
||||||
|
View: "Anzeigen",
|
||||||
|
Edit: "Bearbeiten",
|
||||||
|
Delete: "Löschen",
|
||||||
|
DeleteConfirm: "Bestätigen Sie das Löschen?",
|
||||||
},
|
},
|
||||||
EditModal: {
|
EditModal: {
|
||||||
Title: (readonly: boolean) =>
|
Title: (readonly: boolean) =>
|
||||||
`Edit Prompt Template ${readonly ? "(readonly)" : ""}`,
|
`Vordefinierte Maske bearbeiten ${readonly ? "(Nur lesen)" : ""}`,
|
||||||
Download: "Download",
|
Download: "Vorgabe herunterladen",
|
||||||
Clone: "Clone",
|
Clone: "Vorgabe klonen",
|
||||||
},
|
},
|
||||||
Config: {
|
Config: {
|
||||||
Avatar: "Bot Avatar",
|
Avatar: "Rollen-Avatar",
|
||||||
Name: "Bot Name",
|
Name: "Rollenname",
|
||||||
|
Sync: {
|
||||||
|
Title: "Globale Einstellungen verwenden",
|
||||||
|
SubTitle:
|
||||||
|
"Soll das aktuelle Gespräch die globalen Modelleinstellungen verwenden?",
|
||||||
|
Confirm:
|
||||||
|
"Die benutzerdefinierten Einstellungen des aktuellen Gesprächs werden automatisch überschrieben. Bestätigen Sie, dass Sie die globalen Einstellungen aktivieren möchten?",
|
||||||
|
},
|
||||||
|
HideContext: {
|
||||||
|
Title: "Vordefinierte Gespräche ausblenden",
|
||||||
|
SubTitle:
|
||||||
|
"Nach dem Ausblenden werden vordefinierte Gespräche nicht mehr im Chat angezeigt",
|
||||||
|
},
|
||||||
|
Share: {
|
||||||
|
Title: "Diese Maske teilen",
|
||||||
|
SubTitle: "Generieren Sie einen Direktlink zu dieser Maske",
|
||||||
|
Action: "Link kopieren",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
NewChat: {
|
NewChat: {
|
||||||
Return: "Return",
|
Return: "Zurück",
|
||||||
Skip: "Skip",
|
Skip: "Direkt beginnen",
|
||||||
Title: "Pick a Mask",
|
NotShow: "Nicht mehr anzeigen",
|
||||||
SubTitle: "Chat with the Soul behind the Mask",
|
ConfirmNoShow:
|
||||||
More: "Find More",
|
"Bestätigen Sie die Deaktivierung? Nach der Deaktivierung können Sie jederzeit in den Einstellungen wieder aktivieren.",
|
||||||
NotShow: "Not Show Again",
|
Title: "Wählen Sie eine Maske aus",
|
||||||
ConfirmNoShow: "Confirm to disable?You can enable it in settings later.",
|
SubTitle:
|
||||||
|
"Starten Sie jetzt und lassen Sie sich von den Gedanken hinter der Maske inspirieren",
|
||||||
|
More: "Alle anzeigen",
|
||||||
|
},
|
||||||
|
|
||||||
|
URLCommand: {
|
||||||
|
Code: "Ein Zugangscode wurde im Link gefunden. Möchten Sie diesen automatisch einfügen?",
|
||||||
|
Settings:
|
||||||
|
"Vordefinierte Einstellungen wurden im Link gefunden. Möchten Sie diese automatisch einfügen?",
|
||||||
},
|
},
|
||||||
|
|
||||||
UI: {
|
UI: {
|
||||||
Confirm: "Confirm",
|
Confirm: "Bestätigen",
|
||||||
Cancel: "Cancel",
|
Cancel: "Abbrechen",
|
||||||
Close: "Close",
|
Close: "Schließen",
|
||||||
Create: "Create",
|
Create: "Neu erstellen",
|
||||||
Edit: "Edit",
|
Edit: "Bearbeiten",
|
||||||
|
Export: "Exportieren",
|
||||||
|
Import: "Importieren",
|
||||||
|
Sync: "Synchronisieren",
|
||||||
|
Config: "Konfigurieren",
|
||||||
},
|
},
|
||||||
Exporter: {
|
Exporter: {
|
||||||
|
Description: {
|
||||||
|
Title: "Nur Nachrichten nach dem Löschen des Kontexts werden angezeigt",
|
||||||
|
},
|
||||||
Model: "Modell",
|
Model: "Modell",
|
||||||
Messages: "Nachrichten",
|
Messages: "Nachrichten",
|
||||||
Topic: "Thema",
|
Topic: "Thema",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { getClientConfig } from "../config/client";
|
import { getClientConfig } from "../config/client";
|
||||||
import { SubmitKey } from "../store/config";
|
import { SubmitKey } from "../store/config";
|
||||||
import { LocaleType } from "./index";
|
import { LocaleType } from "./index";
|
||||||
|
import { SAAS_CHAT_UTM_URL } from "@/app/constant";
|
||||||
// if you are adding a new translation, please use PartialLocaleType instead of LocaleType
|
// if you are adding a new translation, please use PartialLocaleType instead of LocaleType
|
||||||
|
|
||||||
const isApp = !!getClientConfig()?.isApp;
|
const isApp = !!getClientConfig()?.isApp;
|
||||||
|
@ -9,16 +9,26 @@ const en: LocaleType = {
|
||||||
WIP: "Coming Soon...",
|
WIP: "Coming Soon...",
|
||||||
Error: {
|
Error: {
|
||||||
Unauthorized: isApp
|
Unauthorized: isApp
|
||||||
? "Invalid API Key, please check it in [Settings](/#/settings) page."
|
? `😆 Oops, there's an issue. No worries:
|
||||||
: "Unauthorized access, please enter access code in [auth](/#/auth) page, or enter your OpenAI API Key.",
|
\\ 1️⃣ New here? [Click to start chatting now 🚀](${SAAS_CHAT_UTM_URL})
|
||||||
|
\\ 2️⃣ Want to use your own OpenAI resources? [Click here](/#/settings) to change settings ⚙️`
|
||||||
|
: `😆 Oops, there's an issue. Let's fix it:
|
||||||
|
\ 1️⃣ New here? [Click to start chatting now 🚀](${SAAS_CHAT_UTM_URL})
|
||||||
|
\ 2️⃣ Using a private setup? [Click here](/#/auth) to enter your key 🔑
|
||||||
|
\ 3️⃣ Want to use your own OpenAI resources? [Click here](/#/settings) to change settings ⚙️
|
||||||
|
`,
|
||||||
},
|
},
|
||||||
Auth: {
|
Auth: {
|
||||||
|
Return: "Return",
|
||||||
Title: "Need Access Code",
|
Title: "Need Access Code",
|
||||||
Tips: "Please enter access code below",
|
Tips: "Please enter access code below",
|
||||||
SubTips: "Or enter your OpenAI or Google API Key",
|
SubTips: "Or enter your OpenAI or Google API Key",
|
||||||
Input: "access code",
|
Input: "access code",
|
||||||
Confirm: "Confirm",
|
Confirm: "Confirm",
|
||||||
Later: "Later",
|
Later: "Later",
|
||||||
|
SaasTips: "Too Complex, Use Immediately Now",
|
||||||
|
TopTips:
|
||||||
|
"🥳 NextChat AI launch promotion: Instantly unlock the latest models like OpenAI o1, GPT-4o, Claude-3.5!",
|
||||||
},
|
},
|
||||||
ChatItem: {
|
ChatItem: {
|
||||||
ChatItemCount: (count: number) => `${count} messages`,
|
ChatItemCount: (count: number) => `${count} messages`,
|
||||||
|
@ -44,6 +54,11 @@ const en: LocaleType = {
|
||||||
PinToastAction: "View",
|
PinToastAction: "View",
|
||||||
Delete: "Delete",
|
Delete: "Delete",
|
||||||
Edit: "Edit",
|
Edit: "Edit",
|
||||||
|
FullScreen: "FullScreen",
|
||||||
|
RefreshTitle: "Refresh Title",
|
||||||
|
RefreshToast: "Title refresh request sent",
|
||||||
|
Speech: "Play",
|
||||||
|
StopSpeech: "Stop",
|
||||||
},
|
},
|
||||||
Commands: {
|
Commands: {
|
||||||
new: "Start a new chat",
|
new: "Start a new chat",
|
||||||
|
@ -51,6 +66,7 @@ const en: LocaleType = {
|
||||||
next: "Next Chat",
|
next: "Next Chat",
|
||||||
prev: "Previous Chat",
|
prev: "Previous Chat",
|
||||||
clear: "Clear Context",
|
clear: "Clear Context",
|
||||||
|
fork: "Copy Chat",
|
||||||
del: "Delete Chat",
|
del: "Delete Chat",
|
||||||
},
|
},
|
||||||
InputActions: {
|
InputActions: {
|
||||||
|
@ -77,11 +93,21 @@ const en: LocaleType = {
|
||||||
return inputHints + ", / to search prompts, : to use commands";
|
return inputHints + ", / to search prompts, : to use commands";
|
||||||
},
|
},
|
||||||
Send: "Send",
|
Send: "Send",
|
||||||
|
StartSpeak: "Start Speak",
|
||||||
|
StopSpeak: "Stop Speak",
|
||||||
Config: {
|
Config: {
|
||||||
Reset: "Reset to Default",
|
Reset: "Reset to Default",
|
||||||
SaveAs: "Save as Mask",
|
SaveAs: "Save as Mask",
|
||||||
},
|
},
|
||||||
IsContext: "Contextual Prompt",
|
IsContext: "Contextual Prompt",
|
||||||
|
ShortcutKey: {
|
||||||
|
Title: "Keyboard Shortcuts",
|
||||||
|
newChat: "Open New Chat",
|
||||||
|
focusInput: "Focus Input Field",
|
||||||
|
copyLastMessage: "Copy Last Reply",
|
||||||
|
copyLastCode: "Copy Last Code Block",
|
||||||
|
showShortcutKey: "Show Shortcuts",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Export: {
|
Export: {
|
||||||
Title: "Export Messages",
|
Title: "Export Messages",
|
||||||
|
@ -135,6 +161,7 @@ const en: LocaleType = {
|
||||||
Settings: {
|
Settings: {
|
||||||
Title: "Settings",
|
Title: "Settings",
|
||||||
SubTitle: "All Settings",
|
SubTitle: "All Settings",
|
||||||
|
ShowPassword: "ShowPassword",
|
||||||
Danger: {
|
Danger: {
|
||||||
Reset: {
|
Reset: {
|
||||||
Title: "Reset All Settings",
|
Title: "Reset All Settings",
|
||||||
|
@ -284,6 +311,14 @@ const en: LocaleType = {
|
||||||
NoAccess: "Enter API Key to check balance",
|
NoAccess: "Enter API Key to check balance",
|
||||||
},
|
},
|
||||||
Access: {
|
Access: {
|
||||||
|
SaasStart: {
|
||||||
|
Title: "Use NextChat AI",
|
||||||
|
Label: " (Most Cost-Effective Option)",
|
||||||
|
SubTitle:
|
||||||
|
"Maintained by NextChat, zero setup needed, unlock OpenAI o1, GPT-4o," +
|
||||||
|
" Claude-3.5 and more",
|
||||||
|
ChatNow: "Start Now",
|
||||||
|
},
|
||||||
AccessCode: {
|
AccessCode: {
|
||||||
Title: "Access Code",
|
Title: "Access Code",
|
||||||
SubTitle: "Access control Enabled",
|
SubTitle: "Access control Enabled",
|
||||||
|
@ -420,6 +455,22 @@ const en: LocaleType = {
|
||||||
SubTitle: "Example: ",
|
SubTitle: "Example: ",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Iflytek: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "Iflytek API Key",
|
||||||
|
SubTitle: "Use a Iflytek API Key",
|
||||||
|
Placeholder: "Iflytek API Key",
|
||||||
|
},
|
||||||
|
ApiSecret: {
|
||||||
|
Title: "Iflytek API Secret",
|
||||||
|
SubTitle: "Use a Iflytek API Secret",
|
||||||
|
Placeholder: "Iflytek API Secret",
|
||||||
|
},
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Endpoint Address",
|
||||||
|
SubTitle: "Example: ",
|
||||||
|
},
|
||||||
|
},
|
||||||
CustomModel: {
|
CustomModel: {
|
||||||
Title: "Custom Models",
|
Title: "Custom Models",
|
||||||
SubTitle: "Custom model options, seperated by comma",
|
SubTitle: "Custom model options, seperated by comma",
|
||||||
|
@ -428,7 +479,7 @@ const en: LocaleType = {
|
||||||
ApiKey: {
|
ApiKey: {
|
||||||
Title: "API Key",
|
Title: "API Key",
|
||||||
SubTitle: "Obtain your API Key from Google AI",
|
SubTitle: "Obtain your API Key from Google AI",
|
||||||
Placeholder: "Enter your Google AI Studio API Key",
|
Placeholder: "Google AI API Key",
|
||||||
},
|
},
|
||||||
|
|
||||||
Endpoint: {
|
Endpoint: {
|
||||||
|
@ -448,6 +499,10 @@ const en: LocaleType = {
|
||||||
},
|
},
|
||||||
|
|
||||||
Model: "Model",
|
Model: "Model",
|
||||||
|
CompressModel: {
|
||||||
|
Title: "Compression Model",
|
||||||
|
SubTitle: "Model used to compress history",
|
||||||
|
},
|
||||||
Temperature: {
|
Temperature: {
|
||||||
Title: "Temperature",
|
Title: "Temperature",
|
||||||
SubTitle: "A larger value makes the more random output",
|
SubTitle: "A larger value makes the more random output",
|
||||||
|
@ -470,6 +525,27 @@ const en: LocaleType = {
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"A larger value decreasing the likelihood to repeat the same line",
|
"A larger value decreasing the likelihood to repeat the same line",
|
||||||
},
|
},
|
||||||
|
TTS: {
|
||||||
|
Enable: {
|
||||||
|
Title: "Enable TTS",
|
||||||
|
SubTitle: "Enable text-to-speech service",
|
||||||
|
},
|
||||||
|
Autoplay: {
|
||||||
|
Title: "Enable Autoplay",
|
||||||
|
SubTitle:
|
||||||
|
"Automatically generate speech and play, you need to enable the text-to-speech switch first",
|
||||||
|
},
|
||||||
|
Model: "Model",
|
||||||
|
Voice: {
|
||||||
|
Title: "Voice",
|
||||||
|
SubTitle: "The voice to use when generating the audio",
|
||||||
|
},
|
||||||
|
Speed: {
|
||||||
|
Title: "Speed",
|
||||||
|
SubTitle: "The speed of the generated audio",
|
||||||
|
},
|
||||||
|
Engine: "TTS Engine",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Store: {
|
Store: {
|
||||||
DefaultTopic: "New Conversation",
|
DefaultTopic: "New Conversation",
|
||||||
|
@ -499,16 +575,68 @@ const en: LocaleType = {
|
||||||
Clear: "Context Cleared",
|
Clear: "Context Cleared",
|
||||||
Revert: "Revert",
|
Revert: "Revert",
|
||||||
},
|
},
|
||||||
Plugin: {
|
|
||||||
Name: "Plugin",
|
|
||||||
Artifacts: "Artifacts",
|
|
||||||
},
|
|
||||||
Discovery: {
|
Discovery: {
|
||||||
Name: "Discovery",
|
Name: "Discovery",
|
||||||
},
|
},
|
||||||
FineTuned: {
|
FineTuned: {
|
||||||
Sysmessage: "You are an assistant that",
|
Sysmessage: "You are an assistant that",
|
||||||
},
|
},
|
||||||
|
SearchChat: {
|
||||||
|
Name: "Search",
|
||||||
|
Page: {
|
||||||
|
Title: "Search Chat History",
|
||||||
|
Search: "Enter search query to search chat history",
|
||||||
|
NoResult: "No results found",
|
||||||
|
NoData: "No data",
|
||||||
|
Loading: "Loading...",
|
||||||
|
|
||||||
|
SubTitle: (count: number) => `Found ${count} results`,
|
||||||
|
},
|
||||||
|
Item: {
|
||||||
|
View: "View",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Plugin: {
|
||||||
|
Name: "Plugin",
|
||||||
|
Page: {
|
||||||
|
Title: "Plugins",
|
||||||
|
SubTitle: (count: number) => `${count} plugins`,
|
||||||
|
Search: "Search Plugin",
|
||||||
|
Create: "Create",
|
||||||
|
Find: "You can find awesome plugins on github: ",
|
||||||
|
},
|
||||||
|
Item: {
|
||||||
|
Info: (count: number) => `${count} method`,
|
||||||
|
View: "View",
|
||||||
|
Edit: "Edit",
|
||||||
|
Delete: "Delete",
|
||||||
|
DeleteConfirm: "Confirm to delete?",
|
||||||
|
},
|
||||||
|
Auth: {
|
||||||
|
None: "None",
|
||||||
|
Basic: "Basic",
|
||||||
|
Bearer: "Bearer",
|
||||||
|
Custom: "Custom",
|
||||||
|
CustomHeader: "Parameter Name",
|
||||||
|
Token: "Token",
|
||||||
|
Proxy: "Using Proxy",
|
||||||
|
ProxyDescription: "Using proxies to solve CORS error",
|
||||||
|
Location: "Location",
|
||||||
|
LocationHeader: "Header",
|
||||||
|
LocationQuery: "Query",
|
||||||
|
LocationBody: "Body",
|
||||||
|
},
|
||||||
|
EditModal: {
|
||||||
|
Title: (readonly: boolean) =>
|
||||||
|
`Edit Plugin ${readonly ? "(readonly)" : ""}`,
|
||||||
|
Download: "Download",
|
||||||
|
Auth: "Authentication Type",
|
||||||
|
Content: "OpenAPI Schema",
|
||||||
|
Load: "Load From URL",
|
||||||
|
Method: "Method",
|
||||||
|
Error: "OpenAPI Schema Error",
|
||||||
|
},
|
||||||
|
},
|
||||||
Mask: {
|
Mask: {
|
||||||
Name: "Mask",
|
Name: "Mask",
|
||||||
Page: {
|
Page: {
|
||||||
|
@ -543,6 +671,10 @@ const en: LocaleType = {
|
||||||
Title: "Hide Context Prompts",
|
Title: "Hide Context Prompts",
|
||||||
SubTitle: "Do not show in-context prompts in chat",
|
SubTitle: "Do not show in-context prompts in chat",
|
||||||
},
|
},
|
||||||
|
Artifacts: {
|
||||||
|
Title: "Enable Artifacts",
|
||||||
|
SubTitle: "Can render HTML page when enable artifacts.",
|
||||||
|
},
|
||||||
Share: {
|
Share: {
|
||||||
Title: "Share This Mask",
|
Title: "Share This Mask",
|
||||||
SubTitle: "Generate a link to this mask",
|
SubTitle: "Generate a link to this mask",
|
||||||
|
|
|
@ -1,75 +1,176 @@
|
||||||
import { SubmitKey } from "../store/config";
|
import { SubmitKey } from "../store/config";
|
||||||
import type { PartialLocaleType } from "./index";
|
import type { PartialLocaleType } from "./index";
|
||||||
|
import { getClientConfig } from "../config/client";
|
||||||
|
import { SAAS_CHAT_UTM_URL } from "@/app/constant";
|
||||||
|
const isApp = !!getClientConfig()?.isApp;
|
||||||
|
|
||||||
const es: PartialLocaleType = {
|
const es: PartialLocaleType = {
|
||||||
WIP: "En construcción...",
|
WIP: "En construcción...",
|
||||||
Error: {
|
Error: {
|
||||||
Unauthorized:
|
Unauthorized: isApp
|
||||||
"Acceso no autorizado, por favor ingrese el código de acceso en la [página](/#/auth) de configuración.",
|
? `😆 La conversación encontró algunos problemas, no te preocupes:
|
||||||
|
\\ 1️⃣ Si deseas comenzar sin configuración, [haz clic aquí para empezar a chatear inmediatamente 🚀](${SAAS_CHAT_UTM_URL})
|
||||||
|
\\ 2️⃣ Si deseas usar tus propios recursos de OpenAI, haz clic [aquí](/#/settings) para modificar la configuración ⚙️`
|
||||||
|
: `😆 La conversación encontró algunos problemas, no te preocupes:
|
||||||
|
\ 1️⃣ Si deseas comenzar sin configuración, [haz clic aquí para empezar a chatear inmediatamente 🚀](${SAAS_CHAT_UTM_URL})
|
||||||
|
\ 2️⃣ Si estás utilizando una versión de implementación privada, haz clic [aquí](/#/auth) para ingresar la clave de acceso 🔑
|
||||||
|
\ 3️⃣ Si deseas usar tus propios recursos de OpenAI, haz clic [aquí](/#/settings) para modificar la configuración ⚙️
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
Auth: {
|
||||||
|
Title: "Se requiere contraseña",
|
||||||
|
Tips: "El administrador ha habilitado la verificación de contraseña. Introduce el código de acceso a continuación",
|
||||||
|
SubTips: "O ingresa tu clave API de OpenAI o Google",
|
||||||
|
Input: "Introduce el código de acceso aquí",
|
||||||
|
Confirm: "Confirmar",
|
||||||
|
Later: "Más tarde",
|
||||||
|
Return: "Regresar",
|
||||||
|
SaasTips:
|
||||||
|
"La configuración es demasiado complicada, quiero usarlo de inmediato",
|
||||||
|
TopTips:
|
||||||
|
"🥳 Oferta de lanzamiento de NextChat AI, desbloquea OpenAI o1, GPT-4o, Claude-3.5 y los últimos grandes modelos",
|
||||||
},
|
},
|
||||||
ChatItem: {
|
ChatItem: {
|
||||||
ChatItemCount: (count: number) => `${count} mensajes`,
|
ChatItemCount: (count: number) => `${count} conversaciones`,
|
||||||
},
|
},
|
||||||
Chat: {
|
Chat: {
|
||||||
SubTitle: (count: number) => `${count} mensajes con ChatGPT`,
|
SubTitle: (count: number) => `Total de ${count} conversaciones`,
|
||||||
|
EditMessage: {
|
||||||
|
Title: "Editar registro de mensajes",
|
||||||
|
Topic: {
|
||||||
|
Title: "Tema de la conversación",
|
||||||
|
SubTitle: "Cambiar el tema de la conversación actual",
|
||||||
|
},
|
||||||
|
},
|
||||||
Actions: {
|
Actions: {
|
||||||
ChatList: "Ir a la lista de chats",
|
ChatList: "Ver lista de mensajes",
|
||||||
CompressedHistory: "Historial de memoria comprimido",
|
CompressedHistory: "Ver historial de Prompts comprimidos",
|
||||||
Export: "Exportar todos los mensajes como Markdown",
|
Export: "Exportar historial de chat",
|
||||||
Copy: "Copiar",
|
Copy: "Copiar",
|
||||||
Stop: "Detener",
|
Stop: "Detener",
|
||||||
Retry: "Reintentar",
|
Retry: "Reintentar",
|
||||||
Delete: "Delete",
|
Pin: "Fijar",
|
||||||
|
PinToastContent:
|
||||||
|
"Se ha fijado 1 conversación a los prompts predeterminados",
|
||||||
|
PinToastAction: "Ver",
|
||||||
|
Delete: "Eliminar",
|
||||||
|
Edit: "Editar",
|
||||||
|
RefreshTitle: "Actualizar título",
|
||||||
|
RefreshToast: "Se ha enviado la solicitud de actualización del título",
|
||||||
},
|
},
|
||||||
Rename: "Renombrar chat",
|
Commands: {
|
||||||
Typing: "Escribiendo...",
|
new: "Nueva conversación",
|
||||||
|
newm: "Nueva conversación desde la máscara",
|
||||||
|
next: "Siguiente conversación",
|
||||||
|
prev: "Conversación anterior",
|
||||||
|
clear: "Limpiar contexto",
|
||||||
|
del: "Eliminar conversación",
|
||||||
|
},
|
||||||
|
InputActions: {
|
||||||
|
Stop: "Detener respuesta",
|
||||||
|
ToBottom: "Ir al más reciente",
|
||||||
|
Theme: {
|
||||||
|
auto: "Tema automático",
|
||||||
|
light: "Modo claro",
|
||||||
|
dark: "Modo oscuro",
|
||||||
|
},
|
||||||
|
Prompt: "Comandos rápidos",
|
||||||
|
Masks: "Todas las máscaras",
|
||||||
|
Clear: "Limpiar chat",
|
||||||
|
Settings: "Configuración de conversación",
|
||||||
|
UploadImage: "Subir imagen",
|
||||||
|
},
|
||||||
|
Rename: "Renombrar conversación",
|
||||||
|
Typing: "Escribiendo…",
|
||||||
Input: (submitKey: string) => {
|
Input: (submitKey: string) => {
|
||||||
var inputHints = `Escribe algo y presiona ${submitKey} para enviar`;
|
var inputHints = `${submitKey} para enviar`;
|
||||||
if (submitKey === String(SubmitKey.Enter)) {
|
if (submitKey === String(SubmitKey.Enter)) {
|
||||||
inputHints += ", presiona Shift + Enter para nueva línea";
|
inputHints += ",Shift + Enter para nueva línea";
|
||||||
}
|
}
|
||||||
return inputHints;
|
return (
|
||||||
|
inputHints + ",/ para activar autocompletado,: para activar comandos"
|
||||||
|
);
|
||||||
},
|
},
|
||||||
Send: "Enviar",
|
Send: "Enviar",
|
||||||
Config: {
|
Config: {
|
||||||
Reset: "Reset to Default",
|
Reset: "Borrar memoria",
|
||||||
SaveAs: "Save as Mask",
|
SaveAs: "Guardar como máscara",
|
||||||
},
|
},
|
||||||
|
IsContext: "Prompt predeterminado",
|
||||||
},
|
},
|
||||||
Export: {
|
Export: {
|
||||||
Title: "Todos los mensajes",
|
Title: "Compartir historial de chat",
|
||||||
Copy: "Copiar todo",
|
Copy: "Copiar todo",
|
||||||
Download: "Descargar",
|
Download: "Descargar archivo",
|
||||||
MessageFromYou: "Mensaje de ti",
|
Share: "Compartir en ShareGPT",
|
||||||
MessageFromChatGPT: "Mensaje de ChatGPT",
|
MessageFromYou: "Usuario",
|
||||||
|
MessageFromChatGPT: "ChatGPT",
|
||||||
|
Format: {
|
||||||
|
Title: "Formato de exportación",
|
||||||
|
SubTitle: "Puedes exportar como texto Markdown o imagen PNG",
|
||||||
|
},
|
||||||
|
IncludeContext: {
|
||||||
|
Title: "Incluir contexto de máscara",
|
||||||
|
SubTitle: "Mostrar contexto de máscara en los mensajes",
|
||||||
|
},
|
||||||
|
Steps: {
|
||||||
|
Select: "Seleccionar",
|
||||||
|
Preview: "Vista previa",
|
||||||
|
},
|
||||||
|
Image: {
|
||||||
|
Toast: "Generando captura de pantalla",
|
||||||
|
Modal: "Mantén presionado o haz clic derecho para guardar la imagen",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Select: {
|
||||||
|
Search: "Buscar mensajes",
|
||||||
|
All: "Seleccionar todo",
|
||||||
|
Latest: "Últimos mensajes",
|
||||||
|
Clear: "Limpiar selección",
|
||||||
},
|
},
|
||||||
Memory: {
|
Memory: {
|
||||||
Title: "Historial de memoria",
|
Title: "Resumen histórico",
|
||||||
EmptyContent: "Aún no hay nada.",
|
EmptyContent:
|
||||||
Copy: "Copiar todo",
|
"El contenido de la conversación es demasiado corto para resumir",
|
||||||
Send: "Send Memory",
|
Send: "Comprimir automáticamente el historial de chat y enviarlo como contexto",
|
||||||
Reset: "Reset Session",
|
Copy: "Copiar resumen",
|
||||||
ResetConfirm:
|
Reset: "[no usado]",
|
||||||
"Resetting will clear the current conversation history and historical memory. Are you sure you want to reset?",
|
ResetConfirm: "¿Confirmar para borrar el resumen histórico?",
|
||||||
},
|
},
|
||||||
Home: {
|
Home: {
|
||||||
NewChat: "Nuevo chat",
|
NewChat: "Nueva conversación",
|
||||||
DeleteChat: "¿Confirmar eliminación de la conversación seleccionada?",
|
DeleteChat: "¿Confirmar la eliminación de la conversación seleccionada?",
|
||||||
DeleteToast: "Chat Deleted",
|
DeleteToast: "Conversación eliminada",
|
||||||
Revert: "Revert",
|
Revert: "Deshacer",
|
||||||
},
|
},
|
||||||
Settings: {
|
Settings: {
|
||||||
Title: "Configuración",
|
Title: "Configuración",
|
||||||
SubTitle: "Todas las configuraciones",
|
SubTitle: "Todas las opciones de configuración",
|
||||||
|
|
||||||
|
Danger: {
|
||||||
|
Reset: {
|
||||||
|
Title: "Restablecer todas las configuraciones",
|
||||||
|
SubTitle:
|
||||||
|
"Restablecer todas las configuraciones a los valores predeterminados",
|
||||||
|
Action: "Restablecer ahora",
|
||||||
|
Confirm: "¿Confirmar el restablecimiento de todas las configuraciones?",
|
||||||
|
},
|
||||||
|
Clear: {
|
||||||
|
Title: "Eliminar todos los datos",
|
||||||
|
SubTitle: "Eliminar todos los chats y datos de configuración",
|
||||||
|
Action: "Eliminar ahora",
|
||||||
|
Confirm:
|
||||||
|
"¿Confirmar la eliminación de todos los chats y datos de configuración?",
|
||||||
|
},
|
||||||
|
},
|
||||||
Lang: {
|
Lang: {
|
||||||
Name: "Language", // ATTENTION: if you wanna add a new translation, please do not translate this value, leave it as `Language`
|
Name: "Language", // ATENCIÓN: si deseas agregar una nueva traducción, por favor no traduzcas este valor, déjalo como `Language`
|
||||||
All: "Todos los idiomas",
|
All: "Todos los idiomas",
|
||||||
},
|
},
|
||||||
Avatar: "Avatar",
|
Avatar: "Avatar",
|
||||||
FontSize: {
|
FontSize: {
|
||||||
Title: "Tamaño de fuente",
|
Title: "Tamaño de fuente",
|
||||||
SubTitle: "Ajustar el tamaño de fuente del contenido del chat",
|
SubTitle: "Tamaño de la fuente del contenido del chat",
|
||||||
},
|
},
|
||||||
FontFamily: {
|
FontFamily: {
|
||||||
Title: "Fuente del Chat",
|
Title: "Fuente del Chat",
|
||||||
|
@ -78,168 +179,427 @@ const es: PartialLocaleType = {
|
||||||
Placeholder: "Nombre de la Fuente",
|
Placeholder: "Nombre de la Fuente",
|
||||||
},
|
},
|
||||||
InjectSystemPrompts: {
|
InjectSystemPrompts: {
|
||||||
Title: "Inyectar Prompts del Sistema",
|
Title: "Inyectar mensajes del sistema",
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Agregar forzosamente un prompt de sistema simulado de ChatGPT al comienzo de la lista de mensajes en cada solicitud",
|
"Forzar la adición de un mensaje del sistema simulado de ChatGPT al principio de cada lista de mensajes",
|
||||||
},
|
},
|
||||||
|
InputTemplate: {
|
||||||
|
Title: "Preprocesamiento de entrada del usuario",
|
||||||
|
SubTitle: "El último mensaje del usuario se rellenará en esta plantilla",
|
||||||
|
},
|
||||||
|
|
||||||
Update: {
|
Update: {
|
||||||
Version: (x: string) => `Versión: ${x}`,
|
Version: (x: string) => `Versión actual: ${x}`,
|
||||||
IsLatest: "Última versión",
|
IsLatest: "Ya estás en la última versión",
|
||||||
CheckUpdate: "Buscar actualizaciones",
|
CheckUpdate: "Buscar actualizaciones",
|
||||||
IsChecking: "Buscando actualizaciones...",
|
IsChecking: "Buscando actualizaciones...",
|
||||||
FoundUpdate: (x: string) => `Se encontró una nueva versión: ${x}`,
|
FoundUpdate: (x: string) => `Nueva versión encontrada: ${x}`,
|
||||||
GoToUpdate: "Actualizar",
|
GoToUpdate: "Ir a actualizar",
|
||||||
},
|
},
|
||||||
SendKey: "Tecla de envío",
|
SendKey: "Tecla de enviar",
|
||||||
Theme: "Tema",
|
Theme: "Tema",
|
||||||
TightBorder: "Borde ajustado",
|
TightBorder: "Modo sin borde",
|
||||||
SendPreviewBubble: {
|
SendPreviewBubble: {
|
||||||
Title: "Enviar burbuja de vista previa",
|
Title: "Vista previa del globo",
|
||||||
SubTitle: "Preview markdown in bubble",
|
SubTitle:
|
||||||
|
"Previsualiza el contenido Markdown en un globo de vista previa",
|
||||||
|
},
|
||||||
|
AutoGenerateTitle: {
|
||||||
|
Title: "Generar título automáticamente",
|
||||||
|
SubTitle: "Generar un título adecuado basado en el contenido del chat",
|
||||||
|
},
|
||||||
|
Sync: {
|
||||||
|
CloudState: "Datos en la nube",
|
||||||
|
NotSyncYet: "Aún no se ha sincronizado",
|
||||||
|
Success: "Sincronización exitosa",
|
||||||
|
Fail: "Sincronización fallida",
|
||||||
|
|
||||||
|
Config: {
|
||||||
|
Modal: {
|
||||||
|
Title: "Configurar sincronización en la nube",
|
||||||
|
Check: "Verificar disponibilidad",
|
||||||
|
},
|
||||||
|
SyncType: {
|
||||||
|
Title: "Tipo de sincronización",
|
||||||
|
SubTitle: "Selecciona el servidor de sincronización preferido",
|
||||||
|
},
|
||||||
|
Proxy: {
|
||||||
|
Title: "Habilitar proxy",
|
||||||
|
SubTitle:
|
||||||
|
"Debes habilitar el proxy para sincronizar en el navegador y evitar restricciones de CORS",
|
||||||
|
},
|
||||||
|
ProxyUrl: {
|
||||||
|
Title: "Dirección del proxy",
|
||||||
|
SubTitle: "Solo para el proxy CORS incluido en este proyecto",
|
||||||
|
},
|
||||||
|
|
||||||
|
WebDav: {
|
||||||
|
Endpoint: "Dirección WebDAV",
|
||||||
|
UserName: "Nombre de usuario",
|
||||||
|
Password: "Contraseña",
|
||||||
|
},
|
||||||
|
|
||||||
|
UpStash: {
|
||||||
|
Endpoint: "URL de REST de UpStash Redis",
|
||||||
|
UserName: "Nombre de respaldo",
|
||||||
|
Password: "Token de REST de UpStash Redis",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
LocalState: "Datos locales",
|
||||||
|
Overview: (overview: any) => {
|
||||||
|
return `${overview.chat} conversaciones, ${overview.message} mensajes, ${overview.prompt} prompts, ${overview.mask} máscaras`;
|
||||||
|
},
|
||||||
|
ImportFailed: "Importación fallida",
|
||||||
},
|
},
|
||||||
Mask: {
|
Mask: {
|
||||||
Splash: {
|
Splash: {
|
||||||
Title: "Mask Splash Screen",
|
Title: "Pantalla de inicio de máscara",
|
||||||
SubTitle: "Show a mask splash screen before starting new chat",
|
SubTitle:
|
||||||
|
"Mostrar la pantalla de inicio de la máscara al iniciar un nuevo chat",
|
||||||
|
},
|
||||||
|
Builtin: {
|
||||||
|
Title: "Ocultar máscaras integradas",
|
||||||
|
SubTitle:
|
||||||
|
"Ocultar las máscaras integradas en todas las listas de máscaras",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Prompt: {
|
Prompt: {
|
||||||
Disable: {
|
Disable: {
|
||||||
Title: "Desactivar autocompletado",
|
Title: "Deshabilitar autocompletado de prompts",
|
||||||
SubTitle: "Escribe / para activar el autocompletado",
|
SubTitle:
|
||||||
|
"Escribe / al principio del campo de entrada para activar el autocompletado",
|
||||||
},
|
},
|
||||||
List: "Lista de autocompletado",
|
List: "Lista de prompts personalizados",
|
||||||
ListCount: (builtin: number, custom: number) =>
|
ListCount: (builtin: number, custom: number) =>
|
||||||
`${builtin} incorporado, ${custom} definido por el usuario`,
|
`Integrados ${builtin}, definidos por el usuario ${custom}`,
|
||||||
Edit: "Editar",
|
Edit: "Editar",
|
||||||
Modal: {
|
Modal: {
|
||||||
Title: "Prompt List",
|
Title: "Lista de prompts",
|
||||||
Add: "Add One",
|
Add: "Nuevo",
|
||||||
Search: "Search Prompts",
|
Search: "Buscar prompts",
|
||||||
},
|
},
|
||||||
EditModal: {
|
EditModal: {
|
||||||
Title: "Edit Prompt",
|
Title: "Editar prompt",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
HistoryCount: {
|
HistoryCount: {
|
||||||
Title: "Cantidad de mensajes adjuntos",
|
Title: "Número de mensajes históricos adjuntos",
|
||||||
SubTitle: "Número de mensajes enviados adjuntos por solicitud",
|
SubTitle: "Número de mensajes históricos enviados con cada solicitud",
|
||||||
},
|
},
|
||||||
CompressThreshold: {
|
CompressThreshold: {
|
||||||
Title: "Umbral de compresión de historial",
|
Title: "Umbral de compresión de mensajes históricos",
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Se comprimirán los mensajes si la longitud de los mensajes no comprimidos supera el valor",
|
"Cuando los mensajes históricos no comprimidos superan este valor, se realizará la compresión",
|
||||||
},
|
},
|
||||||
|
|
||||||
Usage: {
|
Usage: {
|
||||||
Title: "Saldo de la cuenta",
|
Title: "Consulta de saldo",
|
||||||
SubTitle(used: any, total: any) {
|
SubTitle(used: any, total: any) {
|
||||||
return `Usado $${used}, subscription $${total}`;
|
return `Saldo usado este mes: $${used}, total suscrito: $${total}`;
|
||||||
},
|
},
|
||||||
IsChecking: "Comprobando...",
|
IsChecking: "Verificando…",
|
||||||
Check: "Comprobar de nuevo",
|
Check: "Revisar de nuevo",
|
||||||
NoAccess: "Introduzca la clave API para comprobar el saldo",
|
NoAccess:
|
||||||
|
"Introduce la clave API o la contraseña de acceso para ver el saldo",
|
||||||
},
|
},
|
||||||
|
|
||||||
Model: "Modelo",
|
Access: {
|
||||||
|
SaasStart: {
|
||||||
|
Title: "Use NextChat AI",
|
||||||
|
Label: "(The most cost-effective solution)",
|
||||||
|
SubTitle:
|
||||||
|
"Officially maintained by NextChat, zero configuration ready to use, supports the latest large models like OpenAI o1, GPT-4o, and Claude-3.5",
|
||||||
|
ChatNow: "Chat Now",
|
||||||
|
},
|
||||||
|
|
||||||
|
AccessCode: {
|
||||||
|
Title: "Contraseña de acceso",
|
||||||
|
SubTitle: "El administrador ha habilitado el acceso encriptado",
|
||||||
|
Placeholder: "Introduce la contraseña de acceso",
|
||||||
|
},
|
||||||
|
CustomEndpoint: {
|
||||||
|
Title: "Interfaz personalizada",
|
||||||
|
SubTitle: "¿Usar servicios personalizados de Azure u OpenAI?",
|
||||||
|
},
|
||||||
|
Provider: {
|
||||||
|
Title: "Proveedor de modelos",
|
||||||
|
SubTitle: "Cambiar entre diferentes proveedores",
|
||||||
|
},
|
||||||
|
OpenAI: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "Clave API",
|
||||||
|
SubTitle:
|
||||||
|
"Usa una clave API de OpenAI personalizada para omitir la restricción de acceso por contraseña",
|
||||||
|
Placeholder: "Clave API de OpenAI",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Dirección del endpoint",
|
||||||
|
SubTitle:
|
||||||
|
"Debe incluir http(s):// además de la dirección predeterminada",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Azure: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "Clave de interfaz",
|
||||||
|
SubTitle:
|
||||||
|
"Usa una clave de Azure personalizada para omitir la restricción de acceso por contraseña",
|
||||||
|
Placeholder: "Clave API de Azure",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Dirección del endpoint",
|
||||||
|
SubTitle: "Ejemplo:",
|
||||||
|
},
|
||||||
|
|
||||||
|
ApiVerion: {
|
||||||
|
Title: "Versión de la interfaz (versión de api de azure)",
|
||||||
|
SubTitle: "Selecciona una versión específica",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Anthropic: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "Clave de interfaz",
|
||||||
|
SubTitle:
|
||||||
|
"Usa una clave de Anthropic personalizada para omitir la restricción de acceso por contraseña",
|
||||||
|
Placeholder: "Clave API de Anthropic",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Dirección del endpoint",
|
||||||
|
SubTitle: "Ejemplo:",
|
||||||
|
},
|
||||||
|
|
||||||
|
ApiVerion: {
|
||||||
|
Title: "Versión de la interfaz (versión de claude api)",
|
||||||
|
SubTitle: "Selecciona una versión específica de la API",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Google: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "Clave API",
|
||||||
|
SubTitle: "Obtén tu clave API de Google AI",
|
||||||
|
Placeholder: "Introduce tu clave API de Google AI Studio",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Dirección del endpoint",
|
||||||
|
SubTitle: "Ejemplo:",
|
||||||
|
},
|
||||||
|
|
||||||
|
ApiVersion: {
|
||||||
|
Title: "Versión de la API (solo para gemini-pro)",
|
||||||
|
SubTitle: "Selecciona una versión específica de la API",
|
||||||
|
},
|
||||||
|
GoogleSafetySettings: {
|
||||||
|
Title: "Nivel de filtrado de seguridad de Google",
|
||||||
|
SubTitle: "Configura el nivel de filtrado de contenido",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Baidu: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "Clave API",
|
||||||
|
SubTitle: "Usa una clave API de Baidu personalizada",
|
||||||
|
Placeholder: "Clave API de Baidu",
|
||||||
|
},
|
||||||
|
SecretKey: {
|
||||||
|
Title: "Clave secreta",
|
||||||
|
SubTitle: "Usa una clave secreta de Baidu personalizada",
|
||||||
|
Placeholder: "Clave secreta de Baidu",
|
||||||
|
},
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Dirección del endpoint",
|
||||||
|
SubTitle:
|
||||||
|
"No admite personalización, dirígete a .env para configurarlo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ByteDance: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "Clave de interfaz",
|
||||||
|
SubTitle: "Usa una clave API de ByteDance personalizada",
|
||||||
|
Placeholder: "Clave API de ByteDance",
|
||||||
|
},
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Dirección del endpoint",
|
||||||
|
SubTitle: "Ejemplo:",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Alibaba: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "Clave de interfaz",
|
||||||
|
SubTitle: "Usa una clave API de Alibaba Cloud personalizada",
|
||||||
|
Placeholder: "Clave API de Alibaba Cloud",
|
||||||
|
},
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Dirección del endpoint",
|
||||||
|
SubTitle: "Ejemplo:",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CustomModel: {
|
||||||
|
Title: "Nombre del modelo personalizado",
|
||||||
|
SubTitle:
|
||||||
|
"Agrega opciones de modelos personalizados, separados por comas",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
Model: "Modelo (model)",
|
||||||
|
CompressModel: {
|
||||||
|
Title: "Modelo de compresión",
|
||||||
|
SubTitle: "Modelo utilizado para comprimir el historial",
|
||||||
|
},
|
||||||
Temperature: {
|
Temperature: {
|
||||||
Title: "Temperatura",
|
Title: "Aleatoriedad (temperature)",
|
||||||
SubTitle: "Un valor mayor genera una salida más aleatoria",
|
SubTitle: "Cuanto mayor sea el valor, más aleatorio será el resultado",
|
||||||
|
},
|
||||||
|
TopP: {
|
||||||
|
Title: "Muestreo por núcleo (top_p)",
|
||||||
|
SubTitle: "Similar a la aleatoriedad, pero no cambies ambos a la vez",
|
||||||
},
|
},
|
||||||
MaxTokens: {
|
MaxTokens: {
|
||||||
Title: "Máximo de tokens",
|
Title: "Límite de tokens por respuesta (max_tokens)",
|
||||||
SubTitle: "Longitud máxima de tokens de entrada y tokens generados",
|
SubTitle: "Número máximo de tokens utilizados en una sola interacción",
|
||||||
},
|
},
|
||||||
PresencePenalty: {
|
PresencePenalty: {
|
||||||
Title: "Penalización de presencia",
|
Title: "Novedad de temas (presence_penalty)",
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Un valor mayor aumenta la probabilidad de hablar sobre nuevos temas",
|
"Cuanto mayor sea el valor, más probable es que se amplíen a nuevos temas",
|
||||||
},
|
},
|
||||||
FrequencyPenalty: {
|
FrequencyPenalty: {
|
||||||
Title: "Penalización de frecuencia",
|
Title: "Penalización de frecuencia (frequency_penalty)",
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Un valor mayor que disminuye la probabilidad de repetir la misma línea",
|
"Cuanto mayor sea el valor, más probable es que se reduzcan las palabras repetidas",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Store: {
|
Store: {
|
||||||
DefaultTopic: "Nueva conversación",
|
DefaultTopic: "Nuevo chat",
|
||||||
BotHello: "¡Hola! ¿Cómo puedo ayudarte hoy?",
|
BotHello: "¿En qué puedo ayudarte?",
|
||||||
Error: "Algo salió mal, por favor intenta nuevamente más tarde.",
|
Error: "Hubo un error, inténtalo de nuevo más tarde",
|
||||||
Prompt: {
|
Prompt: {
|
||||||
History: (content: string) =>
|
History: (content: string) =>
|
||||||
"Este es un resumen del historial del chat entre la IA y el usuario como recapitulación: " +
|
"Este es un resumen del chat histórico como referencia: " + content,
|
||||||
content,
|
|
||||||
Topic:
|
Topic:
|
||||||
"Por favor, genera un título de cuatro a cinco palabras que resuma nuestra conversación sin ningún inicio, puntuación, comillas, puntos, símbolos o texto adicional. Elimina las comillas que lo envuelven.",
|
"Devuelve un tema breve de esta frase en cuatro a cinco palabras, sin explicación, sin puntuación, sin muletillas, sin texto adicional, sin negritas. Si no hay tema, devuelve 'charlas casuales'",
|
||||||
Summarize:
|
Summarize:
|
||||||
"Resuma nuestra discusión brevemente en 200 caracteres o menos para usarlo como un recordatorio para futuros contextos.",
|
"Resume brevemente el contenido de la conversación para usar como un prompt de contexto, manteniéndolo dentro de 200 palabras",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Copy: {
|
Copy: {
|
||||||
Success: "Copiado al portapapeles",
|
Success: "Copiado al portapapeles",
|
||||||
Failed:
|
Failed: "Error al copiar, por favor otorga permisos al portapapeles",
|
||||||
"La copia falló, por favor concede permiso para acceder al portapapeles",
|
},
|
||||||
|
Download: {
|
||||||
|
Success: "Contenido descargado en tu directorio.",
|
||||||
|
Failed: "Error al descargar.",
|
||||||
},
|
},
|
||||||
Context: {
|
Context: {
|
||||||
Toast: (x: any) => `With ${x} contextual prompts`,
|
Toast: (x: any) => `Contiene ${x} prompts predefinidos`,
|
||||||
Edit: "Contextual and Memory Prompts",
|
Edit: "Configuración del chat actual",
|
||||||
Add: "Add One",
|
Add: "Agregar una conversación",
|
||||||
|
Clear: "Contexto borrado",
|
||||||
|
Revert: "Restaurar contexto",
|
||||||
},
|
},
|
||||||
Plugin: {
|
Plugin: {
|
||||||
Name: "Plugin",
|
Name: "Complemento",
|
||||||
},
|
},
|
||||||
FineTuned: {
|
FineTuned: {
|
||||||
Sysmessage: "Eres un asistente que",
|
Sysmessage: "Eres un asistente",
|
||||||
},
|
},
|
||||||
Mask: {
|
SearchChat: {
|
||||||
Name: "Mask",
|
Name: "Buscar",
|
||||||
Page: {
|
Page: {
|
||||||
Title: "Prompt Template",
|
Title: "Buscar en el historial de chat",
|
||||||
SubTitle: (count: number) => `${count} prompt templates`,
|
Search: "Ingrese la palabra clave de búsqueda",
|
||||||
Search: "Search Templates",
|
NoResult: "No se encontraron resultados",
|
||||||
Create: "Create",
|
NoData: "Sin datos",
|
||||||
|
Loading: "Cargando",
|
||||||
|
|
||||||
|
SubTitle: (count: number) => `Se encontraron ${count} resultados`,
|
||||||
},
|
},
|
||||||
Item: {
|
Item: {
|
||||||
Info: (count: number) => `${count} prompts`,
|
View: "Ver",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Mask: {
|
||||||
|
Name: "Máscara",
|
||||||
|
Page: {
|
||||||
|
Title: "Máscaras de rol predefinidas",
|
||||||
|
SubTitle: (count: number) => `${count} definiciones de rol predefinidas`,
|
||||||
|
Search: "Buscar máscara de rol",
|
||||||
|
Create: "Crear nuevo",
|
||||||
|
},
|
||||||
|
Item: {
|
||||||
|
Info: (count: number) => `Contiene ${count} conversaciones predefinidas`,
|
||||||
Chat: "Chat",
|
Chat: "Chat",
|
||||||
View: "View",
|
View: "Ver",
|
||||||
Edit: "Edit",
|
Edit: "Editar",
|
||||||
Delete: "Delete",
|
Delete: "Eliminar",
|
||||||
DeleteConfirm: "Confirm to delete?",
|
DeleteConfirm: "¿Confirmar eliminación?",
|
||||||
},
|
},
|
||||||
EditModal: {
|
EditModal: {
|
||||||
Title: (readonly: boolean) =>
|
Title: (readonly: boolean) =>
|
||||||
`Edit Prompt Template ${readonly ? "(readonly)" : ""}`,
|
`Editar máscara predefinida ${readonly ? "(solo lectura)" : ""}`,
|
||||||
Download: "Download",
|
Download: "Descargar predefinido",
|
||||||
Clone: "Clone",
|
Clone: "Clonar predefinido",
|
||||||
},
|
},
|
||||||
Config: {
|
Config: {
|
||||||
Avatar: "Bot Avatar",
|
Avatar: "Avatar del rol",
|
||||||
Name: "Bot Name",
|
Name: "Nombre del rol",
|
||||||
|
Sync: {
|
||||||
|
Title: "Usar configuración global",
|
||||||
|
SubTitle:
|
||||||
|
"¿Usar la configuración global del modelo para la conversación actual?",
|
||||||
|
Confirm:
|
||||||
|
"La configuración personalizada de la conversación actual se sobrescribirá automáticamente, ¿confirmar habilitar la configuración global?",
|
||||||
|
},
|
||||||
|
HideContext: {
|
||||||
|
Title: "Ocultar conversaciones predefinidas",
|
||||||
|
SubTitle:
|
||||||
|
"Las conversaciones predefinidas ocultas no aparecerán en la interfaz de chat",
|
||||||
|
},
|
||||||
|
Share: {
|
||||||
|
Title: "Compartir esta máscara",
|
||||||
|
SubTitle: "Generar un enlace directo a esta máscara",
|
||||||
|
Action: "Copiar enlace",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
NewChat: {
|
NewChat: {
|
||||||
Return: "Return",
|
Return: "Regresar",
|
||||||
Skip: "Skip",
|
Skip: "Comenzar ahora",
|
||||||
Title: "Pick a Mask",
|
NotShow: "No mostrar más",
|
||||||
SubTitle: "Chat with the Soul behind the Mask",
|
ConfirmNoShow:
|
||||||
More: "Find More",
|
"¿Confirmar desactivación? Puedes reactivar en la configuración en cualquier momento.",
|
||||||
NotShow: "Not Show Again",
|
Title: "Selecciona una máscara",
|
||||||
ConfirmNoShow: "Confirm to disable?You can enable it in settings later.",
|
SubTitle: "Comienza ahora y colisiona con la mente detrás de la máscara",
|
||||||
|
More: "Ver todo",
|
||||||
|
},
|
||||||
|
|
||||||
|
URLCommand: {
|
||||||
|
Code: "Detectado un código de acceso en el enlace, ¿deseas autocompletarlo?",
|
||||||
|
Settings:
|
||||||
|
"Detectada configuración predefinida en el enlace, ¿deseas autocompletarla?",
|
||||||
},
|
},
|
||||||
|
|
||||||
UI: {
|
UI: {
|
||||||
Confirm: "Confirm",
|
Confirm: "Confirmar",
|
||||||
Cancel: "Cancel",
|
Cancel: "Cancelar",
|
||||||
Close: "Close",
|
Close: "Cerrar",
|
||||||
Create: "Create",
|
Create: "Crear",
|
||||||
Edit: "Edit",
|
Edit: "Editar",
|
||||||
|
Export: "Exportar",
|
||||||
|
Import: "Importar",
|
||||||
|
Sync: "Sincronizar",
|
||||||
|
Config: "Configurar",
|
||||||
},
|
},
|
||||||
Exporter: {
|
Exporter: {
|
||||||
|
Description: {
|
||||||
|
Title: "Solo se mostrarán los mensajes después de borrar el contexto",
|
||||||
|
},
|
||||||
Model: "Modelo",
|
Model: "Modelo",
|
||||||
Messages: "Mensajes",
|
Messages: "Mensajes",
|
||||||
Topic: "Tema",
|
Topic: "Tema",
|
||||||
Time: "Time",
|
Time: "Hora",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,115 +1,174 @@
|
||||||
import { SubmitKey } from "../store/config";
|
import { SubmitKey } from "../store/config";
|
||||||
import type { PartialLocaleType } from "./index";
|
import type { PartialLocaleType } from "./index";
|
||||||
|
import { getClientConfig } from "../config/client";
|
||||||
|
import { SAAS_CHAT_UTM_URL } from "@/app/constant";
|
||||||
|
const isApp = !!getClientConfig()?.isApp;
|
||||||
|
|
||||||
const fr: PartialLocaleType = {
|
const fr: PartialLocaleType = {
|
||||||
WIP: "Prochainement...",
|
WIP: "Prochainement...",
|
||||||
Error: {
|
Error: {
|
||||||
Unauthorized:
|
Unauthorized: isApp
|
||||||
"Accès non autorisé, veuillez saisir le code d'accès dans la [page](/#/auth) des paramètres.",
|
? `😆 La conversation a rencontré quelques problèmes, pas de panique :
|
||||||
|
\\ 1️⃣ Si vous souhaitez commencer sans configuration, [cliquez ici pour démarrer la conversation immédiatement 🚀](${SAAS_CHAT_UTM_URL})
|
||||||
|
\\ 2️⃣ Si vous souhaitez utiliser vos propres ressources OpenAI, cliquez [ici](/#/settings) pour modifier les paramètres ⚙️`
|
||||||
|
: `😆 La conversation a rencontré quelques problèmes, pas de panique :
|
||||||
|
\ 1️⃣ Si vous souhaitez commencer sans configuration, [cliquez ici pour démarrer la conversation immédiatement 🚀](${SAAS_CHAT_UTM_URL})
|
||||||
|
\ 2️⃣ Si vous utilisez une version déployée privée, cliquez [ici](/#/auth) pour entrer la clé d'accès 🔑
|
||||||
|
\ 3️⃣ Si vous souhaitez utiliser vos propres ressources OpenAI, cliquez [ici](/#/settings) pour modifier les paramètres ⚙️
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
Auth: {
|
||||||
|
Title: "Mot de passe requis",
|
||||||
|
Tips: "L'administrateur a activé la vérification par mot de passe. Veuillez entrer le code d'accès ci-dessous",
|
||||||
|
SubTips: "Ou entrez votre clé API OpenAI ou Google",
|
||||||
|
Input: "Entrez le code d'accès ici",
|
||||||
|
Confirm: "Confirmer",
|
||||||
|
Later: "Plus tard",
|
||||||
|
Return: "Retour",
|
||||||
|
SaasTips:
|
||||||
|
"La configuration est trop compliquée, je veux l'utiliser immédiatement",
|
||||||
|
TopTips:
|
||||||
|
"🥳 Offre de lancement NextChat AI, débloquez OpenAI o1, GPT-4o, Claude-3.5 et les derniers grands modèles",
|
||||||
},
|
},
|
||||||
ChatItem: {
|
ChatItem: {
|
||||||
ChatItemCount: (count: number) => `${count} messages en total`,
|
ChatItemCount: (count: number) => `${count} conversations`,
|
||||||
},
|
},
|
||||||
Chat: {
|
Chat: {
|
||||||
SubTitle: (count: number) => `${count} messages échangés avec ChatGPT`,
|
SubTitle: (count: number) => `Total de ${count} conversations`,
|
||||||
|
EditMessage: {
|
||||||
|
Title: "Modifier l'historique des messages",
|
||||||
|
Topic: {
|
||||||
|
Title: "Sujet de la discussion",
|
||||||
|
SubTitle: "Modifier le sujet de la discussion actuel",
|
||||||
|
},
|
||||||
|
},
|
||||||
Actions: {
|
Actions: {
|
||||||
ChatList: "Aller à la liste de discussion",
|
ChatList: "Voir la liste des messages",
|
||||||
CompressedHistory: "Mémoire d'historique compressée Prompt",
|
CompressedHistory: "Voir l'historique des prompts compressés",
|
||||||
Export: "Exporter tous les messages en tant que Markdown",
|
Export: "Exporter l'historique de la discussion",
|
||||||
Copy: "Copier",
|
Copy: "Copier",
|
||||||
Stop: "Arrêter",
|
Stop: "Arrêter",
|
||||||
Retry: "Réessayer",
|
Retry: "Réessayer",
|
||||||
Delete: "Supprimer",
|
|
||||||
Pin: "Épingler",
|
Pin: "Épingler",
|
||||||
PinToastContent: "Épingler 2 messages à des messages contextuels",
|
PinToastContent: "1 conversation épinglée aux prompts prédéfinis",
|
||||||
PinToastAction: "Voir",
|
PinToastAction: "Voir",
|
||||||
|
Delete: "Supprimer",
|
||||||
Edit: "Modifier",
|
Edit: "Modifier",
|
||||||
|
RefreshTitle: "Actualiser le titre",
|
||||||
|
RefreshToast: "Demande d'actualisation du titre envoyée",
|
||||||
},
|
},
|
||||||
Commands: {
|
Commands: {
|
||||||
new: "Commencer une nouvelle conversation",
|
new: "Nouvelle discussion",
|
||||||
newm: "Démarrer une nouvelle conversation avec un assistant",
|
newm: "Créer une discussion à partir du masque",
|
||||||
next: "Conversation suivante",
|
next: "Discussion suivante",
|
||||||
prev: "Conversation précédente",
|
prev: "Discussion précédente",
|
||||||
clear: "Effacer le contexte",
|
clear: "Effacer le contexte",
|
||||||
del: "Supprimer la Conversation",
|
del: "Supprimer la discussion",
|
||||||
},
|
},
|
||||||
InputActions: {
|
InputActions: {
|
||||||
Stop: "Stop",
|
Stop: "Arrêter la réponse",
|
||||||
ToBottom: "Au dernier",
|
ToBottom: "Aller au plus récent",
|
||||||
Theme: {
|
Theme: {
|
||||||
auto: "Auto",
|
auto: "Thème automatique",
|
||||||
light: "Thème clair",
|
light: "Mode clair",
|
||||||
dark: "Thème sombre",
|
dark: "Mode sombre",
|
||||||
},
|
},
|
||||||
Prompt: "Instructions",
|
Prompt: "Commandes rapides",
|
||||||
Masks: "Assistants",
|
Masks: "Tous les masques",
|
||||||
Clear: "Effacer le contexte",
|
Clear: "Effacer la discussion",
|
||||||
Settings: "Réglages",
|
Settings: "Paramètres de la discussion",
|
||||||
|
UploadImage: "Télécharger une image",
|
||||||
},
|
},
|
||||||
Rename: "Renommer la conversation",
|
Rename: "Renommer la discussion",
|
||||||
Typing: "En train d'écrire…",
|
Typing: "En train d'écrire…",
|
||||||
Input: (submitKey: string) => {
|
Input: (submitKey: string) => {
|
||||||
var inputHints = `Appuyez sur ${submitKey} pour envoyer`;
|
var inputHints = `${submitKey} pour envoyer`;
|
||||||
if (submitKey === String(SubmitKey.Enter)) {
|
if (submitKey === String(SubmitKey.Enter)) {
|
||||||
inputHints += ", Shift + Enter pour insérer un saut de ligne";
|
inputHints += ",Shift + Enter pour passer à la ligne";
|
||||||
}
|
}
|
||||||
return inputHints + ", / pour rechercher des prompts";
|
return inputHints + ",/ pour compléter, : pour déclencher des commandes";
|
||||||
},
|
},
|
||||||
Send: "Envoyer",
|
Send: "Envoyer",
|
||||||
Config: {
|
Config: {
|
||||||
Reset: "Restaurer les paramètres par défaut",
|
Reset: "Effacer la mémoire",
|
||||||
SaveAs: "Enregistrer en tant que masque",
|
SaveAs: "Enregistrer comme masque",
|
||||||
},
|
},
|
||||||
|
IsContext: "Prompt prédéfini",
|
||||||
},
|
},
|
||||||
Export: {
|
Export: {
|
||||||
Title: "Tous les messages",
|
Title: "Partager l'historique des discussions",
|
||||||
Copy: "Tout sélectionner",
|
Copy: "Tout copier",
|
||||||
Download: "Télécharger",
|
Download: "Télécharger le fichier",
|
||||||
MessageFromYou: "Message de votre part",
|
Share: "Partager sur ShareGPT",
|
||||||
MessageFromChatGPT: "Message de ChatGPT",
|
MessageFromYou: "Utilisateur",
|
||||||
|
MessageFromChatGPT: "ChatGPT",
|
||||||
|
Format: {
|
||||||
|
Title: "Format d'exportation",
|
||||||
|
SubTitle: "Vous pouvez exporter en texte Markdown ou en image PNG",
|
||||||
|
},
|
||||||
|
IncludeContext: {
|
||||||
|
Title: "Inclure le contexte du masque",
|
||||||
|
SubTitle: "Afficher le contexte du masque dans les messages",
|
||||||
|
},
|
||||||
|
Steps: {
|
||||||
|
Select: "Sélectionner",
|
||||||
|
Preview: "Aperçu",
|
||||||
|
},
|
||||||
|
Image: {
|
||||||
|
Toast: "Génération de la capture d'écran",
|
||||||
|
Modal:
|
||||||
|
"Appuyez longuement ou faites un clic droit pour enregistrer l'image",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Select: {
|
||||||
|
Search: "Rechercher des messages",
|
||||||
|
All: "Tout sélectionner",
|
||||||
|
Latest: "Derniers messages",
|
||||||
|
Clear: "Effacer la sélection",
|
||||||
},
|
},
|
||||||
Memory: {
|
Memory: {
|
||||||
Title: "Prompt mémoire",
|
Title: "Résumé historique",
|
||||||
EmptyContent: "Rien encore.",
|
EmptyContent: "Le contenu de la discussion est trop court pour être résumé",
|
||||||
Send: "Envoyer la mémoire",
|
Send: "Compresser automatiquement l'historique des discussions et l'envoyer comme contexte",
|
||||||
Copy: "Copier la mémoire",
|
Copy: "Copier le résumé",
|
||||||
Reset: "Réinitialiser la session",
|
Reset: "[unused]",
|
||||||
ResetConfirm:
|
ResetConfirm: "Confirmer la suppression du résumé historique ?",
|
||||||
"La réinitialisation supprimera l'historique de la conversation actuelle ainsi que la mémoire de l'historique. Êtes-vous sûr de vouloir procéder à la réinitialisation?",
|
|
||||||
},
|
},
|
||||||
Home: {
|
Home: {
|
||||||
NewChat: "Nouvelle discussion",
|
NewChat: "Nouvelle discussion",
|
||||||
DeleteChat: "Confirmer la suppression de la conversation sélectionnée ?",
|
DeleteChat: "Confirmer la suppression de la discussion sélectionnée ?",
|
||||||
DeleteToast: "Conversation supprimée",
|
DeleteToast: "Discussion supprimée",
|
||||||
Revert: "Revenir en arrière",
|
Revert: "Annuler",
|
||||||
},
|
},
|
||||||
Settings: {
|
Settings: {
|
||||||
Title: "Paramètres",
|
Title: "Paramètres",
|
||||||
SubTitle: "Toutes les configurations",
|
SubTitle: "Toutes les options de configuration",
|
||||||
|
|
||||||
Danger: {
|
Danger: {
|
||||||
Reset: {
|
Reset: {
|
||||||
Title: "Restaurer les paramètres",
|
Title: "Réinitialiser tous les paramètres",
|
||||||
SubTitle: "Restaurer les paramètres par défaut",
|
SubTitle:
|
||||||
Action: "Reinitialiser",
|
"Réinitialiser toutes les options de configuration aux valeurs par défaut",
|
||||||
Confirm: "Confirmer la réinitialisation des paramètres?",
|
Action: "Réinitialiser maintenant",
|
||||||
|
Confirm: "Confirmer la réinitialisation de tous les paramètres ?",
|
||||||
},
|
},
|
||||||
Clear: {
|
Clear: {
|
||||||
Title: "Supprimer toutes les données",
|
Title: "Effacer toutes les données",
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Effacer toutes les données, y compris les conversations et les paramètres",
|
"Effacer toutes les discussions et les données de configuration",
|
||||||
Action: "Supprimer",
|
Action: "Effacer maintenant",
|
||||||
Confirm: "Confirmer la suppression de toutes les données?",
|
Confirm:
|
||||||
|
"Confirmer l'effacement de toutes les discussions et données de configuration ?",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Lang: {
|
Lang: {
|
||||||
Name: "Language", // ATTENTION : si vous souhaitez ajouter une nouvelle traduction, ne traduisez pas cette valeur, laissez-la sous forme de `Language`
|
Name: "Language", // ATTENTION: if you wanna add a new translation, please do not translate this value, leave it as `Language`
|
||||||
All: "Toutes les langues",
|
All: "Toutes les langues",
|
||||||
},
|
},
|
||||||
|
|
||||||
Avatar: "Avatar",
|
Avatar: "Avatar",
|
||||||
FontSize: {
|
FontSize: {
|
||||||
Title: "Taille des polices",
|
Title: "Taille de la police",
|
||||||
SubTitle: "Ajuste la taille de police du contenu de la conversation",
|
SubTitle: "Taille de la police pour le contenu des discussions",
|
||||||
},
|
},
|
||||||
FontFamily: {
|
FontFamily: {
|
||||||
Title: "Police de Chat",
|
Title: "Police de Chat",
|
||||||
|
@ -120,181 +179,406 @@ const fr: PartialLocaleType = {
|
||||||
InjectSystemPrompts: {
|
InjectSystemPrompts: {
|
||||||
Title: "Injecter des invites système",
|
Title: "Injecter des invites système",
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Ajoute de force une invite système simulée de ChatGPT au début de la liste des messages pour chaque demande",
|
"Ajouter de manière forcée une invite système simulée de ChatGPT au début de chaque liste de messages",
|
||||||
},
|
},
|
||||||
InputTemplate: {
|
InputTemplate: {
|
||||||
Title: "Template",
|
Title: "Prétraitement des entrées utilisateur",
|
||||||
SubTitle: "Le message le plus récent sera ajouté à ce template.",
|
SubTitle:
|
||||||
|
"Le dernier message de l'utilisateur sera intégré dans ce modèle",
|
||||||
},
|
},
|
||||||
|
|
||||||
Update: {
|
Update: {
|
||||||
Version: (x: string) => `Version : ${x}`,
|
Version: (x: string) => `Version actuelle : ${x}`,
|
||||||
IsLatest: "Dernière version",
|
IsLatest: "Vous avez la dernière version",
|
||||||
CheckUpdate: "Vérifier la mise à jour",
|
CheckUpdate: "Vérifier les mises à jour",
|
||||||
IsChecking: "Vérification de la mise à jour...",
|
IsChecking: "Vérification des mises à jour en cours...",
|
||||||
FoundUpdate: (x: string) => `Nouvelle version disponible : ${x}`,
|
FoundUpdate: (x: string) => `Nouvelle version trouvée : ${x}`,
|
||||||
GoToUpdate: "Mise à jour",
|
GoToUpdate: "Aller à la mise à jour",
|
||||||
},
|
},
|
||||||
SendKey: "Clé d'envoi",
|
SendKey: "Touche d'envoi",
|
||||||
Theme: "Thème",
|
Theme: "Thème",
|
||||||
TightBorder: "Bordure serrée",
|
TightBorder: "Mode sans bordure",
|
||||||
SendPreviewBubble: {
|
SendPreviewBubble: {
|
||||||
Title: "Aperçu de l'envoi dans une bulle",
|
Title: "Bulle d'aperçu",
|
||||||
SubTitle: "Aperçu du Markdown dans une bulle",
|
SubTitle: "Aperçu du contenu Markdown dans la bulle d'aperçu",
|
||||||
|
},
|
||||||
|
AutoGenerateTitle: {
|
||||||
|
Title: "Génération automatique de titres",
|
||||||
|
SubTitle:
|
||||||
|
"Générer un titre approprié en fonction du contenu de la discussion",
|
||||||
|
},
|
||||||
|
Sync: {
|
||||||
|
CloudState: "Données cloud",
|
||||||
|
NotSyncYet: "Pas encore synchronisé",
|
||||||
|
Success: "Synchronisation réussie",
|
||||||
|
Fail: "Échec de la synchronisation",
|
||||||
|
|
||||||
|
Config: {
|
||||||
|
Modal: {
|
||||||
|
Title: "Configurer la synchronisation cloud",
|
||||||
|
Check: "Vérifier la disponibilité",
|
||||||
|
},
|
||||||
|
SyncType: {
|
||||||
|
Title: "Type de synchronisation",
|
||||||
|
SubTitle: "Choisissez le serveur de synchronisation préféré",
|
||||||
|
},
|
||||||
|
Proxy: {
|
||||||
|
Title: "Activer le proxy",
|
||||||
|
SubTitle:
|
||||||
|
"Lors de la synchronisation dans le navigateur, le proxy doit être activé pour éviter les restrictions de domaine croisé",
|
||||||
|
},
|
||||||
|
ProxyUrl: {
|
||||||
|
Title: "Adresse du proxy",
|
||||||
|
SubTitle:
|
||||||
|
"Uniquement pour le proxy de domaine croisé fourni par le projet",
|
||||||
|
},
|
||||||
|
|
||||||
|
WebDav: {
|
||||||
|
Endpoint: "Adresse WebDAV",
|
||||||
|
UserName: "Nom d'utilisateur",
|
||||||
|
Password: "Mot de passe",
|
||||||
|
},
|
||||||
|
|
||||||
|
UpStash: {
|
||||||
|
Endpoint: "URL REST Redis UpStash",
|
||||||
|
UserName: "Nom de sauvegarde",
|
||||||
|
Password: "Token REST Redis UpStash",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
LocalState: "Données locales",
|
||||||
|
Overview: (overview: any) => {
|
||||||
|
return `${overview.chat} discussions, ${overview.message} messages, ${overview.prompt} invites, ${overview.mask} masques`;
|
||||||
|
},
|
||||||
|
ImportFailed: "Échec de l'importation",
|
||||||
},
|
},
|
||||||
Mask: {
|
Mask: {
|
||||||
Splash: {
|
Splash: {
|
||||||
Title: "Écran de masque",
|
Title: "Page de démarrage du masque",
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Afficher un écran de masque avant de démarrer une nouvelle discussion",
|
"Afficher la page de démarrage du masque lors de la création d'une nouvelle discussion",
|
||||||
},
|
},
|
||||||
Builtin: {
|
Builtin: {
|
||||||
Title: "Masquer Les Assistants Intégrés",
|
Title: "Masquer les masques intégrés",
|
||||||
SubTitle: "Masquer les assistants intégrés par défaut",
|
SubTitle:
|
||||||
|
"Masquer les masques intégrés dans toutes les listes de masques",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Prompt: {
|
Prompt: {
|
||||||
Disable: {
|
Disable: {
|
||||||
Title: "Désactiver la saisie semi-automatique",
|
Title: "Désactiver la complétion automatique des invites",
|
||||||
SubTitle: "Appuyez sur / pour activer la saisie semi-automatique",
|
SubTitle:
|
||||||
|
"Saisir / au début de la zone de texte pour déclencher la complétion automatique",
|
||||||
},
|
},
|
||||||
List: "Liste de prompts",
|
List: "Liste des invites personnalisées",
|
||||||
ListCount: (builtin: number, custom: number) =>
|
ListCount: (builtin: number, custom: number) =>
|
||||||
`${builtin} intégré, ${custom} personnalisé`,
|
`${builtin} intégrées, ${custom} définies par l'utilisateur`,
|
||||||
Edit: "Modifier",
|
Edit: "Modifier",
|
||||||
Modal: {
|
Modal: {
|
||||||
Title: "Liste de prompts",
|
Title: "Liste des invites",
|
||||||
Add: "Ajouter un élément",
|
Add: "Créer",
|
||||||
Search: "Rechercher des prompts",
|
Search: "Rechercher des invites",
|
||||||
},
|
},
|
||||||
EditModal: {
|
EditModal: {
|
||||||
Title: "Modifier le prompt",
|
Title: "Modifier les invites",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
HistoryCount: {
|
HistoryCount: {
|
||||||
Title: "Nombre de messages joints",
|
Title: "Nombre de messages historiques",
|
||||||
SubTitle: "Nombre de messages envoyés attachés par demande",
|
SubTitle: "Nombre de messages historiques envoyés avec chaque demande",
|
||||||
},
|
},
|
||||||
CompressThreshold: {
|
CompressThreshold: {
|
||||||
Title: "Seuil de compression de l'historique",
|
Title: "Seuil de compression des messages historiques",
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Comprimera si la longueur des messages non compressés dépasse cette valeur",
|
"Compresser les messages historiques lorsque leur longueur dépasse cette valeur",
|
||||||
},
|
},
|
||||||
|
|
||||||
Usage: {
|
Usage: {
|
||||||
Title: "Solde du compte",
|
Title: "Vérification du solde",
|
||||||
SubTitle(used: any, total: any) {
|
SubTitle(used: any, total: any) {
|
||||||
return `Épuisé ce mois-ci $${used}, abonnement $${total}`;
|
return `Utilisé ce mois-ci : $${used}, Total d'abonnement : $${total}`;
|
||||||
|
},
|
||||||
|
IsChecking: "Vérification en cours…",
|
||||||
|
Check: "Re-vérifier",
|
||||||
|
NoAccess:
|
||||||
|
"Entrez la clé API ou le mot de passe d'accès pour vérifier le solde",
|
||||||
|
},
|
||||||
|
|
||||||
|
Access: {
|
||||||
|
SaasStart: {
|
||||||
|
Title: "Utiliser NextChat AI",
|
||||||
|
Label: "(La solution la plus rentable)",
|
||||||
|
SubTitle:
|
||||||
|
"Officiellement maintenu par NextChat, prêt à l'emploi sans configuration, prend en charge les derniers grands modèles comme OpenAI o1, GPT-4o et Claude-3.5",
|
||||||
|
ChatNow: "Discuter maintenant",
|
||||||
|
},
|
||||||
|
|
||||||
|
AccessCode: {
|
||||||
|
Title: "Mot de passe d'accès",
|
||||||
|
SubTitle: "L'administrateur a activé l'accès sécurisé",
|
||||||
|
Placeholder: "Veuillez entrer le mot de passe d'accès",
|
||||||
|
},
|
||||||
|
CustomEndpoint: {
|
||||||
|
Title: "Interface personnalisée",
|
||||||
|
SubTitle: "Utiliser un service Azure ou OpenAI personnalisé",
|
||||||
|
},
|
||||||
|
Provider: {
|
||||||
|
Title: "Fournisseur de modèle",
|
||||||
|
SubTitle: "Changer de fournisseur de service",
|
||||||
|
},
|
||||||
|
OpenAI: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "Clé API",
|
||||||
|
SubTitle:
|
||||||
|
"Utiliser une clé OpenAI personnalisée pour contourner les restrictions d'accès par mot de passe",
|
||||||
|
Placeholder: "Clé API OpenAI",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Adresse de l'interface",
|
||||||
|
SubTitle: "Doit inclure http(s):// en dehors de l'adresse par défaut",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Azure: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "Clé d'interface",
|
||||||
|
SubTitle:
|
||||||
|
"Utiliser une clé Azure personnalisée pour contourner les restrictions d'accès par mot de passe",
|
||||||
|
Placeholder: "Clé API Azure",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Adresse de l'interface",
|
||||||
|
SubTitle: "Exemple :",
|
||||||
|
},
|
||||||
|
|
||||||
|
ApiVerion: {
|
||||||
|
Title: "Version de l'interface (version API azure)",
|
||||||
|
SubTitle: "Choisissez une version spécifique",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Anthropic: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "Clé d'interface",
|
||||||
|
SubTitle:
|
||||||
|
"Utiliser une clé Anthropic personnalisée pour contourner les restrictions d'accès par mot de passe",
|
||||||
|
Placeholder: "Clé API Anthropic",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Adresse de l'interface",
|
||||||
|
SubTitle: "Exemple :",
|
||||||
|
},
|
||||||
|
|
||||||
|
ApiVerion: {
|
||||||
|
Title: "Version de l'interface (version API claude)",
|
||||||
|
SubTitle: "Choisissez une version spécifique de l'API",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Google: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "Clé API",
|
||||||
|
SubTitle: "Obtenez votre clé API Google AI",
|
||||||
|
Placeholder: "Entrez votre clé API Google AI Studio",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Adresse de l'interface",
|
||||||
|
SubTitle: "Exemple :",
|
||||||
|
},
|
||||||
|
|
||||||
|
ApiVersion: {
|
||||||
|
Title: "Version de l'API (pour gemini-pro uniquement)",
|
||||||
|
SubTitle: "Choisissez une version spécifique de l'API",
|
||||||
|
},
|
||||||
|
GoogleSafetySettings: {
|
||||||
|
Title: "Niveau de filtrage de sécurité Google",
|
||||||
|
SubTitle: "Définir le niveau de filtrage du contenu",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Baidu: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "Clé API",
|
||||||
|
SubTitle: "Utiliser une clé API Baidu personnalisée",
|
||||||
|
Placeholder: "Clé API Baidu",
|
||||||
|
},
|
||||||
|
SecretKey: {
|
||||||
|
Title: "Clé secrète",
|
||||||
|
SubTitle: "Utiliser une clé secrète Baidu personnalisée",
|
||||||
|
Placeholder: "Clé secrète Baidu",
|
||||||
|
},
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Adresse de l'interface",
|
||||||
|
SubTitle:
|
||||||
|
"Non pris en charge pour les configurations personnalisées dans .env",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ByteDance: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "Clé d'interface",
|
||||||
|
SubTitle: "Utiliser une clé API ByteDance personnalisée",
|
||||||
|
Placeholder: "Clé API ByteDance",
|
||||||
|
},
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Adresse de l'interface",
|
||||||
|
SubTitle: "Exemple :",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Alibaba: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "Clé d'interface",
|
||||||
|
SubTitle: "Utiliser une clé API Alibaba Cloud personnalisée",
|
||||||
|
Placeholder: "Clé API Alibaba Cloud",
|
||||||
|
},
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Adresse de l'interface",
|
||||||
|
SubTitle: "Exemple :",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CustomModel: {
|
||||||
|
Title: "Nom du modèle personnalisé",
|
||||||
|
SubTitle:
|
||||||
|
"Ajouter des options de modèles personnalisés, séparées par des virgules",
|
||||||
},
|
},
|
||||||
IsChecking: "Vérification...",
|
|
||||||
Check: "Vérifier",
|
|
||||||
NoAccess: "Entrez la clé API pour vérifier le solde",
|
|
||||||
},
|
},
|
||||||
|
|
||||||
Model: "Modèle",
|
Model: "Modèle",
|
||||||
|
CompressModel: {
|
||||||
|
Title: "Modèle de compression",
|
||||||
|
SubTitle: "Modèle utilisé pour compresser l'historique",
|
||||||
|
},
|
||||||
Temperature: {
|
Temperature: {
|
||||||
Title: "Température",
|
Title: "Aléatoire (temperature)",
|
||||||
SubTitle: "Une valeur plus élevée rendra les réponses plus aléatoires",
|
SubTitle: "Plus la valeur est élevée, plus les réponses sont aléatoires",
|
||||||
},
|
},
|
||||||
TopP: {
|
TopP: {
|
||||||
Title: "Top P",
|
Title: "Échantillonnage par noyau (top_p)",
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Ne modifiez pas à moins que vous ne sachiez ce que vous faites",
|
"Semblable à l'aléatoire, mais ne pas modifier en même temps que l'aléatoire",
|
||||||
},
|
},
|
||||||
MaxTokens: {
|
MaxTokens: {
|
||||||
Title: "Limite de Tokens",
|
Title: "Limite de réponse unique (max_tokens)",
|
||||||
SubTitle: "Longueur maximale des tokens d'entrée et des tokens générés",
|
SubTitle: "Nombre maximal de tokens utilisés pour une interaction unique",
|
||||||
},
|
},
|
||||||
PresencePenalty: {
|
PresencePenalty: {
|
||||||
Title: "Pénalité de présence",
|
Title: "Nouveauté du sujet (presence_penalty)",
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Une valeur plus élevée augmentera la probabilité d'introduire de nouveaux sujets",
|
"Plus la valeur est élevée, plus il est probable d'élargir aux nouveaux sujets",
|
||||||
},
|
},
|
||||||
FrequencyPenalty: {
|
FrequencyPenalty: {
|
||||||
Title: "Pénalité de fréquence",
|
Title: "Pénalité de fréquence (frequency_penalty)",
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Une valeur plus élevée diminuant la probabilité de répéter la même ligne",
|
"Plus la valeur est élevée, plus il est probable de réduire les répétitions",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Store: {
|
Store: {
|
||||||
DefaultTopic: "Nouvelle conversation",
|
DefaultTopic: "Nouvelle discussion",
|
||||||
BotHello: "Bonjour ! Comment puis-je vous aider aujourd'hui ?",
|
BotHello: "Comment puis-je vous aider ?",
|
||||||
Error: "Quelque chose s'est mal passé, veuillez réessayer plus tard.",
|
Error: "Une erreur est survenue, veuillez réessayer plus tard",
|
||||||
Prompt: {
|
Prompt: {
|
||||||
History: (content: string) =>
|
History: (content: string) =>
|
||||||
"Ceci est un résumé de l'historique des discussions entre l'IA et l'utilisateur : " +
|
"Voici le résumé de la discussion précédente : " + content,
|
||||||
content,
|
|
||||||
Topic:
|
Topic:
|
||||||
"Veuillez générer un titre de quatre à cinq mots résumant notre conversation sans introduction, ponctuation, guillemets, points, symboles ou texte supplémentaire. Supprimez les guillemets inclus.",
|
"Utilisez quatre à cinq mots pour retourner le sujet succinct de cette phrase, sans explication, sans ponctuation, sans interjections, sans texte superflu, sans gras. Si aucun sujet, retournez simplement « discussion informelle »",
|
||||||
Summarize:
|
Summarize:
|
||||||
"Résumez brièvement nos discussions en 200 mots ou moins pour les utiliser comme prompt de contexte futur.",
|
"Faites un résumé succinct de la discussion, à utiliser comme prompt de contexte ultérieur, en moins de 200 mots",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Copy: {
|
Copy: {
|
||||||
Success: "Copié dans le presse-papiers",
|
Success: "Copié dans le presse-papiers",
|
||||||
Failed:
|
Failed: "Échec de la copie, veuillez autoriser l'accès au presse-papiers",
|
||||||
"La copie a échoué, veuillez accorder l'autorisation d'accès au presse-papiers",
|
},
|
||||||
|
Download: {
|
||||||
|
Success: "Le contenu a été téléchargé dans votre répertoire.",
|
||||||
|
Failed: "Échec du téléchargement.",
|
||||||
},
|
},
|
||||||
Context: {
|
Context: {
|
||||||
Toast: (x: any) => `Avec ${x} contextes de prompts`,
|
Toast: (x: any) => `Contient ${x} invites prédéfinies`,
|
||||||
Edit: "Contextes et mémoires de prompts",
|
Edit: "Paramètres de la discussion actuelle",
|
||||||
Add: "Ajouter un prompt",
|
Add: "Ajouter une discussion",
|
||||||
|
Clear: "Contexte effacé",
|
||||||
|
Revert: "Restaurer le contexte",
|
||||||
},
|
},
|
||||||
Plugin: {
|
Plugin: {
|
||||||
Name: "Extension",
|
Name: "Plugin",
|
||||||
},
|
},
|
||||||
FineTuned: {
|
FineTuned: {
|
||||||
Sysmessage: "Eres un asistente que",
|
Sysmessage: "Vous êtes un assistant",
|
||||||
|
},
|
||||||
|
SearchChat: {
|
||||||
|
Name: "Recherche",
|
||||||
|
Page: {
|
||||||
|
Title: "Rechercher dans l'historique des discussions",
|
||||||
|
Search: "Entrez le mot-clé de recherche",
|
||||||
|
NoResult: "Aucun résultat trouvé",
|
||||||
|
NoData: "Aucune donnée",
|
||||||
|
Loading: "Chargement",
|
||||||
|
|
||||||
|
SubTitle: (count: number) => `${count} résultats trouvés`,
|
||||||
|
},
|
||||||
|
Item: {
|
||||||
|
View: "Voir",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Mask: {
|
Mask: {
|
||||||
Name: "Masque",
|
Name: "Masque",
|
||||||
Page: {
|
Page: {
|
||||||
Title: "Modèle de prompt",
|
Title: "Masques de rôle prédéfinis",
|
||||||
SubTitle: (count: number) => `${count} modèles de prompts`,
|
SubTitle: (count: number) => `${count} définitions de rôle prédéfinies`,
|
||||||
Search: "Rechercher des modèles",
|
Search: "Rechercher des masques de rôle",
|
||||||
Create: "Créer",
|
Create: "Créer",
|
||||||
},
|
},
|
||||||
Item: {
|
Item: {
|
||||||
Info: (count: number) => `${count} prompts`,
|
Info: (count: number) => `Contient ${count} discussions prédéfinies`,
|
||||||
Chat: "Discussion",
|
Chat: "Discussion",
|
||||||
View: "Vue",
|
View: "Voir",
|
||||||
Edit: "Modifier",
|
Edit: "Modifier",
|
||||||
Delete: "Supprimer",
|
Delete: "Supprimer",
|
||||||
DeleteConfirm: "Confirmer la suppression?",
|
DeleteConfirm: "Confirmer la suppression ?",
|
||||||
},
|
},
|
||||||
EditModal: {
|
EditModal: {
|
||||||
Title: (readonly: boolean) =>
|
Title: (readonly: boolean) =>
|
||||||
`Modifier le modèle de prompt ${readonly ? "(en lecture seule)" : ""}`,
|
`Modifier le masque prédéfini ${readonly ? " (lecture seule)" : ""}`,
|
||||||
Download: "Télécharger",
|
Download: "Télécharger le masque",
|
||||||
Clone: "Dupliquer",
|
Clone: "Cloner le masque",
|
||||||
},
|
},
|
||||||
Config: {
|
Config: {
|
||||||
Avatar: "Avatar de lassistant",
|
Avatar: "Avatar du rôle",
|
||||||
Name: "Nom de lassistant",
|
Name: "Nom du rôle",
|
||||||
Sync: {
|
Sync: {
|
||||||
Title: "Utiliser la configuration globale",
|
Title: "Utiliser les paramètres globaux",
|
||||||
SubTitle: "Utiliser la configuration globale dans cette conversation",
|
SubTitle:
|
||||||
Confirm: "Voulez-vous definir votre configuration personnalisée ?",
|
"Cette discussion utilise-t-elle les paramètres du modèle globaux ?",
|
||||||
|
Confirm:
|
||||||
|
"Les paramètres personnalisés de cette discussion seront automatiquement remplacés. Confirmer l'activation des paramètres globaux ?",
|
||||||
},
|
},
|
||||||
HideContext: {
|
HideContext: {
|
||||||
Title: "Masquer les invites contextuelles",
|
Title: "Masquer les discussions prédéfinies",
|
||||||
SubTitle: "Ne pas afficher les instructions contextuelles dans le chat",
|
SubTitle:
|
||||||
|
"Les discussions prédéfinies ne seront pas affichées dans l'interface de discussion après masquage",
|
||||||
},
|
},
|
||||||
Share: {
|
Share: {
|
||||||
Title: "Partager ce masque",
|
Title: "Partager ce masque",
|
||||||
SubTitle: "Générer un lien vers ce masque",
|
SubTitle: "Générer un lien direct pour ce masque",
|
||||||
Action: "Copier le lien",
|
Action: "Copier le lien",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
NewChat: {
|
NewChat: {
|
||||||
Return: "Retour",
|
Return: "Retour",
|
||||||
Skip: "Passer",
|
Skip: "Commencer directement",
|
||||||
Title: "Choisir un assitant",
|
NotShow: "Ne plus afficher",
|
||||||
SubTitle: "Discutez avec l'âme derrière le masque",
|
|
||||||
More: "En savoir plus",
|
|
||||||
NotShow: "Ne pas afficher à nouveau",
|
|
||||||
ConfirmNoShow:
|
ConfirmNoShow:
|
||||||
"Confirmez-vous vouloir désactiver cela? Vous pouvez le réactiver plus tard dans les paramètres.",
|
"Confirmer la désactivation ? Vous pourrez réactiver cette option à tout moment dans les paramètres.",
|
||||||
|
Title: "Choisir un masque",
|
||||||
|
SubTitle: "Commencez maintenant, rencontrez les pensées derrière le masque",
|
||||||
|
More: "Voir tout",
|
||||||
|
},
|
||||||
|
|
||||||
|
URLCommand: {
|
||||||
|
Code: "Code d'accès détecté dans le lien, souhaitez-vous le remplir automatiquement ?",
|
||||||
|
Settings:
|
||||||
|
"Paramètres prédéfinis détectés dans le lien, souhaitez-vous les remplir automatiquement ?",
|
||||||
},
|
},
|
||||||
|
|
||||||
UI: {
|
UI: {
|
||||||
|
@ -302,9 +586,17 @@ const fr: PartialLocaleType = {
|
||||||
Cancel: "Annuler",
|
Cancel: "Annuler",
|
||||||
Close: "Fermer",
|
Close: "Fermer",
|
||||||
Create: "Créer",
|
Create: "Créer",
|
||||||
Edit: "Éditer",
|
Edit: "Modifier",
|
||||||
|
Export: "Exporter",
|
||||||
|
Import: "Importer",
|
||||||
|
Sync: "Synchroniser",
|
||||||
|
Config: "Configurer",
|
||||||
},
|
},
|
||||||
Exporter: {
|
Exporter: {
|
||||||
|
Description: {
|
||||||
|
Title:
|
||||||
|
"Seuls les messages après avoir effacé le contexte seront affichés",
|
||||||
|
},
|
||||||
Model: "Modèle",
|
Model: "Modèle",
|
||||||
Messages: "Messages",
|
Messages: "Messages",
|
||||||
Topic: "Sujet",
|
Topic: "Sujet",
|
||||||
|
|
|
@ -1,144 +1,170 @@
|
||||||
import { SubmitKey } from "../store/config";
|
import { SubmitKey } from "../store/config";
|
||||||
import { PartialLocaleType } from "./index";
|
import type { PartialLocaleType } from "./index";
|
||||||
|
import { getClientConfig } from "../config/client";
|
||||||
|
import { SAAS_CHAT_UTM_URL } from "@/app/constant";
|
||||||
|
const isApp = !!getClientConfig()?.isApp;
|
||||||
|
|
||||||
const id: PartialLocaleType = {
|
const id: PartialLocaleType = {
|
||||||
WIP: "Coming Soon...",
|
WIP: "Coming Soon...",
|
||||||
Error: {
|
Error: {
|
||||||
Unauthorized:
|
Unauthorized: isApp
|
||||||
"Akses tidak diizinkan, silakan masukkan kode akses atau masukkan kunci API OpenAI Anda. di halaman [autentikasi](/#/auth) atau di halaman [Pengaturan](/#/settings).",
|
? `😆 Percakapan mengalami beberapa masalah, tidak perlu khawatir:
|
||||||
|
\\ 1️⃣ Jika Anda ingin memulai tanpa konfigurasi, [klik di sini untuk mulai mengobrol segera 🚀](${SAAS_CHAT_UTM_URL})
|
||||||
|
\\ 2️⃣ Jika Anda ingin menggunakan sumber daya OpenAI Anda sendiri, klik [di sini](/#/settings) untuk mengubah pengaturan ⚙️`
|
||||||
|
: `😆 Percakapan mengalami beberapa masalah, tidak perlu khawatir:
|
||||||
|
\ 1️⃣ Jika Anda ingin memulai tanpa konfigurasi, [klik di sini untuk mulai mengobrol segera 🚀](${SAAS_CHAT_UTM_URL})
|
||||||
|
\ 2️⃣ Jika Anda menggunakan versi penyebaran pribadi, klik [di sini](/#/auth) untuk memasukkan kunci akses 🔑
|
||||||
|
\ 3️⃣ Jika Anda ingin menggunakan sumber daya OpenAI Anda sendiri, klik [di sini](/#/settings) untuk mengubah pengaturan ⚙️
|
||||||
|
`,
|
||||||
},
|
},
|
||||||
Auth: {
|
Auth: {
|
||||||
Title: "Diperlukan Kode Akses",
|
Title: "Kebutuhan Kata Sandi",
|
||||||
Tips: "Masukkan kode akses di bawah",
|
Tips: "Administrator telah mengaktifkan verifikasi kata sandi, silakan masukkan kode akses di bawah ini",
|
||||||
SubTips: "Atau masukkan kunci API OpenAI Anda",
|
SubTips: "Atau masukkan kunci API OpenAI atau Google Anda",
|
||||||
Input: "Kode Akses",
|
Input: "Masukkan kode akses di sini",
|
||||||
Confirm: "Konfirmasi",
|
Confirm: "Konfirmasi",
|
||||||
Later: "Nanti",
|
Later: "Nanti",
|
||||||
|
Return: "Kembali",
|
||||||
|
SaasTips: "Konfigurasi terlalu rumit, saya ingin menggunakannya segera",
|
||||||
|
TopTips:
|
||||||
|
"🥳 Penawaran Peluncuran NextChat AI, buka OpenAI o1, GPT-4o, Claude-3.5 dan model besar terbaru sekarang",
|
||||||
},
|
},
|
||||||
ChatItem: {
|
ChatItem: {
|
||||||
ChatItemCount: (count: number) => `${count} pesan`,
|
ChatItemCount: (count: number) => `${count} percakapan`,
|
||||||
},
|
},
|
||||||
Chat: {
|
Chat: {
|
||||||
SubTitle: (count: number) => `${count} pesan`,
|
SubTitle: (count: number) => `Total ${count} percakapan`,
|
||||||
|
EditMessage: {
|
||||||
|
Title: "Edit Riwayat Pesan",
|
||||||
|
Topic: {
|
||||||
|
Title: "Topik Obrolan",
|
||||||
|
SubTitle: "Ubah topik obrolan saat ini",
|
||||||
|
},
|
||||||
|
},
|
||||||
Actions: {
|
Actions: {
|
||||||
ChatList: "Buka Daftar Chat",
|
ChatList: "Lihat daftar pesan",
|
||||||
CompressedHistory: "Ekspor Riwayat Terkompresi",
|
CompressedHistory: "Lihat riwayat Prompt yang dikompresi",
|
||||||
Export: "Ekspor Semua Pesan sebagai Markdown",
|
Export: "Ekspor riwayat obrolan",
|
||||||
Copy: "Salin",
|
Copy: "Salin",
|
||||||
Stop: "Berhenti",
|
Stop: "Berhenti",
|
||||||
Retry: "Coba Lagi",
|
Retry: "Coba lagi",
|
||||||
Pin: "Pin",
|
Pin: "Sematkan",
|
||||||
PinToastContent: "2 pesan telah ditandai",
|
PinToastContent: "1 percakapan telah disematkan ke prompt default",
|
||||||
PinToastAction: "Lihat",
|
PinToastAction: "Lihat",
|
||||||
Delete: "Hapus",
|
Delete: "Hapus",
|
||||||
Edit: "Edit",
|
Edit: "Edit",
|
||||||
|
RefreshTitle: "Segarkan Judul",
|
||||||
|
RefreshToast: "Permintaan penyegaran judul telah dikirim",
|
||||||
},
|
},
|
||||||
Commands: {
|
Commands: {
|
||||||
new: "Mulai Chat Baru",
|
new: "Obrolan Baru",
|
||||||
newm: "Mulai Chat Baru dengan Masks",
|
newm: "Buat Obrolan Baru dari Masker",
|
||||||
next: "Chat Selanjutnya",
|
next: "Obrolan Berikutnya",
|
||||||
prev: "Chat Sebelumnya",
|
prev: "Obrolan Sebelumnya",
|
||||||
clear: "Bersihkan Percakapan",
|
clear: "Hapus Konteks",
|
||||||
del: "Hapus Chat",
|
del: "Hapus Obrolan",
|
||||||
},
|
},
|
||||||
InputActions: {
|
InputActions: {
|
||||||
Stop: "Berhenti",
|
Stop: "Hentikan Respons",
|
||||||
ToBottom: "Ke Bagian Bawah",
|
ToBottom: "Gulir ke bawah",
|
||||||
Theme: {
|
Theme: {
|
||||||
auto: "Otomatis",
|
auto: "Tema Otomatis",
|
||||||
light: "Tema Terang",
|
light: "Mode Terang",
|
||||||
dark: "Tema Gelap",
|
dark: "Mode Gelap",
|
||||||
},
|
},
|
||||||
Prompt: "Prompts",
|
Prompt: "Perintah Cepat",
|
||||||
Masks: "Masks",
|
Masks: "Semua Masker",
|
||||||
Clear: "Bersihkan Percakapan",
|
Clear: "Hapus Obrolan",
|
||||||
Settings: "Pengaturan",
|
Settings: "Pengaturan Obrolan",
|
||||||
|
UploadImage: "Unggah Gambar",
|
||||||
},
|
},
|
||||||
Rename: "Ubah Nama Chat",
|
Rename: "Ganti Nama Obrolan",
|
||||||
Typing: "Mengetik...",
|
Typing: "Sedang Mengetik…",
|
||||||
Input: (submitKey: string) => {
|
Input: (submitKey: string) => {
|
||||||
var inputHints = `${submitKey} untuk mengirim`;
|
var inputHints = `${submitKey} kirim`;
|
||||||
if (submitKey === String(SubmitKey.Enter)) {
|
if (submitKey === String(SubmitKey.Enter)) {
|
||||||
inputHints += ", Shift + Enter untuk membalut";
|
inputHints += ",Shift + Enter untuk baris baru";
|
||||||
}
|
}
|
||||||
return (
|
return inputHints + ",/ untuk melengkapi, : untuk memicu perintah";
|
||||||
inputHints + ", / untuk mencari prompt, : untuk menggunakan perintah"
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
Send: "Kirim",
|
Send: "Kirim",
|
||||||
Config: {
|
Config: {
|
||||||
Reset: "Reset ke Default",
|
Reset: "Hapus Memori",
|
||||||
SaveAs: "Simpan sebagai Masks",
|
SaveAs: "Simpan sebagai Masker",
|
||||||
},
|
},
|
||||||
|
IsContext: "Prompt Default",
|
||||||
},
|
},
|
||||||
Export: {
|
Export: {
|
||||||
Title: "Ekspor Pesan",
|
Title: "Bagikan Riwayat Obrolan",
|
||||||
Copy: "Salin Semua",
|
Copy: "Salin Semua",
|
||||||
Download: "Unduh",
|
Download: "Unduh File",
|
||||||
MessageFromYou: "Pesan dari Anda",
|
|
||||||
MessageFromChatGPT: "Pesan dari ChatGPT",
|
|
||||||
Share: "Bagikan ke ShareGPT",
|
Share: "Bagikan ke ShareGPT",
|
||||||
|
MessageFromYou: "Pengguna",
|
||||||
|
MessageFromChatGPT: "ChatGPT",
|
||||||
Format: {
|
Format: {
|
||||||
Title: "Format Ekspor",
|
Title: "Format Ekspor",
|
||||||
SubTitle: "Markdown atau Gambar PNG",
|
SubTitle: "Dapat mengekspor teks Markdown atau gambar PNG",
|
||||||
},
|
},
|
||||||
IncludeContext: {
|
IncludeContext: {
|
||||||
Title: "Sertakan Konteks",
|
Title: "Sertakan Konteks Masker",
|
||||||
SubTitle: "Apakah akan menyertakan masks",
|
SubTitle: "Apakah akan menampilkan konteks masker dalam pesan",
|
||||||
},
|
},
|
||||||
Steps: {
|
Steps: {
|
||||||
Select: "Pilih",
|
Select: "Pilih",
|
||||||
Preview: "Pratinjau",
|
Preview: "Prabaca",
|
||||||
|
},
|
||||||
|
Image: {
|
||||||
|
Toast: "Sedang Membuat Screenshot",
|
||||||
|
Modal: "Tekan lama atau klik kanan untuk menyimpan gambar",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Select: {
|
Select: {
|
||||||
Search: "Cari",
|
Search: "Cari Pesan",
|
||||||
All: "Pilih Semua",
|
All: "Pilih Semua",
|
||||||
Latest: "Pilih Terbaru",
|
Latest: "Beberapa Terbaru",
|
||||||
Clear: "Bersihkan",
|
Clear: "Hapus Pilihan",
|
||||||
},
|
},
|
||||||
Memory: {
|
Memory: {
|
||||||
Title: "Prompt Memori",
|
Title: "Ringkasan Sejarah",
|
||||||
EmptyContent: "Belum ada yang tersedia.",
|
EmptyContent: "Isi percakapan terlalu pendek, tidak perlu dirangkum",
|
||||||
Send: "Kirim Memori",
|
Send: "Otomatis kompres riwayat obrolan dan kirim sebagai konteks",
|
||||||
Copy: "Salin Memori",
|
Copy: "Salin Ringkasan",
|
||||||
Reset: "Reset",
|
Reset: "[unused]",
|
||||||
ResetConfirm:
|
ResetConfirm: "Konfirmasi untuk menghapus ringkasan sejarah?",
|
||||||
"Jika Anda mereset, riwayat obrolan saat ini dan memori historis akan dihapus. Apakah Anda yakin ingin melakukan reset?",
|
|
||||||
},
|
},
|
||||||
Home: {
|
Home: {
|
||||||
NewChat: "Obrolan Baru",
|
NewChat: "Obrolan Baru",
|
||||||
DeleteChat: "Anda yakin ingin menghapus percakapan yang dipilih?",
|
DeleteChat: "Konfirmasi untuk menghapus percakapan yang dipilih?",
|
||||||
DeleteToast: "Percakapan telah dihapus",
|
DeleteToast: "Percakapan telah dihapus",
|
||||||
Revert: "Kembali",
|
Revert: "Batalkan",
|
||||||
},
|
},
|
||||||
Settings: {
|
Settings: {
|
||||||
Title: "Pengaturan",
|
Title: "Pengaturan",
|
||||||
SubTitle: "Semua Pengaturan",
|
SubTitle: "Semua opsi pengaturan",
|
||||||
|
|
||||||
Danger: {
|
Danger: {
|
||||||
Reset: {
|
Reset: {
|
||||||
Title: "Setel Ulang Semua Pengaturan",
|
Title: "Atur Ulang Semua Pengaturan",
|
||||||
SubTitle: "Mengembalikan semua pengaturan ke nilai default",
|
SubTitle: "Atur ulang semua opsi pengaturan ke nilai default",
|
||||||
Action: "Setel Ulang",
|
Action: "Atur Ulang Sekarang",
|
||||||
Confirm:
|
Confirm: "Konfirmasi untuk mengatur ulang semua pengaturan?",
|
||||||
"Anda yakin ingin mengembalikan semua pengaturan ke nilai default?",
|
|
||||||
},
|
},
|
||||||
Clear: {
|
Clear: {
|
||||||
Title: "Hapus Semua Data",
|
Title: "Hapus Semua Data",
|
||||||
SubTitle: "Semua data yang tersimpan secara lokal akan dihapus",
|
SubTitle: "Hapus semua data obrolan dan pengaturan",
|
||||||
Action: "Hapus",
|
Action: "Hapus Sekarang",
|
||||||
Confirm:
|
Confirm:
|
||||||
"Apakah Anda yakin ingin menghapus semua data yang tersimpan secara lokal?",
|
"Konfirmasi untuk menghapus semua data obrolan dan pengaturan?",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Lang: {
|
Lang: {
|
||||||
Name: "Bahasa", // ATTENTION: if you wanna add a new translation, please do not translate this value, leave it as `Language`
|
Name: "Language", // PERHATIAN: jika Anda ingin menambahkan terjemahan baru, harap jangan terjemahkan nilai ini, biarkan sebagai `Language`
|
||||||
All: "Semua Bahasa",
|
All: "Semua Bahasa",
|
||||||
},
|
},
|
||||||
Avatar: "Avatar",
|
Avatar: "Avatar",
|
||||||
FontSize: {
|
FontSize: {
|
||||||
Title: "Ukuran Font",
|
Title: "Ukuran Font",
|
||||||
SubTitle: "Ubah ukuran font konten chat",
|
SubTitle: "Ukuran font untuk konten obrolan",
|
||||||
},
|
},
|
||||||
FontFamily: {
|
FontFamily: {
|
||||||
Title: "Font Obrolan",
|
Title: "Font Obrolan",
|
||||||
|
@ -147,88 +173,101 @@ const id: PartialLocaleType = {
|
||||||
Placeholder: "Nama Font",
|
Placeholder: "Nama Font",
|
||||||
},
|
},
|
||||||
InjectSystemPrompts: {
|
InjectSystemPrompts: {
|
||||||
Title: "Suntikkan Petunjuk Sistem",
|
Title: "Suntikkan Pesan Sistem",
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Tambahkan petunjuk simulasi sistem ChatGPT di awal daftar pesan yang diminta dalam setiap permintaan",
|
"Memaksa menambahkan pesan sistem simulasi ChatGPT di awal daftar pesan setiap permintaan",
|
||||||
},
|
},
|
||||||
InputTemplate: {
|
InputTemplate: {
|
||||||
Title: "Template Input",
|
Title: "Pra-pemrosesan Input Pengguna",
|
||||||
SubTitle: "Pesan baru akan diisi menggunakan template ini",
|
SubTitle: "Pesan terbaru pengguna akan diisi ke template ini",
|
||||||
},
|
},
|
||||||
|
|
||||||
Update: {
|
Update: {
|
||||||
Version: (x: string) => `Version: ${x}`,
|
Version: (x: string) => `Versi Saat Ini: ${x}`,
|
||||||
IsLatest: "Versi terbaru",
|
IsLatest: "Sudah versi terbaru",
|
||||||
CheckUpdate: "Periksa Pembaruan",
|
CheckUpdate: "Periksa Pembaruan",
|
||||||
IsChecking: "Memeriksa pembaruan...",
|
IsChecking: "Sedang memeriksa pembaruan...",
|
||||||
FoundUpdate: (x: string) => `Versi terbaru ditemukan: ${x}`,
|
FoundUpdate: (x: string) => `Versi Baru Ditemukan: ${x}`,
|
||||||
GoToUpdate: "Perbarui Sekarang",
|
GoToUpdate: "Pergi ke Pembaruan",
|
||||||
|
},
|
||||||
|
SendKey: "Kunci Kirim",
|
||||||
|
Theme: "Tema",
|
||||||
|
TightBorder: "Mode Tanpa Border",
|
||||||
|
SendPreviewBubble: {
|
||||||
|
Title: "Preview Bubble",
|
||||||
|
SubTitle: "Pratinjau konten Markdown di bubble pratinjau",
|
||||||
},
|
},
|
||||||
AutoGenerateTitle: {
|
AutoGenerateTitle: {
|
||||||
Title: "Hasilkan Judul Otomatis",
|
Title: "Otomatis Membuat Judul",
|
||||||
SubTitle: "Hasilkan judul yang sesuai berdasarkan konten percakapan",
|
SubTitle: "Membuat judul yang sesuai berdasarkan konten obrolan",
|
||||||
},
|
},
|
||||||
Sync: {
|
Sync: {
|
||||||
CloudState: "Pembaruan Terakhir",
|
CloudState: "Data Cloud",
|
||||||
NotSyncYet: "Belum disinkronkan",
|
NotSyncYet: "Belum disinkronkan",
|
||||||
Success: "Sinkronisasi Berhasil",
|
Success: "Sinkronisasi Berhasil",
|
||||||
Fail: "Sinkronisasi Gagal",
|
Fail: "Sinkronisasi Gagal",
|
||||||
|
|
||||||
Config: {
|
Config: {
|
||||||
Modal: {
|
Modal: {
|
||||||
Title: "Konfigurasi Sinkronisasi",
|
Title: "Konfigurasi Sinkronisasi Cloud",
|
||||||
|
Check: "Periksa Ketersediaan",
|
||||||
},
|
},
|
||||||
SyncType: {
|
SyncType: {
|
||||||
Title: "Tipe Sinkronisasi",
|
Title: "Jenis Sinkronisasi",
|
||||||
SubTitle: "Pilih layanan sinkronisasi favorit Anda",
|
SubTitle: "Pilih server sinkronisasi favorit",
|
||||||
},
|
},
|
||||||
Proxy: {
|
Proxy: {
|
||||||
Title: "Aktifkan Proxy CORS",
|
Title: "Aktifkan Proxy",
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Aktifkan Proxy untuk menghindari pembatasan atau pemblokiran lintas sumber",
|
"Saat menyinkronkan di browser, proxy harus diaktifkan untuk menghindari pembatasan lintas domain",
|
||||||
},
|
},
|
||||||
ProxyUrl: {
|
ProxyUrl: {
|
||||||
Title: "Lokasi Titik Akhir Proxy CORS",
|
Title: "Alamat Proxy",
|
||||||
SubTitle: "Hanya berlaku untuk Proxy CORS bawaan untuk proyek ini",
|
SubTitle: "Hanya berlaku untuk proxy lintas domain bawaan proyek ini",
|
||||||
},
|
},
|
||||||
|
|
||||||
WebDav: {
|
WebDav: {
|
||||||
Endpoint: "Lokasi Titik Akhir WebDAV",
|
Endpoint: "Alamat WebDAV",
|
||||||
UserName: "User Pengguna",
|
UserName: "Nama Pengguna",
|
||||||
Password: "Kata Sandi",
|
Password: "Kata Sandi",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
UpStash: {
|
||||||
|
Endpoint: "Url REST Redis UpStash",
|
||||||
|
UserName: "Nama Cadangan",
|
||||||
|
Password: "Token REST Redis UpStash",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
SendKey: "Kirim",
|
|
||||||
Theme: "Tema",
|
LocalState: "Data Lokal",
|
||||||
TightBorder: "Batas Ketat",
|
Overview: (overview: any) => {
|
||||||
SendPreviewBubble: {
|
return `${overview.chat} percakapan, ${overview.message} pesan, ${overview.prompt} prompt, ${overview.mask} masker`;
|
||||||
Title: "Pratinjau Obrolan",
|
},
|
||||||
SubTitle: "Pratinjau Obrolan dengan markdown",
|
ImportFailed: "Impor Gagal",
|
||||||
},
|
},
|
||||||
Mask: {
|
Mask: {
|
||||||
Splash: {
|
Splash: {
|
||||||
Title: "Layar Pembuka Masks",
|
Title: "Halaman Awal Masker",
|
||||||
SubTitle:
|
SubTitle: "Tampilkan halaman awal masker saat memulai obrolan baru",
|
||||||
"Tampilkan layar pembuka masks sebelum memulai percakapan baru",
|
|
||||||
},
|
},
|
||||||
Builtin: {
|
Builtin: {
|
||||||
Title: "Sembunyikan Masks Bawaan",
|
Title: "Sembunyikan Masker Bawaan",
|
||||||
SubTitle: "Sembunyikan Masks bawaan dari daftar masks",
|
SubTitle: "Sembunyikan masker bawaan dari semua daftar masker",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Prompt: {
|
Prompt: {
|
||||||
Disable: {
|
Disable: {
|
||||||
Title: "Nonaktifkan Otomatisasi",
|
Title: "Nonaktifkan Pelengkapan Prompt Otomatis",
|
||||||
SubTitle: "Aktifkan/Matikan otomatisasi",
|
SubTitle:
|
||||||
|
"Ketik / di awal kotak input untuk memicu pelengkapan otomatis",
|
||||||
},
|
},
|
||||||
List: "Daftar Prompt",
|
List: "Daftar Prompt Kustom",
|
||||||
ListCount: (builtin: number, custom: number) =>
|
ListCount: (builtin: number, custom: number) =>
|
||||||
`${builtin} bawaan, ${custom} penggunaan khusus`,
|
`Bawaan ${builtin} item, pengguna ${custom} item`,
|
||||||
Edit: "Edit",
|
Edit: "Edit",
|
||||||
Modal: {
|
Modal: {
|
||||||
Title: "Daftar Prompt",
|
Title: "Daftar Prompt",
|
||||||
Add: "Tambahkan",
|
Add: "Baru",
|
||||||
Search: "Cari Prompt",
|
Search: "Cari Prompt",
|
||||||
},
|
},
|
||||||
EditModal: {
|
EditModal: {
|
||||||
|
@ -236,142 +275,311 @@ const id: PartialLocaleType = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
HistoryCount: {
|
HistoryCount: {
|
||||||
Title: "Jumlah Pesan Riwayat",
|
Title: "Jumlah Pesan Sejarah",
|
||||||
SubTitle: "Jumlah pesan yang akan dikirim setiap permintaan",
|
SubTitle: "Jumlah pesan sejarah yang dibawa setiap permintaan",
|
||||||
},
|
},
|
||||||
CompressThreshold: {
|
CompressThreshold: {
|
||||||
Title: "Batas Kompresi Riwayat",
|
Title: "Ambang Batas Kompresi Pesan Sejarah",
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Jika panjang pesan melebihi batas yang ditentukan, pesan tersebut akan dikompresi",
|
"Ketika pesan sejarah yang tidak terkompresi melebihi nilai ini, akan dikompresi",
|
||||||
},
|
},
|
||||||
|
|
||||||
Usage: {
|
Usage: {
|
||||||
Title: "Saldo Akun",
|
Title: "Cek Saldo",
|
||||||
SubTitle(used: any, total: any) {
|
SubTitle(used: any, total: any) {
|
||||||
return `Digunakan bulan ini: ${used}, total langganan: ${total}`;
|
return `Digunakan bulan ini $${used}, total langganan $${total}`;
|
||||||
|
},
|
||||||
|
IsChecking: "Sedang memeriksa…",
|
||||||
|
Check: "Periksa Lagi",
|
||||||
|
NoAccess: "Masukkan API Key atau kata sandi akses untuk melihat saldo",
|
||||||
|
},
|
||||||
|
|
||||||
|
Access: {
|
||||||
|
SaasStart: {
|
||||||
|
Title: "Gunakan NextChat AI",
|
||||||
|
Label: "(Solusi paling hemat biaya)",
|
||||||
|
SubTitle:
|
||||||
|
"Dikelola secara resmi oleh NextChat, siap digunakan tanpa konfigurasi, mendukung model besar terbaru seperti OpenAI o1, GPT-4o, dan Claude-3.5",
|
||||||
|
ChatNow: "Chat Sekarang",
|
||||||
|
},
|
||||||
|
|
||||||
|
AccessCode: {
|
||||||
|
Title: "Kata Sandi Akses",
|
||||||
|
SubTitle: "Administrator telah mengaktifkan akses terenkripsi",
|
||||||
|
Placeholder: "Masukkan kata sandi akses",
|
||||||
|
},
|
||||||
|
CustomEndpoint: {
|
||||||
|
Title: "Antarmuka Kustom",
|
||||||
|
SubTitle: "Apakah akan menggunakan layanan Azure atau OpenAI kustom",
|
||||||
|
},
|
||||||
|
Provider: {
|
||||||
|
Title: "Penyedia Layanan Model",
|
||||||
|
SubTitle: "Ganti penyedia layanan yang berbeda",
|
||||||
|
},
|
||||||
|
OpenAI: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "API Key",
|
||||||
|
SubTitle:
|
||||||
|
"Gunakan OpenAI Key kustom untuk menghindari batasan akses kata sandi",
|
||||||
|
Placeholder: "OpenAI API Key",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Alamat Antarmuka",
|
||||||
|
SubTitle: "Selain alamat default, harus menyertakan http(s)://",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Azure: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "Kunci Antarmuka",
|
||||||
|
SubTitle:
|
||||||
|
"Gunakan Azure Key kustom untuk menghindari batasan akses kata sandi",
|
||||||
|
Placeholder: "Azure API Key",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Alamat Antarmuka",
|
||||||
|
SubTitle: "Contoh:",
|
||||||
|
},
|
||||||
|
|
||||||
|
ApiVerion: {
|
||||||
|
Title: "Versi Antarmuka (azure api version)",
|
||||||
|
SubTitle: "Pilih versi parsial tertentu",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Anthropic: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "Kunci Antarmuka",
|
||||||
|
SubTitle:
|
||||||
|
"Gunakan Anthropic Key kustom untuk menghindari batasan akses kata sandi",
|
||||||
|
Placeholder: "Anthropic API Key",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Alamat Antarmuka",
|
||||||
|
SubTitle: "Contoh:",
|
||||||
|
},
|
||||||
|
|
||||||
|
ApiVerion: {
|
||||||
|
Title: "Versi Antarmuka (claude api version)",
|
||||||
|
SubTitle: "Pilih versi API tertentu",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Google: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "Kunci API",
|
||||||
|
SubTitle: "Dapatkan kunci API Anda dari Google AI",
|
||||||
|
Placeholder: "Masukkan kunci API Studio Google AI Anda",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Alamat Akhir",
|
||||||
|
SubTitle: "Contoh:",
|
||||||
|
},
|
||||||
|
|
||||||
|
ApiVersion: {
|
||||||
|
Title: "Versi API (hanya untuk gemini-pro)",
|
||||||
|
SubTitle: "Pilih versi API tertentu",
|
||||||
|
},
|
||||||
|
GoogleSafetySettings: {
|
||||||
|
Title: "Tingkat Filter Keamanan Google",
|
||||||
|
SubTitle: "Atur tingkat filter konten",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Baidu: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "API Key",
|
||||||
|
SubTitle: "Gunakan Baidu API Key kustom",
|
||||||
|
Placeholder: "Baidu API Key",
|
||||||
|
},
|
||||||
|
SecretKey: {
|
||||||
|
Title: "Secret Key",
|
||||||
|
SubTitle: "Gunakan Baidu Secret Key kustom",
|
||||||
|
Placeholder: "Baidu Secret Key",
|
||||||
|
},
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Alamat Antarmuka",
|
||||||
|
SubTitle: "Tidak mendukung kustom, pergi ke .env untuk konfigurasi",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ByteDance: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "Kunci Antarmuka",
|
||||||
|
SubTitle: "Gunakan ByteDance API Key kustom",
|
||||||
|
Placeholder: "ByteDance API Key",
|
||||||
|
},
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Alamat Antarmuka",
|
||||||
|
SubTitle: "Contoh:",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Alibaba: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "Kunci Antarmuka",
|
||||||
|
SubTitle: "Gunakan Alibaba Cloud API Key kustom",
|
||||||
|
Placeholder: "Alibaba Cloud API Key",
|
||||||
|
},
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Alamat Antarmuka",
|
||||||
|
SubTitle: "Contoh:",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CustomModel: {
|
||||||
|
Title: "Nama Model Kustom",
|
||||||
|
SubTitle: "Tambahkan opsi model kustom, pisahkan dengan koma",
|
||||||
},
|
},
|
||||||
IsChecking: "Memeriksa...",
|
|
||||||
Check: "Periksa",
|
|
||||||
NoAccess: "Masukkan kunci API untuk memeriksa saldo",
|
|
||||||
},
|
},
|
||||||
|
|
||||||
Model: "Model",
|
Model: "Model",
|
||||||
|
CompressModel: {
|
||||||
|
Title: "Model Kompresi",
|
||||||
|
SubTitle: "Model yang digunakan untuk mengompres riwayat",
|
||||||
|
},
|
||||||
Temperature: {
|
Temperature: {
|
||||||
Title: "Suhu",
|
Title: "Randomness (temperature)",
|
||||||
SubTitle: "Semakin tinggi nilainya, semakin acak keluarannya",
|
SubTitle: "Semakin tinggi nilainya, semakin acak responsnya",
|
||||||
},
|
},
|
||||||
TopP: {
|
TopP: {
|
||||||
Title: "Top P",
|
Title: "Sampling Inti (top_p)",
|
||||||
SubTitle: "Tidak mengubah nilai dengan suhu",
|
SubTitle:
|
||||||
|
"Mirip dengan randomness, tetapi jangan ubah bersama randomness",
|
||||||
},
|
},
|
||||||
MaxTokens: {
|
MaxTokens: {
|
||||||
Title: "Token Maksimum",
|
Title: "Batas Token Per Respons",
|
||||||
SubTitle: "Panjang maksimum token input dan output",
|
SubTitle: "Jumlah token maksimum yang digunakan per interaksi",
|
||||||
},
|
},
|
||||||
PresencePenalty: {
|
PresencePenalty: {
|
||||||
Title: "Penalti Kehadiran",
|
Title: "Kedekatan Topik (presence_penalty)",
|
||||||
SubTitle: "Semakin tinggi nilai, semakin mungkin topik baru muncul",
|
SubTitle:
|
||||||
|
"Semakin tinggi nilainya, semakin besar kemungkinan memperluas ke topik baru",
|
||||||
},
|
},
|
||||||
FrequencyPenalty: {
|
FrequencyPenalty: {
|
||||||
Title: "Penalti Frekuensi",
|
Title: "Hukuman Frekuensi (frequency_penalty)",
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Semakin tinggi nilai, semakin rendah kemungkinan penggunaan ulang baris yang sama",
|
"Semakin tinggi nilainya, semakin besar kemungkinan mengurangi kata-kata yang berulang",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Store: {
|
Store: {
|
||||||
DefaultTopic: "Percakapan Baru",
|
DefaultTopic: "Obrolan Baru",
|
||||||
BotHello: "Halo! Bagaimana saya bisa membantu Anda hari ini?",
|
BotHello: "Ada yang bisa saya bantu?",
|
||||||
Error: "Terjadi kesalahan, silakan coba lagi nanti.",
|
Error: "Terjadi kesalahan, coba lagi nanti",
|
||||||
Prompt: {
|
Prompt: {
|
||||||
History: (content: string) =>
|
History: (content: string) =>
|
||||||
"Ini adalah ringkasan singkat dari riwayat percakapan: " + content,
|
"Ini adalah ringkasan obrolan sebelumnya sebagai latar belakang: " +
|
||||||
|
content,
|
||||||
Topic:
|
Topic:
|
||||||
"Buat judul berisi empat hingga lima kata untuk percakapan kita yang tidak akan disertakan dalam ringkasan percakapan, seperti instruksi, format, kutipan, tanda baca awal, tanda kutip pendahuluan, atau karakter tambahan. Silakan coba dengan kutipan berakhir.",
|
"Gunakan empat hingga lima kata untuk langsung memberikan ringkasan topik kalimat ini, tanpa penjelasan, tanpa tanda baca, tanpa kata pengisi, tanpa teks tambahan, tanpa menebalkan. Jika tidak ada topik, langsung jawab 'Obrolan Santai'",
|
||||||
Summarize:
|
Summarize:
|
||||||
"Buat ringkasan percakapan dalam 200 kata yang akan digunakan sebagai promp di masa depan.",
|
"Berikan ringkasan singkat tentang konten obrolan, untuk digunakan sebagai prompt konteks selanjutnya, dalam 200 kata atau kurang",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Copy: {
|
Copy: {
|
||||||
Success: "Tersalin ke clipboard",
|
Success: "Telah disalin ke clipboard",
|
||||||
Failed:
|
Failed: "Gagal menyalin, mohon berikan izin clipboard",
|
||||||
"Gagal menyalin, mohon berikan izin untuk mengakses clipboard atau Clipboard API tidak didukung (Tauri)",
|
|
||||||
},
|
},
|
||||||
Download: {
|
Download: {
|
||||||
Success: "Konten berhasil diunduh ke direktori Anda.",
|
Success: "Konten telah diunduh ke direktori Anda.",
|
||||||
Failed: "Unduhan gagal.",
|
Failed: "Unduhan gagal.",
|
||||||
},
|
},
|
||||||
Context: {
|
Context: {
|
||||||
Toast: (x: any) => `Dengan ${x} promp kontekstual`,
|
Toast: (x: any) => `Berisi ${x} prompt preset`,
|
||||||
Edit: "Pengaturan Obrolan Saat Ini",
|
Edit: "Pengaturan Obrolan Saat Ini",
|
||||||
Add: "Tambahkan Promp",
|
Add: "Tambah Obrolan",
|
||||||
Clear: "Bersihkan Konteks",
|
Clear: "Konteks telah dihapus",
|
||||||
Revert: "Kembali ke Posisi Sebelumnya",
|
Revert: "Kembalikan Konteks",
|
||||||
},
|
},
|
||||||
Plugin: {
|
Plugin: {
|
||||||
Name: "Plugin",
|
Name: "Plugin",
|
||||||
},
|
},
|
||||||
FineTuned: {
|
FineTuned: {
|
||||||
Sysmessage: "Anda adalah asisten yang",
|
Sysmessage: "Anda adalah seorang asisten",
|
||||||
},
|
},
|
||||||
Mask: {
|
SearchChat: {
|
||||||
Name: "Masks",
|
Name: "Cari",
|
||||||
Page: {
|
Page: {
|
||||||
Title: "Template Promp",
|
Title: "Cari riwayat obrolan",
|
||||||
SubTitle: (count: number) => `${count} template prompt`,
|
Search: "Masukkan kata kunci pencarian",
|
||||||
Search: "Cari template",
|
NoResult: "Tidak ada hasil ditemukan",
|
||||||
Create: "Buat",
|
NoData: "Tidak ada data",
|
||||||
|
Loading: "Memuat",
|
||||||
|
|
||||||
|
SubTitle: (count: number) => `Ditemukan ${count} hasil`,
|
||||||
},
|
},
|
||||||
Item: {
|
Item: {
|
||||||
Info: (count: number) => `${count} prompt`,
|
View: "Lihat",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Mask: {
|
||||||
|
Name: "Masker",
|
||||||
|
Page: {
|
||||||
|
Title: "Preset Karakter Masker",
|
||||||
|
SubTitle: (count: number) => `${count} definisi karakter preset`,
|
||||||
|
Search: "Cari Masker Karakter",
|
||||||
|
Create: "Buat Baru",
|
||||||
|
},
|
||||||
|
Item: {
|
||||||
|
Info: (count: number) => `Berisi ${count} obrolan preset`,
|
||||||
Chat: "Obrolan",
|
Chat: "Obrolan",
|
||||||
View: "Lihat",
|
View: "Lihat",
|
||||||
Edit: "Edit",
|
Edit: "Edit",
|
||||||
Delete: "Hapus",
|
Delete: "Hapus",
|
||||||
DeleteConfirm: "Anda yakin ingin menghapus?",
|
DeleteConfirm: "Konfirmasi penghapusan?",
|
||||||
},
|
},
|
||||||
EditModal: {
|
EditModal: {
|
||||||
Title: (readonly: boolean) =>
|
Title: (readonly: boolean) =>
|
||||||
`Edit Template Prompt ${readonly ? "(hanya baca)" : ""}`,
|
`Edit Masker Preset ${readonly ? "(Hanya Baca)" : ""}`,
|
||||||
Download: "Unduh",
|
Download: "Unduh Preset",
|
||||||
Clone: "Duplikat",
|
Clone: "Klon Preset",
|
||||||
},
|
},
|
||||||
Config: {
|
Config: {
|
||||||
Avatar: "Avatar Bot",
|
Avatar: "Avatar Karakter",
|
||||||
Name: "Nama Bot",
|
Name: "Nama Karakter",
|
||||||
Sync: {
|
Sync: {
|
||||||
Title: "Gunakan Konfigurasi Global",
|
Title: "Gunakan Pengaturan Global",
|
||||||
SubTitle: "Gunakan konfigurasi global dalam percakapan ini",
|
SubTitle:
|
||||||
|
"Apakah obrolan saat ini akan menggunakan pengaturan model global?",
|
||||||
Confirm:
|
Confirm:
|
||||||
"Pastikan untuk mengganti konfigurasi kustom dengan konfigurasi global?",
|
"Pengaturan kustom obrolan saat ini akan ditimpa secara otomatis, konfirmasi untuk mengaktifkan pengaturan global?",
|
||||||
},
|
},
|
||||||
HideContext: {
|
HideContext: {
|
||||||
Title: "Sembunyikan Prompt Konteks",
|
Title: "Sembunyikan Obrolan Preset",
|
||||||
SubTitle: "Tidak menampilkan prompt konteks dalam obrolan",
|
SubTitle:
|
||||||
|
"Setelah disembunyikan, obrolan preset tidak akan muncul di antarmuka obrolan",
|
||||||
},
|
},
|
||||||
Share: {
|
Share: {
|
||||||
Title: "Bagikan Masks Ini",
|
Title: "Bagikan Masker Ini",
|
||||||
SubTitle: "Buat tautan untuk masks ini",
|
SubTitle: "Hasilkan tautan langsung ke masker ini",
|
||||||
Action: "Salin Tautan",
|
Action: "Salin Tautan",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
NewChat: {
|
NewChat: {
|
||||||
Return: "Kembali",
|
Return: "Kembali",
|
||||||
Skip: "Lewati",
|
Skip: "Mulai Sekarang",
|
||||||
Title: "Pilih Masks",
|
NotShow: "Jangan Tampilkan Lagi",
|
||||||
SubTitle: "Berkonversasilah dengan diri Anda di balik masks",
|
|
||||||
More: "Lebih Lanjut",
|
|
||||||
NotShow: "Jangan Tampilkan Sekarang",
|
|
||||||
ConfirmNoShow:
|
ConfirmNoShow:
|
||||||
"Pastikan untuk menonaktifkannya? Anda dapat mengaktifkannya nanti melalui pengaturan.",
|
"Konfirmasi untuk menonaktifkan? Setelah dinonaktifkan, Anda dapat mengaktifkannya kembali kapan saja di pengaturan.",
|
||||||
|
Title: "Pilih Masker",
|
||||||
|
SubTitle: "Mulai sekarang, berinteraksi dengan pemikiran di balik masker",
|
||||||
|
More: "Lihat Semua",
|
||||||
|
},
|
||||||
|
|
||||||
|
URLCommand: {
|
||||||
|
Code: "Terdeteksi bahwa tautan sudah mengandung kode akses, apakah akan diisi secara otomatis?",
|
||||||
|
Settings:
|
||||||
|
"Terdeteksi bahwa tautan mengandung pengaturan preset, apakah akan diisi secara otomatis?",
|
||||||
},
|
},
|
||||||
|
|
||||||
UI: {
|
UI: {
|
||||||
Confirm: "Konfirmasi",
|
Confirm: "Konfirmasi",
|
||||||
Cancel: "Batal",
|
Cancel: "Batal",
|
||||||
Close: "Tutup",
|
Close: "Tutup",
|
||||||
Create: "Buat",
|
Create: "Buat Baru",
|
||||||
Edit: "Edit",
|
Edit: "Edit",
|
||||||
|
Export: "Ekspor",
|
||||||
|
Import: "Impor",
|
||||||
|
Sync: "Sinkronkan",
|
||||||
|
Config: "Konfigurasi",
|
||||||
},
|
},
|
||||||
Exporter: {
|
Exporter: {
|
||||||
Description: {
|
Description: {
|
||||||
|
@ -380,11 +588,7 @@ const id: PartialLocaleType = {
|
||||||
Model: "Model",
|
Model: "Model",
|
||||||
Messages: "Pesan",
|
Messages: "Pesan",
|
||||||
Topic: "Topik",
|
Topic: "Topik",
|
||||||
Time: "Tanggal & Waktu",
|
Time: "Waktu",
|
||||||
},
|
|
||||||
URLCommand: {
|
|
||||||
Code: "Kode akses terdeteksi dari url, konfirmasi untuk mendaftar ? ",
|
|
||||||
Settings: "Pengaturan terdeteksi dari url, konfirmasi untuk diterapkan ?",
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -18,10 +18,13 @@ import ar from "./ar";
|
||||||
import bn from "./bn";
|
import bn from "./bn";
|
||||||
import sk from "./sk";
|
import sk from "./sk";
|
||||||
import { merge } from "../utils/merge";
|
import { merge } from "../utils/merge";
|
||||||
|
import { safeLocalStorage } from "@/app/utils";
|
||||||
|
|
||||||
import type { LocaleType } from "./cn";
|
import type { LocaleType } from "./cn";
|
||||||
export type { LocaleType, PartialLocaleType } from "./cn";
|
export type { LocaleType, PartialLocaleType } from "./cn";
|
||||||
|
|
||||||
|
const localStorage = safeLocalStorage();
|
||||||
|
|
||||||
const ALL_LANGS = {
|
const ALL_LANGS = {
|
||||||
cn,
|
cn,
|
||||||
en,
|
en,
|
||||||
|
@ -82,17 +85,11 @@ merge(fallbackLang, targetLang);
|
||||||
export default fallbackLang as LocaleType;
|
export default fallbackLang as LocaleType;
|
||||||
|
|
||||||
function getItem(key: string) {
|
function getItem(key: string) {
|
||||||
try {
|
|
||||||
return localStorage.getItem(key);
|
return localStorage.getItem(key);
|
||||||
} catch {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function setItem(key: string, value: string) {
|
function setItem(key: string, value: string) {
|
||||||
try {
|
|
||||||
localStorage.setItem(key, value);
|
localStorage.setItem(key, value);
|
||||||
} catch {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLanguage() {
|
function getLanguage() {
|
||||||
|
@ -137,3 +134,34 @@ export function getISOLang() {
|
||||||
const lang = getLang();
|
const lang = getLang();
|
||||||
return isoLangString[lang] ?? lang;
|
return isoLangString[lang] ?? lang;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DEFAULT_STT_LANG = "zh-CN";
|
||||||
|
export const STT_LANG_MAP: Record<Lang, string> = {
|
||||||
|
cn: "zh-CN",
|
||||||
|
en: "en-US",
|
||||||
|
pt: "pt-BR",
|
||||||
|
tw: "zh-TW",
|
||||||
|
jp: "ja-JP",
|
||||||
|
ko: "ko-KR",
|
||||||
|
id: "id-ID",
|
||||||
|
fr: "fr-FR",
|
||||||
|
es: "es-ES",
|
||||||
|
it: "it-IT",
|
||||||
|
tr: "tr-TR",
|
||||||
|
de: "de-DE",
|
||||||
|
vi: "vi-VN",
|
||||||
|
ru: "ru-RU",
|
||||||
|
cs: "cs-CZ",
|
||||||
|
no: "no-NO",
|
||||||
|
ar: "ar-SA",
|
||||||
|
bn: "bn-BD",
|
||||||
|
sk: "sk-SK",
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getSTTLang(): string {
|
||||||
|
try {
|
||||||
|
return STT_LANG_MAP[getLang()];
|
||||||
|
} catch {
|
||||||
|
return DEFAULT_STT_LANG;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,75 +1,176 @@
|
||||||
import { SubmitKey } from "../store/config";
|
import { SubmitKey } from "../store/config";
|
||||||
import type { PartialLocaleType } from "./index";
|
import type { PartialLocaleType } from "./index";
|
||||||
|
import { getClientConfig } from "../config/client";
|
||||||
|
import { SAAS_CHAT_UTM_URL } from "@/app/constant";
|
||||||
|
const isApp = !!getClientConfig()?.isApp;
|
||||||
|
|
||||||
const it: PartialLocaleType = {
|
const it: PartialLocaleType = {
|
||||||
WIP: "Work in progress...",
|
WIP: "Work in progress...",
|
||||||
Error: {
|
Error: {
|
||||||
Unauthorized:
|
Unauthorized: isApp
|
||||||
"Accesso non autorizzato, inserire il codice di accesso nella [pagina](/#/auth) delle impostazioni.",
|
? `😆 La conversazione ha incontrato alcuni problemi, non preoccuparti:
|
||||||
|
\\ 1️⃣ Se vuoi iniziare senza configurazione, [clicca qui per iniziare a chattare immediatamente 🚀](${SAAS_CHAT_UTM_URL})
|
||||||
|
\\ 2️⃣ Se vuoi utilizzare le tue risorse OpenAI, clicca [qui](/#/settings) per modificare le impostazioni ⚙️`
|
||||||
|
: `😆 La conversazione ha incontrato alcuni problemi, non preoccuparti:
|
||||||
|
\ 1️⃣ Se vuoi iniziare senza configurazione, [clicca qui per iniziare a chattare immediatamente 🚀](${SAAS_CHAT_UTM_URL})
|
||||||
|
\ 2️⃣ Se stai utilizzando una versione di distribuzione privata, clicca [qui](/#/auth) per inserire la chiave di accesso 🔑
|
||||||
|
\ 3️⃣ Se vuoi utilizzare le tue risorse OpenAI, clicca [qui](/#/settings) per modificare le impostazioni ⚙️
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
Auth: {
|
||||||
|
Title: "Password richiesta",
|
||||||
|
Tips: "L'amministratore ha abilitato la verifica della password. Inserisci il codice di accesso qui sotto",
|
||||||
|
SubTips: "O inserisci la tua chiave API OpenAI o Google",
|
||||||
|
Input: "Inserisci il codice di accesso qui",
|
||||||
|
Confirm: "Conferma",
|
||||||
|
Later: "Più tardi",
|
||||||
|
Return: "Ritorna",
|
||||||
|
SaasTips:
|
||||||
|
"La configurazione è troppo complicata, voglio usarlo immediatamente",
|
||||||
|
TopTips:
|
||||||
|
"🥳 Offerta di lancio NextChat AI, sblocca OpenAI o1, GPT-4o, Claude-3.5 e i più recenti modelli di grandi dimensioni",
|
||||||
},
|
},
|
||||||
ChatItem: {
|
ChatItem: {
|
||||||
ChatItemCount: (count: number) => `${count} messaggi`,
|
ChatItemCount: (count: number) => `${count} conversazioni`,
|
||||||
},
|
},
|
||||||
Chat: {
|
Chat: {
|
||||||
SubTitle: (count: number) => `${count} messaggi con ChatGPT`,
|
SubTitle: (count: number) => `Totale ${count} conversazioni`,
|
||||||
Actions: {
|
EditMessage: {
|
||||||
ChatList: "Vai alla Chat List",
|
Title: "Modifica cronologia messaggi",
|
||||||
CompressedHistory: "Prompt di memoria della cronologia compressa",
|
Topic: {
|
||||||
Export: "Esportazione di tutti i messaggi come Markdown",
|
Title: "Argomento della chat",
|
||||||
Copy: "Copia",
|
SubTitle: "Modifica l'argomento della chat corrente",
|
||||||
Stop: "Stop",
|
|
||||||
Retry: "Riprova",
|
|
||||||
Delete: "Delete",
|
|
||||||
},
|
},
|
||||||
Rename: "Rinomina Chat",
|
},
|
||||||
Typing: "Typing…",
|
Actions: {
|
||||||
|
ChatList: "Visualizza l'elenco dei messaggi",
|
||||||
|
CompressedHistory: "Visualizza la cronologia Prompt compressa",
|
||||||
|
Export: "Esporta la cronologia chat",
|
||||||
|
Copy: "Copia",
|
||||||
|
Stop: "Interrompi",
|
||||||
|
Retry: "Riprova",
|
||||||
|
Pin: "Fissa",
|
||||||
|
PinToastContent: "1 conversazione fissata ai suggerimenti predefiniti",
|
||||||
|
PinToastAction: "Visualizza",
|
||||||
|
Delete: "Elimina",
|
||||||
|
Edit: "Modifica",
|
||||||
|
RefreshTitle: "Aggiorna titolo",
|
||||||
|
RefreshToast: "Richiesta di aggiornamento del titolo inviata",
|
||||||
|
},
|
||||||
|
Commands: {
|
||||||
|
new: "Nuova chat",
|
||||||
|
newm: "Nuova chat da maschera",
|
||||||
|
next: "Chat successiva",
|
||||||
|
prev: "Chat precedente",
|
||||||
|
clear: "Pulisci contesto",
|
||||||
|
del: "Elimina chat",
|
||||||
|
},
|
||||||
|
InputActions: {
|
||||||
|
Stop: "Interrompi risposta",
|
||||||
|
ToBottom: "Scorri fino al più recente",
|
||||||
|
Theme: {
|
||||||
|
auto: "Tema automatico",
|
||||||
|
light: "Tema chiaro",
|
||||||
|
dark: "Tema scuro",
|
||||||
|
},
|
||||||
|
Prompt: "Comandi rapidi",
|
||||||
|
Masks: "Tutte le maschere",
|
||||||
|
Clear: "Pulisci chat",
|
||||||
|
Settings: "Impostazioni conversazione",
|
||||||
|
UploadImage: "Carica immagine",
|
||||||
|
},
|
||||||
|
Rename: "Rinomina conversazione",
|
||||||
|
Typing: "Digitazione in corso…",
|
||||||
Input: (submitKey: string) => {
|
Input: (submitKey: string) => {
|
||||||
var inputHints = `Scrivi qualcosa e premi ${submitKey} per inviare`;
|
var inputHints = `${submitKey} per inviare`;
|
||||||
if (submitKey === String(SubmitKey.Enter)) {
|
if (submitKey === String(SubmitKey.Enter)) {
|
||||||
inputHints += ", premi Shift + Enter per andare a capo";
|
inputHints += ",Shift + Enter per andare a capo";
|
||||||
}
|
}
|
||||||
return inputHints;
|
return (
|
||||||
|
inputHints +
|
||||||
|
",/ per attivare il completamento automatico, : per attivare il comando"
|
||||||
|
);
|
||||||
},
|
},
|
||||||
Send: "Invia",
|
Send: "Invia",
|
||||||
Config: {
|
Config: {
|
||||||
Reset: "Reset to Default",
|
Reset: "Pulisci memoria",
|
||||||
SaveAs: "Save as Mask",
|
SaveAs: "Salva come maschera",
|
||||||
},
|
},
|
||||||
|
IsContext: "Suggerimenti predefiniti",
|
||||||
},
|
},
|
||||||
Export: {
|
Export: {
|
||||||
Title: "Tutti i messaggi",
|
Title: "Condividi cronologia chat",
|
||||||
Copy: "Copia tutto",
|
Copy: "Copia tutto",
|
||||||
Download: "Scarica",
|
Download: "Scarica file",
|
||||||
MessageFromYou: "Messaggio da te",
|
Share: "Condividi su ShareGPT",
|
||||||
MessageFromChatGPT: "Messaggio da ChatGPT",
|
MessageFromYou: "Utente",
|
||||||
|
MessageFromChatGPT: "ChatGPT",
|
||||||
|
Format: {
|
||||||
|
Title: "Formato di esportazione",
|
||||||
|
SubTitle: "Puoi esportare come testo Markdown o immagine PNG",
|
||||||
|
},
|
||||||
|
IncludeContext: {
|
||||||
|
Title: "Includi contesto maschera",
|
||||||
|
SubTitle: "Mostrare il contesto della maschera nei messaggi",
|
||||||
|
},
|
||||||
|
Steps: {
|
||||||
|
Select: "Seleziona",
|
||||||
|
Preview: "Anteprima",
|
||||||
|
},
|
||||||
|
Image: {
|
||||||
|
Toast: "Generazione dello screenshot in corso",
|
||||||
|
Modal:
|
||||||
|
"Tieni premuto o fai clic con il tasto destro per salvare l'immagine",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Select: {
|
||||||
|
Search: "Cerca messaggi",
|
||||||
|
All: "Seleziona tutto",
|
||||||
|
Latest: "Ultimi messaggi",
|
||||||
|
Clear: "Pulisci selezione",
|
||||||
},
|
},
|
||||||
Memory: {
|
Memory: {
|
||||||
Title: "Prompt di memoria",
|
Title: "Riassunto storico",
|
||||||
EmptyContent: "Vuoto.",
|
EmptyContent:
|
||||||
Copy: "Copia tutto",
|
"Il contenuto della conversazione è troppo breve, nessun riassunto necessario",
|
||||||
Send: "Send Memory",
|
Send: "Comprimi automaticamente la cronologia chat e inviala come contesto",
|
||||||
Reset: "Reset Session",
|
Copy: "Copia riassunto",
|
||||||
ResetConfirm:
|
Reset: "[unused]",
|
||||||
"Ripristinare cancellerà la conversazione corrente e la cronologia di memoria. Sei sicuro che vuoi riavviare?",
|
ResetConfirm: "Confermi la cancellazione del riassunto storico?",
|
||||||
},
|
},
|
||||||
Home: {
|
Home: {
|
||||||
NewChat: "Nuova Chat",
|
NewChat: "Nuova chat",
|
||||||
DeleteChat: "Confermare la cancellazione della conversazione selezionata?",
|
DeleteChat: "Confermi l'eliminazione della conversazione selezionata?",
|
||||||
DeleteToast: "Chat Cancellata",
|
DeleteToast: "Conversazione eliminata",
|
||||||
Revert: "Revert",
|
Revert: "Annulla",
|
||||||
},
|
},
|
||||||
Settings: {
|
Settings: {
|
||||||
Title: "Impostazioni",
|
Title: "Impostazioni",
|
||||||
SubTitle: "Tutte le impostazioni",
|
SubTitle: "Tutte le opzioni di impostazione",
|
||||||
|
|
||||||
|
Danger: {
|
||||||
|
Reset: {
|
||||||
|
Title: "Ripristina tutte le impostazioni",
|
||||||
|
SubTitle: "Ripristina tutte le opzioni ai valori predefiniti",
|
||||||
|
Action: "Ripristina subito",
|
||||||
|
Confirm: "Confermi il ripristino di tutte le impostazioni?",
|
||||||
|
},
|
||||||
|
Clear: {
|
||||||
|
Title: "Elimina tutti i dati",
|
||||||
|
SubTitle: "Elimina tutte le chat e i dati delle impostazioni",
|
||||||
|
Action: "Elimina subito",
|
||||||
|
Confirm:
|
||||||
|
"Confermi l'eliminazione di tutte le chat e dei dati delle impostazioni?",
|
||||||
|
},
|
||||||
|
},
|
||||||
Lang: {
|
Lang: {
|
||||||
Name: "Language", // ATTENTION: if you wanna add a new translation, please do not translate this value, leave it as `Language`
|
Name: "Language", // ATTENZIONE: se vuoi aggiungere una nuova traduzione, non tradurre questo valore, lascialo come `Language`
|
||||||
All: "Tutte le lingue",
|
All: "Tutte le lingue",
|
||||||
},
|
},
|
||||||
Avatar: "Avatar",
|
Avatar: "Avatar",
|
||||||
FontSize: {
|
FontSize: {
|
||||||
Title: "Dimensione carattere",
|
Title: "Dimensione del carattere",
|
||||||
SubTitle: "Regolare la dimensione dei caratteri del contenuto della chat",
|
SubTitle: "Dimensione del carattere per il contenuto della chat",
|
||||||
},
|
},
|
||||||
FontFamily: {
|
FontFamily: {
|
||||||
Title: "Font della Chat",
|
Title: "Font della Chat",
|
||||||
|
@ -78,167 +179,428 @@ const it: PartialLocaleType = {
|
||||||
Placeholder: "Nome del Font",
|
Placeholder: "Nome del Font",
|
||||||
},
|
},
|
||||||
InjectSystemPrompts: {
|
InjectSystemPrompts: {
|
||||||
Title: "Inserisci Prompts di Sistema",
|
Title: "Inserisci suggerimenti di sistema",
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Aggiungi forzatamente un prompt di sistema simulato di ChatGPT all'inizio della lista dei messaggi per ogni richiesta",
|
"Aggiungi forzatamente un suggerimento di sistema simulato di ChatGPT all'inizio della lista dei messaggi per ogni richiesta",
|
||||||
},
|
},
|
||||||
|
InputTemplate: {
|
||||||
|
Title: "Preprocessing dell'input utente",
|
||||||
|
SubTitle:
|
||||||
|
"L'ultimo messaggio dell'utente verrà inserito in questo modello",
|
||||||
|
},
|
||||||
|
|
||||||
Update: {
|
Update: {
|
||||||
Version: (x: string) => `Versione: ${x}`,
|
Version: (x: string) => `Versione attuale: ${x}`,
|
||||||
IsLatest: "Ultima versione",
|
IsLatest: "È l'ultima versione",
|
||||||
CheckUpdate: "Controlla aggiornamenti",
|
CheckUpdate: "Controlla aggiornamenti",
|
||||||
IsChecking: "Sto controllando gli aggiornamenti...",
|
IsChecking: "Verifica aggiornamenti in corso...",
|
||||||
FoundUpdate: (x: string) => `Trovata nuova versione: ${x}`,
|
FoundUpdate: (x: string) => `Nuova versione trovata: ${x}`,
|
||||||
GoToUpdate: "Aggiorna",
|
GoToUpdate: "Vai all'aggiornamento",
|
||||||
},
|
},
|
||||||
SendKey: "Tasto invia",
|
SendKey: "Tasto di invio",
|
||||||
Theme: "Tema",
|
Theme: "Tema",
|
||||||
TightBorder: "Schermo intero",
|
TightBorder: "Modalità senza bordi",
|
||||||
SendPreviewBubble: {
|
SendPreviewBubble: {
|
||||||
Title: "Anteprima di digitazione",
|
Title: "Bolla di anteprima",
|
||||||
SubTitle: "Preview markdown in bubble",
|
SubTitle: "Anteprima del contenuto Markdown nella bolla di anteprima",
|
||||||
|
},
|
||||||
|
AutoGenerateTitle: {
|
||||||
|
Title: "Generazione automatica del titolo",
|
||||||
|
SubTitle:
|
||||||
|
"Genera un titolo appropriato in base al contenuto della conversazione",
|
||||||
|
},
|
||||||
|
Sync: {
|
||||||
|
CloudState: "Dati cloud",
|
||||||
|
NotSyncYet: "Non è ancora avvenuta alcuna sincronizzazione",
|
||||||
|
Success: "Sincronizzazione riuscita",
|
||||||
|
Fail: "Sincronizzazione fallita",
|
||||||
|
|
||||||
|
Config: {
|
||||||
|
Modal: {
|
||||||
|
Title: "Configura sincronizzazione cloud",
|
||||||
|
Check: "Controlla disponibilità",
|
||||||
|
},
|
||||||
|
SyncType: {
|
||||||
|
Title: "Tipo di sincronizzazione",
|
||||||
|
SubTitle: "Scegli il server di sincronizzazione preferito",
|
||||||
|
},
|
||||||
|
Proxy: {
|
||||||
|
Title: "Abilita proxy",
|
||||||
|
SubTitle:
|
||||||
|
"Durante la sincronizzazione nel browser, è necessario abilitare il proxy per evitare restrizioni CORS",
|
||||||
|
},
|
||||||
|
ProxyUrl: {
|
||||||
|
Title: "Indirizzo proxy",
|
||||||
|
SubTitle: "Solo per il proxy CORS fornito con questo progetto",
|
||||||
|
},
|
||||||
|
|
||||||
|
WebDav: {
|
||||||
|
Endpoint: "Indirizzo WebDAV",
|
||||||
|
UserName: "Nome utente",
|
||||||
|
Password: "Password",
|
||||||
|
},
|
||||||
|
|
||||||
|
UpStash: {
|
||||||
|
Endpoint: "URL REST di UpStash Redis",
|
||||||
|
UserName: "Nome di backup",
|
||||||
|
Password: "Token REST di UpStash Redis",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
LocalState: "Dati locali",
|
||||||
|
Overview: (overview: any) => {
|
||||||
|
return `${overview.chat} chat, ${overview.message} messaggi, ${overview.prompt} suggerimenti, ${overview.mask} maschere`;
|
||||||
|
},
|
||||||
|
ImportFailed: "Importazione fallita",
|
||||||
},
|
},
|
||||||
Mask: {
|
Mask: {
|
||||||
Splash: {
|
Splash: {
|
||||||
Title: "Mask Splash Screen",
|
Title: "Pagina di avvio delle maschere",
|
||||||
SubTitle: "Show a mask splash screen before starting new chat",
|
SubTitle:
|
||||||
|
"Mostra la pagina di avvio delle maschere quando si avvia una nuova chat",
|
||||||
|
},
|
||||||
|
Builtin: {
|
||||||
|
Title: "Nascondi maschere predefinite",
|
||||||
|
SubTitle:
|
||||||
|
"Nascondi le maschere predefinite in tutte le liste delle maschere",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Prompt: {
|
Prompt: {
|
||||||
Disable: {
|
Disable: {
|
||||||
Title: "Disabilita l'auto completamento",
|
Title: "Disabilita completamento automatico dei suggerimenti",
|
||||||
SubTitle: "Input / per attivare il completamento automatico",
|
SubTitle:
|
||||||
|
"Inserisci / all'inizio della casella di input per attivare il completamento automatico",
|
||||||
},
|
},
|
||||||
List: "Elenco dei suggerimenti",
|
List: "Elenco dei suggerimenti personalizzati",
|
||||||
ListCount: (builtin: number, custom: number) =>
|
ListCount: (builtin: number, custom: number) =>
|
||||||
`${builtin} built-in, ${custom} user-defined`,
|
`${builtin} predefiniti, ${custom} definiti dall'utente`,
|
||||||
Edit: "Modifica",
|
Edit: "Modifica",
|
||||||
Modal: {
|
Modal: {
|
||||||
Title: "Prompt List",
|
Title: "Elenco dei suggerimenti",
|
||||||
Add: "Add One",
|
Add: "Nuovo",
|
||||||
Search: "Search Prompts",
|
Search: "Cerca suggerimenti",
|
||||||
},
|
},
|
||||||
EditModal: {
|
EditModal: {
|
||||||
Title: "Edit Prompt",
|
Title: "Modifica suggerimenti",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
HistoryCount: {
|
HistoryCount: {
|
||||||
Title: "Conteggio dei messaggi allegati",
|
Title: "Numero di messaggi storici inclusi",
|
||||||
SubTitle: "Numero di messaggi inviati allegati per richiesta",
|
SubTitle: "Numero di messaggi storici inclusi in ogni richiesta",
|
||||||
},
|
},
|
||||||
CompressThreshold: {
|
CompressThreshold: {
|
||||||
Title: "Soglia di compressione della cronologia",
|
Title: "Soglia di compressione dei messaggi storici",
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Comprimerà se la lunghezza dei messaggi non compressi supera il valore",
|
"Quando i messaggi storici non compressi superano questo valore, verranno compressi",
|
||||||
},
|
},
|
||||||
|
|
||||||
Usage: {
|
Usage: {
|
||||||
Title: "Bilancio Account",
|
Title: "Verifica saldo",
|
||||||
SubTitle(used: any, total: any) {
|
SubTitle(used: any, total: any) {
|
||||||
return `Attualmente usato in questo mese $${used}, soglia massima $${total}`;
|
return `Utilizzato questo mese $${used}, totale abbonamento $${total}`;
|
||||||
},
|
},
|
||||||
IsChecking: "Controllando...",
|
IsChecking: "Verifica in corso…",
|
||||||
Check: "Controlla ancora",
|
Check: "Verifica di nuovo",
|
||||||
NoAccess: "Inserire la chiave API per controllare il saldo",
|
NoAccess:
|
||||||
|
"Inserisci API Key o password di accesso per visualizzare il saldo",
|
||||||
},
|
},
|
||||||
|
|
||||||
Model: "Modello GPT",
|
Access: {
|
||||||
|
SaasStart: {
|
||||||
|
Title: "Usa NextChat AI",
|
||||||
|
Label: "(La soluzione più conveniente)",
|
||||||
|
SubTitle:
|
||||||
|
"Mantenuto ufficialmente da NextChat, pronto all'uso senza configurazione, supporta i modelli più recenti come OpenAI o1, GPT-4o e Claude-3.5",
|
||||||
|
ChatNow: "Chatta ora",
|
||||||
|
},
|
||||||
|
|
||||||
|
AccessCode: {
|
||||||
|
Title: "Password di accesso",
|
||||||
|
SubTitle: "L'amministratore ha abilitato l'accesso criptato",
|
||||||
|
Placeholder: "Inserisci la password di accesso",
|
||||||
|
},
|
||||||
|
CustomEndpoint: {
|
||||||
|
Title: "Interfaccia personalizzata",
|
||||||
|
SubTitle: "Utilizzare servizi Azure o OpenAI personalizzati",
|
||||||
|
},
|
||||||
|
Provider: {
|
||||||
|
Title: "Fornitore del modello",
|
||||||
|
SubTitle: "Cambia fornitore di servizi",
|
||||||
|
},
|
||||||
|
OpenAI: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "API Key",
|
||||||
|
SubTitle:
|
||||||
|
"Utilizza una chiave OpenAI personalizzata per bypassare le limitazioni di accesso",
|
||||||
|
Placeholder: "API Key OpenAI",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Indirizzo dell'interfaccia",
|
||||||
|
SubTitle: "Deve includere http(s):// oltre all'indirizzo predefinito",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Azure: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "Chiave dell'interfaccia",
|
||||||
|
SubTitle:
|
||||||
|
"Utilizza una chiave Azure personalizzata per bypassare le limitazioni di accesso",
|
||||||
|
Placeholder: "Chiave API Azure",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Indirizzo dell'interfaccia",
|
||||||
|
SubTitle: "Esempio:",
|
||||||
|
},
|
||||||
|
|
||||||
|
ApiVerion: {
|
||||||
|
Title: "Versione dell'interfaccia (versione api azure)",
|
||||||
|
SubTitle: "Scegli una versione specifica",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Anthropic: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "Chiave dell'interfaccia",
|
||||||
|
SubTitle:
|
||||||
|
"Utilizza una chiave Anthropic personalizzata per bypassare le limitazioni di accesso",
|
||||||
|
Placeholder: "API Key Anthropic",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Indirizzo dell'interfaccia",
|
||||||
|
SubTitle: "Esempio:",
|
||||||
|
},
|
||||||
|
|
||||||
|
ApiVerion: {
|
||||||
|
Title: "Versione dell'interfaccia (versione api claude)",
|
||||||
|
SubTitle: "Scegli una versione API specifica",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Google: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "API Key",
|
||||||
|
SubTitle: "Ottieni la tua chiave API da Google AI",
|
||||||
|
Placeholder: "Inserisci la tua chiave API Google AI Studio",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Indirizzo dell'interfaccia",
|
||||||
|
SubTitle: "Esempio:",
|
||||||
|
},
|
||||||
|
|
||||||
|
ApiVersion: {
|
||||||
|
Title: "Versione API (solo per gemini-pro)",
|
||||||
|
SubTitle: "Scegli una versione API specifica",
|
||||||
|
},
|
||||||
|
GoogleSafetySettings: {
|
||||||
|
Title: "Livello di filtraggio sicurezza Google",
|
||||||
|
SubTitle: "Imposta il livello di filtraggio dei contenuti",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Baidu: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "API Key",
|
||||||
|
SubTitle: "Utilizza una chiave API Baidu personalizzata",
|
||||||
|
Placeholder: "API Key Baidu",
|
||||||
|
},
|
||||||
|
SecretKey: {
|
||||||
|
Title: "Secret Key",
|
||||||
|
SubTitle: "Utilizza una chiave segreta Baidu personalizzata",
|
||||||
|
Placeholder: "Secret Key Baidu",
|
||||||
|
},
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Indirizzo dell'interfaccia",
|
||||||
|
SubTitle:
|
||||||
|
"Non supporta configurazioni personalizzate, andare su .env",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ByteDance: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "Chiave dell'interfaccia",
|
||||||
|
SubTitle: "Utilizza una chiave API ByteDance personalizzata",
|
||||||
|
Placeholder: "API Key ByteDance",
|
||||||
|
},
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Indirizzo dell'interfaccia",
|
||||||
|
SubTitle: "Esempio:",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Alibaba: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "Chiave dell'interfaccia",
|
||||||
|
SubTitle: "Utilizza una chiave API Alibaba Cloud personalizzata",
|
||||||
|
Placeholder: "API Key Alibaba Cloud",
|
||||||
|
},
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Indirizzo dell'interfaccia",
|
||||||
|
SubTitle: "Esempio:",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CustomModel: {
|
||||||
|
Title: "Nome del modello personalizzato",
|
||||||
|
SubTitle:
|
||||||
|
"Aggiungi opzioni di modelli personalizzati, separati da virgole",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
Model: "Modello (model)",
|
||||||
|
CompressModel: {
|
||||||
|
Title: "Modello di compressione",
|
||||||
|
SubTitle: "Modello utilizzato per comprimere la cronologia",
|
||||||
|
},
|
||||||
Temperature: {
|
Temperature: {
|
||||||
Title: "Temperature",
|
Title: "Casualità (temperature)",
|
||||||
SubTitle: "Un valore maggiore rende l'output più casuale",
|
SubTitle: "Valore più alto, risposte più casuali",
|
||||||
|
},
|
||||||
|
TopP: {
|
||||||
|
Title: "Campionamento nucleare (top_p)",
|
||||||
|
SubTitle:
|
||||||
|
"Simile alla casualità, ma non cambiarlo insieme alla casualità",
|
||||||
},
|
},
|
||||||
MaxTokens: {
|
MaxTokens: {
|
||||||
Title: "Token massimi",
|
Title: "Limite di token per risposta (max_tokens)",
|
||||||
SubTitle: "Lunghezza massima dei token in ingresso e dei token generati",
|
SubTitle: "Numero massimo di token per ogni interazione",
|
||||||
},
|
},
|
||||||
PresencePenalty: {
|
PresencePenalty: {
|
||||||
Title: "Penalità di presenza",
|
Title: "Novità del tema (presence_penalty)",
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Un valore maggiore aumenta la probabilità di parlare di nuovi argomenti",
|
"Valore più alto, maggiore possibilità di espandere a nuovi argomenti",
|
||||||
},
|
},
|
||||||
FrequencyPenalty: {
|
FrequencyPenalty: {
|
||||||
Title: "Penalità di frequenza",
|
Title: "Penalità di frequenza (frequency_penalty)",
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Un valore maggiore che diminuisce la probabilità di ripetere la stessa riga",
|
"Valore più alto, maggiore possibilità di ridurre le ripetizioni",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Store: {
|
Store: {
|
||||||
DefaultTopic: "Nuova conversazione",
|
DefaultTopic: "Nuova chat",
|
||||||
BotHello: "Ciao, come posso aiutarti oggi?",
|
BotHello: "Come posso aiutarti?",
|
||||||
Error: "Qualcosa è andato storto, riprova più tardi.",
|
Error: "Si è verificato un errore, riprova più tardi",
|
||||||
Prompt: {
|
Prompt: {
|
||||||
History: (content: string) =>
|
History: (content: string) =>
|
||||||
"Questo è un riassunto della cronologia delle chat tra l'IA e l'utente:" +
|
"Questo è un riassunto della chat storica come contesto: " + content,
|
||||||
content,
|
|
||||||
Topic:
|
Topic:
|
||||||
"Si prega di generare un titolo di quattro o cinque parole che riassuma la nostra conversazione senza alcuna traccia, punteggiatura, virgolette, punti, simboli o testo aggiuntivo. Rimuovere le virgolette",
|
"Riporta il tema di questa frase in modo conciso con quattro o cinque parole, senza spiegazioni, punteggiatura, interiezioni, testo superfluo e senza grassetto. Se non c'è un tema, rispondi direttamente con 'chit-chat'",
|
||||||
Summarize:
|
Summarize:
|
||||||
"Riassumi brevemente la nostra discussione in 200 caratteri o meno per usarla come spunto per una futura conversazione.",
|
"Riassumi brevemente il contenuto della conversazione come prompt di contesto per il seguito, mantenendolo entro 200 parole",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Copy: {
|
Copy: {
|
||||||
Success: "Copiato sugli appunti",
|
Success: "Copiato negli appunti",
|
||||||
Failed:
|
Failed: "Copia fallita, concedi i permessi per gli appunti",
|
||||||
"Copia fallita, concedere l'autorizzazione all'accesso agli appunti",
|
},
|
||||||
|
Download: {
|
||||||
|
Success: "Contenuto scaricato nella tua directory.",
|
||||||
|
Failed: "Download fallito.",
|
||||||
},
|
},
|
||||||
Context: {
|
Context: {
|
||||||
Toast: (x: any) => `Con ${x} prompts contestuali`,
|
Toast: (x: any) => `Include ${x} suggerimenti predefiniti`,
|
||||||
Edit: "Prompt contestuali e di memoria",
|
Edit: "Impostazioni della conversazione attuale",
|
||||||
Add: "Aggiungi altro",
|
Add: "Aggiungi una conversazione",
|
||||||
|
Clear: "Contesto cancellato",
|
||||||
|
Revert: "Ripristina contesto",
|
||||||
},
|
},
|
||||||
Plugin: {
|
Plugin: {
|
||||||
Name: "Plugin",
|
Name: "Plugin",
|
||||||
},
|
},
|
||||||
FineTuned: {
|
FineTuned: {
|
||||||
Sysmessage: "Sei un assistente che",
|
Sysmessage: "Sei un assistente",
|
||||||
},
|
},
|
||||||
Mask: {
|
SearchChat: {
|
||||||
Name: "Mask",
|
Name: "Cerca",
|
||||||
Page: {
|
Page: {
|
||||||
Title: "Prompt Template",
|
Title: "Cerca nei messaggi",
|
||||||
SubTitle: (count: number) => `${count} prompt templates`,
|
Search: "Inserisci parole chiave per la ricerca",
|
||||||
Search: "Search Templates",
|
NoResult: "Nessun risultato trovato",
|
||||||
Create: "Create",
|
NoData: "Nessun dato",
|
||||||
|
Loading: "Caricamento in corso",
|
||||||
|
|
||||||
|
SubTitle: (count: number) => `Trovati ${count} risultati`,
|
||||||
},
|
},
|
||||||
Item: {
|
Item: {
|
||||||
Info: (count: number) => `${count} prompts`,
|
View: "Visualizza",
|
||||||
Chat: "Chat",
|
},
|
||||||
View: "View",
|
},
|
||||||
Edit: "Edit",
|
Mask: {
|
||||||
Delete: "Delete",
|
Name: "Maschera",
|
||||||
DeleteConfirm: "Confirm to delete?",
|
Page: {
|
||||||
|
Title: "Maschere dei ruoli predefiniti",
|
||||||
|
SubTitle: (count: number) => `${count} definizioni di ruoli predefiniti`,
|
||||||
|
Search: "Cerca maschere di ruolo",
|
||||||
|
Create: "Crea nuovo",
|
||||||
|
},
|
||||||
|
Item: {
|
||||||
|
Info: (count: number) => `Include ${count} conversazioni predefinite`,
|
||||||
|
Chat: "Conversazione",
|
||||||
|
View: "Visualizza",
|
||||||
|
Edit: "Modifica",
|
||||||
|
Delete: "Elimina",
|
||||||
|
DeleteConfirm: "Confermi eliminazione?",
|
||||||
},
|
},
|
||||||
EditModal: {
|
EditModal: {
|
||||||
Title: (readonly: boolean) =>
|
Title: (readonly: boolean) =>
|
||||||
`Edit Prompt Template ${readonly ? "(readonly)" : ""}`,
|
`Modifica maschera predefinita ${readonly ? "(sola lettura)" : ""}`,
|
||||||
Download: "Download",
|
Download: "Scarica predefinito",
|
||||||
Clone: "Clone",
|
Clone: "Clona predefinito",
|
||||||
},
|
},
|
||||||
Config: {
|
Config: {
|
||||||
Avatar: "Bot Avatar",
|
Avatar: "Avatar del ruolo",
|
||||||
Name: "Bot Name",
|
Name: "Nome del ruolo",
|
||||||
|
Sync: {
|
||||||
|
Title: "Utilizza impostazioni globali",
|
||||||
|
SubTitle:
|
||||||
|
"La conversazione attuale utilizzerà le impostazioni globali del modello",
|
||||||
|
Confirm:
|
||||||
|
"Le impostazioni personalizzate della conversazione attuale verranno sovrascritte automaticamente, confermi l'attivazione delle impostazioni globali?",
|
||||||
|
},
|
||||||
|
HideContext: {
|
||||||
|
Title: "Nascondi conversazioni predefinite",
|
||||||
|
SubTitle:
|
||||||
|
"Le conversazioni predefinite non appariranno nella finestra della chat dopo averle nascoste",
|
||||||
|
},
|
||||||
|
Share: {
|
||||||
|
Title: "Condividi questa maschera",
|
||||||
|
SubTitle: "Genera un link diretto a questa maschera",
|
||||||
|
Action: "Copia link",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
NewChat: {
|
NewChat: {
|
||||||
Return: "Return",
|
Return: "Torna",
|
||||||
Skip: "Skip",
|
Skip: "Inizia subito",
|
||||||
Title: "Pick a Mask",
|
NotShow: "Non mostrare più",
|
||||||
SubTitle: "Chat with the Soul behind the Mask",
|
ConfirmNoShow:
|
||||||
More: "Find More",
|
"Confermi di disabilitare? Dopo la disabilitazione, puoi riattivare in qualsiasi momento dalle impostazioni.",
|
||||||
NotShow: "Not Show Again",
|
Title: "Scegli una maschera",
|
||||||
ConfirmNoShow: "Confirm to disable?You can enable it in settings later.",
|
SubTitle: "Inizia ora e interagisci con il pensiero dietro la maschera",
|
||||||
|
More: "Vedi tutto",
|
||||||
|
},
|
||||||
|
|
||||||
|
URLCommand: {
|
||||||
|
Code: "Codice di accesso rilevato nel link, riempirlo automaticamente?",
|
||||||
|
Settings:
|
||||||
|
"Impostazioni predefinite rilevate nel link, riempirle automaticamente?",
|
||||||
},
|
},
|
||||||
|
|
||||||
UI: {
|
UI: {
|
||||||
Confirm: "Confirm",
|
Confirm: "Conferma",
|
||||||
Cancel: "Cancel",
|
Cancel: "Annulla",
|
||||||
Close: "Close",
|
Close: "Chiudi",
|
||||||
Create: "Create",
|
Create: "Crea",
|
||||||
Edit: "Edit",
|
Edit: "Modifica",
|
||||||
|
Export: "Esporta",
|
||||||
|
Import: "Importa",
|
||||||
|
Sync: "Sincronizza",
|
||||||
|
Config: "Configura",
|
||||||
},
|
},
|
||||||
Exporter: {
|
Exporter: {
|
||||||
|
Description: {
|
||||||
|
Title:
|
||||||
|
"Solo i messaggi dopo la cancellazione del contesto verranno visualizzati",
|
||||||
|
},
|
||||||
Model: "Modello",
|
Model: "Modello",
|
||||||
Messages: "Messaggi",
|
Messages: "Messaggi",
|
||||||
Topic: "Argomento",
|
Topic: "Tema",
|
||||||
Time: "Tempo",
|
Time: "Tempo",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,117 +1,164 @@
|
||||||
import { SubmitKey } from "../store/config";
|
import { SubmitKey } from "../store/config";
|
||||||
import type { PartialLocaleType } from "./index";
|
import type { PartialLocaleType } from "./index";
|
||||||
|
import { getClientConfig } from "../config/client";
|
||||||
|
import { SAAS_CHAT_UTM_URL } from "@/app/constant";
|
||||||
|
const isApp = !!getClientConfig()?.isApp;
|
||||||
|
|
||||||
const jp: PartialLocaleType = {
|
const jp: PartialLocaleType = {
|
||||||
WIP: "この機能は開発中です",
|
WIP: "この機能は開発中です",
|
||||||
Error: {
|
Error: {
|
||||||
Unauthorized:
|
Unauthorized: isApp
|
||||||
"現在は未承認状態です。左下の設定ボタンをクリックし、アクセスパスワードかOpenAIのAPIキーを入力してください。",
|
? `😆 会話中に問題が発生しましたが、心配しないでください:
|
||||||
|
\\ 1️⃣ 設定なしで始めたい場合は、[ここをクリックしてすぐにチャットを開始 🚀](${SAAS_CHAT_UTM_URL})
|
||||||
|
\\ 2️⃣ 自分のOpenAIリソースを使用したい場合は、[ここをクリックして](/#/settings)設定を変更してください ⚙️`
|
||||||
|
: `😆 会話中に問題が発生しましたが、心配しないでください:
|
||||||
|
\ 1️⃣ 設定なしで始めたい場合は、[ここをクリックしてすぐにチャットを開始 🚀](${SAAS_CHAT_UTM_URL})
|
||||||
|
\ 2️⃣ プライベートデプロイ版を使用している場合は、[ここをクリックして](/#/auth)アクセストークンを入力してください 🔑
|
||||||
|
\ 3️⃣ 自分のOpenAIリソースを使用したい場合は、[ここをクリックして](/#/settings)設定を変更してください ⚙️
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
Auth: {
|
||||||
|
Title: "パスワードが必要です",
|
||||||
|
Tips: "管理者がパスワード認証を有効にしました。以下にアクセスコードを入力してください",
|
||||||
|
SubTips: "または、OpenAIまたはGoogle APIキーを入力してください",
|
||||||
|
Input: "ここにアクセスコードを入力",
|
||||||
|
Confirm: "確認",
|
||||||
|
Later: "後で",
|
||||||
|
Return: "戻る",
|
||||||
|
SaasTips: "設定が面倒すぎる、すぐに使いたい",
|
||||||
|
TopTips:
|
||||||
|
"🥳 NextChat AIの発売特典で、OpenAI o1、GPT-4o、Claude-3.5などの最新の大規模モデルを今すぐアンロック",
|
||||||
},
|
},
|
||||||
ChatItem: {
|
ChatItem: {
|
||||||
ChatItemCount: (count: number) => `${count} 通のチャット`,
|
ChatItemCount: (count: number) => `${count}件の会話`,
|
||||||
},
|
},
|
||||||
Chat: {
|
Chat: {
|
||||||
SubTitle: (count: number) => `ChatGPTとの ${count} 通のチャット`,
|
SubTitle: (count: number) => `合計${count}件の会話`,
|
||||||
EditMessage: {
|
EditMessage: {
|
||||||
Title: "全てのメッセージを修正",
|
Title: "メッセージ履歴を編集",
|
||||||
Topic: {
|
Topic: {
|
||||||
Title: "トピック",
|
Title: "チャットテーマ",
|
||||||
SubTitle: "このトピックを変える",
|
SubTitle: "現在のチャットテーマを変更",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Actions: {
|
Actions: {
|
||||||
ChatList: "メッセージリストを表示",
|
ChatList: "メッセージリストを見る",
|
||||||
CompressedHistory: "圧縮された履歴プロンプトを表示",
|
CompressedHistory: "圧縮された履歴プロンプトを見る",
|
||||||
Export: "チャット履歴をエクスポート",
|
Export: "チャット履歴をエクスポート",
|
||||||
Copy: "コピー",
|
Copy: "コピー",
|
||||||
Stop: "停止",
|
Stop: "停止",
|
||||||
Retry: "リトライ",
|
Retry: "再試行",
|
||||||
Pin: "ピン",
|
Pin: "固定",
|
||||||
PinToastContent:
|
PinToastContent: "1件の会話をプリセットプロンプトに固定しました",
|
||||||
"コンテキストプロンプトに1つのメッセージをピン留めしました",
|
PinToastAction: "見る",
|
||||||
PinToastAction: "表示",
|
|
||||||
Delete: "削除",
|
Delete: "削除",
|
||||||
Edit: "編集",
|
Edit: "編集",
|
||||||
|
RefreshTitle: "タイトルを更新",
|
||||||
|
RefreshToast: "タイトル更新リクエストが送信されました",
|
||||||
|
},
|
||||||
|
Commands: {
|
||||||
|
new: "新しいチャット",
|
||||||
|
newm: "マスクから新しいチャット",
|
||||||
|
next: "次のチャット",
|
||||||
|
prev: "前のチャット",
|
||||||
|
clear: "コンテキストをクリア",
|
||||||
|
del: "チャットを削除",
|
||||||
|
},
|
||||||
|
InputActions: {
|
||||||
|
Stop: "応答を停止",
|
||||||
|
ToBottom: "最新へスクロール",
|
||||||
|
Theme: {
|
||||||
|
auto: "自動テーマ",
|
||||||
|
light: "ライトモード",
|
||||||
|
dark: "ダークモード",
|
||||||
|
},
|
||||||
|
Prompt: "クイックコマンド",
|
||||||
|
Masks: "すべてのマスク",
|
||||||
|
Clear: "チャットをクリア",
|
||||||
|
Settings: "チャット設定",
|
||||||
|
UploadImage: "画像をアップロード",
|
||||||
},
|
},
|
||||||
Rename: "チャットの名前を変更",
|
Rename: "チャットの名前を変更",
|
||||||
Typing: "入力中…",
|
Typing: "入力中…",
|
||||||
Input: (submitKey: string) => {
|
Input: (submitKey: string) => {
|
||||||
var inputHints = `${submitKey} で送信`;
|
var inputHints = `${submitKey}で送信`;
|
||||||
if (submitKey === String(SubmitKey.Enter)) {
|
if (submitKey === String(SubmitKey.Enter)) {
|
||||||
inputHints += ",Shift + Enter で改行";
|
inputHints += "、Shift + Enterで改行";
|
||||||
}
|
}
|
||||||
return inputHints + ",/ で自動補完をトリガー";
|
return inputHints + "、/で補完をトリガー、:でコマンドをトリガー";
|
||||||
},
|
},
|
||||||
Send: "送信",
|
Send: "送信",
|
||||||
Config: {
|
Config: {
|
||||||
Reset: "リセット",
|
Reset: "メモリをクリア",
|
||||||
SaveAs: "保存",
|
SaveAs: "マスクとして保存",
|
||||||
},
|
},
|
||||||
|
IsContext: "プリセットプロンプト",
|
||||||
},
|
},
|
||||||
Export: {
|
Export: {
|
||||||
Title: "チャット履歴をMarkdown形式でエクスポート",
|
Title: "チャット履歴を共有",
|
||||||
Copy: "すべてコピー",
|
Copy: "すべてコピー",
|
||||||
Download: "ファイルをダウンロード",
|
Download: "ファイルをダウンロード",
|
||||||
MessageFromYou: "あなたからのメッセージ",
|
Share: "ShareGPTに共有",
|
||||||
MessageFromChatGPT: "ChatGPTからのメッセージ",
|
MessageFromYou: "ユーザー",
|
||||||
|
MessageFromChatGPT: "ChatGPT",
|
||||||
Format: {
|
Format: {
|
||||||
Title: "フォーマットをエクスポート",
|
Title: "エクスポート形式",
|
||||||
SubTitle: "マークダウン形式、PNG画像形式を選択できます。",
|
SubTitle: "MarkdownテキストまたはPNG画像としてエクスポートできます",
|
||||||
},
|
},
|
||||||
IncludeContext: {
|
IncludeContext: {
|
||||||
Title: "コンテキストを含みますか?",
|
Title: "マスクコンテキストを含む",
|
||||||
SubTitle: "コンテキストを含ませるか否か",
|
SubTitle: "メッセージにマスクコンテキストを表示するかどうか",
|
||||||
},
|
},
|
||||||
Steps: {
|
Steps: {
|
||||||
Select: "エクスポート設定",
|
Select: "選択",
|
||||||
Preview: "プレビュー",
|
Preview: "プレビュー",
|
||||||
},
|
},
|
||||||
Image: {
|
Image: {
|
||||||
Toast: "画像生成中...",
|
Toast: "スクリーンショットを生成中",
|
||||||
Modal: "長押し、または右クリックで保存してください。",
|
Modal: "長押しまたは右クリックして画像を保存",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Select: {
|
Select: {
|
||||||
Search: "検索",
|
Search: "メッセージを検索",
|
||||||
All: "すべて選択",
|
All: "すべて選択",
|
||||||
Latest: "新しいメッセージを選択",
|
Latest: "最新の数件",
|
||||||
Clear: "クリア",
|
Clear: "選択をクリア",
|
||||||
},
|
},
|
||||||
Memory: {
|
Memory: {
|
||||||
Title: "履歴メモリ",
|
Title: "履歴の要約",
|
||||||
EmptyContent: "まだ記憶されていません",
|
EmptyContent: "対話内容が短いため、要約は不要です",
|
||||||
Send: "メモリを送信",
|
Send: "チャット履歴を自動的に圧縮し、コンテキストとして送信",
|
||||||
Copy: "メモリをコピー",
|
Copy: "要約をコピー",
|
||||||
Reset: "チャットをリセット",
|
Reset: "[unused]",
|
||||||
ResetConfirm:
|
ResetConfirm: "履歴の要約をリセットしてもよろしいですか?",
|
||||||
"リセット後、現在のチャット履歴と過去のメモリがクリアされます。リセットしてもよろしいですか?",
|
|
||||||
},
|
},
|
||||||
Home: {
|
Home: {
|
||||||
NewChat: "新しいチャット",
|
NewChat: "新しいチャット",
|
||||||
DeleteChat: "選択したチャットを削除してもよろしいですか?",
|
DeleteChat: "選択した会話を削除してもよろしいですか?",
|
||||||
DeleteToast: "チャットが削除されました",
|
DeleteToast: "会話を削除しました",
|
||||||
Revert: "元に戻す",
|
Revert: "元に戻す",
|
||||||
},
|
},
|
||||||
Settings: {
|
Settings: {
|
||||||
Title: "設定",
|
Title: "設定",
|
||||||
SubTitle: "設定オプション",
|
SubTitle: "すべての設定オプション",
|
||||||
|
|
||||||
Danger: {
|
Danger: {
|
||||||
Reset: {
|
Reset: {
|
||||||
Title: "設定をリセット",
|
Title: "すべての設定をリセット",
|
||||||
SubTitle: "すべての設定項目をデフォルトにリセットします",
|
SubTitle: "すべての設定項目をデフォルト値にリセット",
|
||||||
Action: "今すぐリセットする",
|
Action: "今すぐリセット",
|
||||||
Confirm: "すべての設定項目をリセットしてもよろしいですか?",
|
Confirm: "すべての設定をリセットしてもよろしいですか?",
|
||||||
},
|
},
|
||||||
Clear: {
|
Clear: {
|
||||||
Title: "データを消去",
|
Title: "すべてのデータをクリア",
|
||||||
SubTitle: "すべてのチャット履歴と設定を消去します",
|
SubTitle: "すべてのチャット、設定データをクリア",
|
||||||
Action: "今すぐ消去する",
|
Action: "今すぐクリア",
|
||||||
Confirm: "すべてのチャット履歴と設定を消去しますか?",
|
Confirm: "すべてのチャット、設定データをクリアしてもよろしいですか?",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Lang: {
|
Lang: {
|
||||||
Name: "Language", // ATTENTION: if you wanna add a new translation, please do not translate this value, leave it as `Language`
|
Name: "Language", // ATTENTION: if you wanna add a new translation, please do not translate this value, leave it as `Language`
|
||||||
All: "全ての言語",
|
All: "すべての言語",
|
||||||
},
|
},
|
||||||
Avatar: "アバター",
|
Avatar: "アバター",
|
||||||
FontSize: {
|
FontSize: {
|
||||||
|
@ -125,183 +172,417 @@ const jp: PartialLocaleType = {
|
||||||
Placeholder: "フォント名",
|
Placeholder: "フォント名",
|
||||||
},
|
},
|
||||||
InjectSystemPrompts: {
|
InjectSystemPrompts: {
|
||||||
Title: "システムプロンプトの挿入",
|
Title: "システムプロンプトの注入",
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"各リクエストのメッセージリストの先頭に、ChatGPTのシステムプロンプトを強制的に追加します",
|
"すべてのリクエストメッセージリストの先頭にChatGPTのシステムプロンプトを強制的に追加",
|
||||||
},
|
},
|
||||||
InputTemplate: {
|
InputTemplate: {
|
||||||
Title: "入力の前処理",
|
Title: "ユーザー入力のプリプロセス",
|
||||||
SubTitle: "新規入力がこのテンプレートに埋め込まれます",
|
SubTitle: "最新のメッセージをこのテンプレートに埋め込む",
|
||||||
},
|
},
|
||||||
|
|
||||||
Update: {
|
Update: {
|
||||||
Version: (x: string) => `現在のバージョン:${x}`,
|
Version: (x: string) => `現在のバージョン:${x}`,
|
||||||
IsLatest: "最新バージョンです",
|
IsLatest: "最新バージョンです",
|
||||||
CheckUpdate: "アップデートを確認",
|
CheckUpdate: "更新を確認",
|
||||||
IsChecking: "アップデートを確認しています...",
|
IsChecking: "更新を確認中...",
|
||||||
FoundUpdate: (x: string) => `新しいバージョンが見つかりました:${x}`,
|
FoundUpdate: (x: string) => `新しいバージョンを発見:${x}`,
|
||||||
GoToUpdate: "更新する",
|
GoToUpdate: "更新へ進む",
|
||||||
},
|
},
|
||||||
SendKey: "送信キー",
|
SendKey: "送信キー",
|
||||||
Theme: "テーマ",
|
Theme: "テーマ",
|
||||||
TightBorder: "ボーダーレスモード",
|
TightBorder: "ボーダーレスモード",
|
||||||
SendPreviewBubble: {
|
SendPreviewBubble: {
|
||||||
Title: "プレビューバブルの送信",
|
Title: "プレビューバブル",
|
||||||
SubTitle: "プレビューバブルでマークダウンコンテンツをプレビュー",
|
SubTitle: "プレビューバブルでMarkdownコンテンツをプレビュー",
|
||||||
|
},
|
||||||
|
AutoGenerateTitle: {
|
||||||
|
Title: "自動タイトル生成",
|
||||||
|
SubTitle: "チャット内容に基づいて適切なタイトルを生成",
|
||||||
|
},
|
||||||
|
Sync: {
|
||||||
|
CloudState: "クラウドデータ",
|
||||||
|
NotSyncYet: "まだ同期されていません",
|
||||||
|
Success: "同期に成功しました",
|
||||||
|
Fail: "同期に失敗しました",
|
||||||
|
|
||||||
|
Config: {
|
||||||
|
Modal: {
|
||||||
|
Title: "クラウド同期の設定",
|
||||||
|
Check: "可用性を確認",
|
||||||
|
},
|
||||||
|
SyncType: {
|
||||||
|
Title: "同期タイプ",
|
||||||
|
SubTitle: "好きな同期サーバーを選択",
|
||||||
|
},
|
||||||
|
Proxy: {
|
||||||
|
Title: "プロキシを有効化",
|
||||||
|
SubTitle:
|
||||||
|
"ブラウザで同期する場合、クロスオリジン制限を避けるためにプロキシを有効にする必要があります",
|
||||||
|
},
|
||||||
|
ProxyUrl: {
|
||||||
|
Title: "プロキシURL",
|
||||||
|
SubTitle: "このプロジェクトに組み込まれたクロスオリジンプロキシ専用",
|
||||||
|
},
|
||||||
|
|
||||||
|
WebDav: {
|
||||||
|
Endpoint: "WebDAV エンドポイント",
|
||||||
|
UserName: "ユーザー名",
|
||||||
|
Password: "パスワード",
|
||||||
|
},
|
||||||
|
|
||||||
|
UpStash: {
|
||||||
|
Endpoint: "UpStash Redis REST URL",
|
||||||
|
UserName: "バックアップ名",
|
||||||
|
Password: "UpStash Redis REST トークン",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
LocalState: "ローカルデータ",
|
||||||
|
Overview: (overview: any) => {
|
||||||
|
return `${overview.chat} 回の対話、${overview.message} 件のメッセージ、${overview.prompt} 件のプロンプト、${overview.mask} 件のマスク`;
|
||||||
|
},
|
||||||
|
ImportFailed: "インポートに失敗しました",
|
||||||
},
|
},
|
||||||
Mask: {
|
Mask: {
|
||||||
Splash: {
|
Splash: {
|
||||||
Title: "キャラクターページ",
|
Title: "マスク起動画面",
|
||||||
SubTitle: "新規チャット作成時にキャラクターページを表示する",
|
SubTitle: "新しいチャットを作成する際にマスク起動画面を表示",
|
||||||
},
|
},
|
||||||
Builtin: {
|
Builtin: {
|
||||||
Title: "ビルトインマスクを非表示",
|
Title: "内蔵マスクを非表示",
|
||||||
SubTitle: "マスクリストからビルトインを非表示する",
|
SubTitle: "すべてのマスクリストで内蔵マスクを非表示",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Prompt: {
|
Prompt: {
|
||||||
Disable: {
|
Disable: {
|
||||||
Title: "プロンプトの自動補完を無効にする",
|
Title: "プロンプトの自動補完を無効化",
|
||||||
SubTitle:
|
SubTitle: "入力フィールドの先頭に / を入力して自動補完をトリガー",
|
||||||
"入力フィールドの先頭に / を入力すると、自動補完がトリガーされます。",
|
|
||||||
},
|
},
|
||||||
List: "カスタムプロンプトリスト",
|
List: "カスタムプロンプトリスト",
|
||||||
ListCount: (builtin: number, custom: number) =>
|
ListCount: (builtin: number, custom: number) =>
|
||||||
`組み込み ${builtin} 件、ユーザー定義 ${custom} 件`,
|
`内蔵 ${builtin} 件、ユーザー定義 ${custom} 件`,
|
||||||
Edit: "編集",
|
Edit: "編集",
|
||||||
Modal: {
|
Modal: {
|
||||||
Title: "プロンプトリスト",
|
Title: "プロンプトリスト",
|
||||||
Add: "新規追加",
|
Add: "新規作成",
|
||||||
Search: "プロンプトワード検索",
|
Search: "プロンプトを検索",
|
||||||
},
|
},
|
||||||
EditModal: {
|
EditModal: {
|
||||||
Title: "編集",
|
Title: "プロンプトを編集",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
HistoryCount: {
|
HistoryCount: {
|
||||||
Title: "履歴メッセージ数を添付",
|
Title: "履歴メッセージ数",
|
||||||
SubTitle: "リクエストごとに添付する履歴メッセージ数",
|
SubTitle: "各リクエストに含まれる履歴メッセージの数",
|
||||||
},
|
},
|
||||||
CompressThreshold: {
|
CompressThreshold: {
|
||||||
Title: "履歴メッセージの長さ圧縮しきい値",
|
Title: "履歴メッセージの圧縮閾値",
|
||||||
SubTitle:
|
SubTitle: "未圧縮の履歴メッセージがこの値を超えた場合、圧縮が行われます",
|
||||||
"圧縮されていない履歴メッセージがこの値を超えた場合、圧縮が行われます。",
|
|
||||||
},
|
},
|
||||||
|
|
||||||
Usage: {
|
Usage: {
|
||||||
Title: "残高照会",
|
Title: "残高確認",
|
||||||
SubTitle(used: any, total: any) {
|
SubTitle(used: any, total: any) {
|
||||||
return `今月は $${used} を使用しました。総額は $${total} です。`;
|
return `今月の使用量 $${used}、サブスクリプション合計 $${total}`;
|
||||||
},
|
},
|
||||||
IsChecking: "確認中...",
|
IsChecking: "確認中…",
|
||||||
Check: "再確認",
|
Check: "再確認",
|
||||||
NoAccess: "APIキーまたはアクセスパスワードを入力して残高を表示",
|
NoAccess: "APIキーまたはアクセスパスワードを入力して残高を確認",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Access: {
|
||||||
|
SaasStart: {
|
||||||
|
Title: "NextChat AIを使用する",
|
||||||
|
Label: "(コストパフォーマンスの最も高いソリューション)",
|
||||||
|
SubTitle:
|
||||||
|
"NextChatによって公式に管理されており、設定なしですぐに使用でき、OpenAI o1、GPT-4o、Claude-3.5などの最新の大規模モデルをサポートしています",
|
||||||
|
ChatNow: "今すぐチャット",
|
||||||
|
},
|
||||||
|
|
||||||
|
AccessCode: {
|
||||||
|
Title: "アクセスパスワード",
|
||||||
|
SubTitle: "管理者が暗号化アクセスを有効にしました",
|
||||||
|
Placeholder: "アクセスパスワードを入力してください",
|
||||||
|
},
|
||||||
|
CustomEndpoint: {
|
||||||
|
Title: "カスタムエンドポイント",
|
||||||
|
SubTitle: "カスタムAzureまたはOpenAIサービスを使用するかどうか",
|
||||||
|
},
|
||||||
|
Provider: {
|
||||||
|
Title: "モデルプロバイダー",
|
||||||
|
SubTitle: "異なるプロバイダーに切り替える",
|
||||||
|
},
|
||||||
|
OpenAI: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "APIキー",
|
||||||
|
SubTitle: "カスタムOpenAIキーを使用してパスワードアクセス制限を回避",
|
||||||
|
Placeholder: "OpenAI APIキー",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "エンドポイント",
|
||||||
|
SubTitle:
|
||||||
|
"デフォルト以外のアドレスにはhttp(s)://を含める必要があります",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Azure: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "APIキー",
|
||||||
|
SubTitle: "カスタムAzureキーを使用してパスワードアクセス制限を回避",
|
||||||
|
Placeholder: "Azure APIキー",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "エンドポイント",
|
||||||
|
SubTitle: "例:",
|
||||||
|
},
|
||||||
|
|
||||||
|
ApiVerion: {
|
||||||
|
Title: "APIバージョン (azure api version)",
|
||||||
|
SubTitle: "特定のバージョンを選択",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Anthropic: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "APIキー",
|
||||||
|
SubTitle:
|
||||||
|
"カスタムAnthropicキーを使用してパスワードアクセス制限を回避",
|
||||||
|
Placeholder: "Anthropic APIキー",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "エンドポイント",
|
||||||
|
SubTitle: "例:",
|
||||||
|
},
|
||||||
|
|
||||||
|
ApiVerion: {
|
||||||
|
Title: "APIバージョン (claude api version)",
|
||||||
|
SubTitle: "特定のAPIバージョンを選択",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Google: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "APIキー",
|
||||||
|
SubTitle: "Google AIからAPIキーを取得",
|
||||||
|
Placeholder: "Google AI Studio APIキーを入力",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "エンドポイント",
|
||||||
|
SubTitle: "例:",
|
||||||
|
},
|
||||||
|
|
||||||
|
ApiVersion: {
|
||||||
|
Title: "APIバージョン(gemini-pro専用)",
|
||||||
|
SubTitle: "特定のAPIバージョンを選択",
|
||||||
|
},
|
||||||
|
GoogleSafetySettings: {
|
||||||
|
Title: "Google セーフティ設定",
|
||||||
|
SubTitle: "コンテンツフィルタリングレベルを設定",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Baidu: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "APIキー",
|
||||||
|
SubTitle: "カスタムBaidu APIキーを使用",
|
||||||
|
Placeholder: "Baidu APIキー",
|
||||||
|
},
|
||||||
|
SecretKey: {
|
||||||
|
Title: "シークレットキー",
|
||||||
|
SubTitle: "カスタムBaiduシークレットキーを使用",
|
||||||
|
Placeholder: "Baiduシークレットキー",
|
||||||
|
},
|
||||||
|
Endpoint: {
|
||||||
|
Title: "エンドポイント",
|
||||||
|
SubTitle: "カスタムはサポートしていません、.env設定に進んでください",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ByteDance: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "APIキー",
|
||||||
|
SubTitle: "カスタムByteDance APIキーを使用",
|
||||||
|
Placeholder: "ByteDance APIキー",
|
||||||
|
},
|
||||||
|
Endpoint: {
|
||||||
|
Title: "エンドポイント",
|
||||||
|
SubTitle: "例:",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Alibaba: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "APIキー",
|
||||||
|
SubTitle: "カスタムAlibaba Cloud APIキーを使用",
|
||||||
|
Placeholder: "Alibaba Cloud APIキー",
|
||||||
|
},
|
||||||
|
Endpoint: {
|
||||||
|
Title: "エンドポイント",
|
||||||
|
SubTitle: "例:",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CustomModel: {
|
||||||
|
Title: "カスタムモデル名",
|
||||||
|
SubTitle: "カスタムモデルの選択肢を追加、英語のカンマで区切る",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
Model: "モデル (model)",
|
Model: "モデル (model)",
|
||||||
|
CompressModel: {
|
||||||
|
Title: "圧縮モデル",
|
||||||
|
SubTitle: "履歴を圧縮するために使用されるモデル",
|
||||||
|
},
|
||||||
Temperature: {
|
Temperature: {
|
||||||
Title: "ランダム性 (temperature)",
|
Title: "ランダム性 (temperature)",
|
||||||
|
SubTitle: "値が大きいほど応答がランダムになります",
|
||||||
|
},
|
||||||
|
TopP: {
|
||||||
|
Title: "トップP (top_p)",
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"値が大きいほど、回答がランダムになります。1以上の値には文字化けが含まれる可能性があります。",
|
"ランダム性に似ていますが、ランダム性と一緒に変更しないでください",
|
||||||
},
|
},
|
||||||
MaxTokens: {
|
MaxTokens: {
|
||||||
Title: "シングルレスポンス制限 (max_tokens)",
|
Title: "1回の応答制限 (max_tokens)",
|
||||||
SubTitle: "1回のインタラクションで使用される最大トークン数",
|
SubTitle: "1回の対話で使用される最大トークン数",
|
||||||
},
|
},
|
||||||
PresencePenalty: {
|
PresencePenalty: {
|
||||||
Title: "トピックの新鮮度 (presence_penalty)",
|
Title: "新鮮度 (presence_penalty)",
|
||||||
SubTitle: "値が大きいほど、新しいトピックへの展開が可能になります。",
|
SubTitle: "値が大きいほど新しいトピックに移行する可能性が高くなります",
|
||||||
},
|
},
|
||||||
FrequencyPenalty: {
|
FrequencyPenalty: {
|
||||||
Title: "話題の頻度 (frequency_penalty)",
|
Title: "頻度ペナルティ (frequency_penalty)",
|
||||||
SubTitle: "値が大きいほど、重複語を低減する可能性が高くなります",
|
SubTitle: "値が大きいほど繰り返しの単語が減少します",
|
||||||
},
|
|
||||||
AutoGenerateTitle: {
|
|
||||||
Title: "タイトルの自動生成",
|
|
||||||
SubTitle: "会話内容に基づいて適切なタイトルを生成する",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Store: {
|
Store: {
|
||||||
DefaultTopic: "新しいチャット",
|
DefaultTopic: "新しいチャット",
|
||||||
BotHello: "何かお手伝いできることはありますか",
|
BotHello: "何かお手伝いできますか?",
|
||||||
Error: "エラーが発生しました。しばらくしてからやり直してください。",
|
Error: "エラーが発生しました。後でもう一度試してください",
|
||||||
Prompt: {
|
Prompt: {
|
||||||
History: (content: string) =>
|
History: (content: string) =>
|
||||||
"これは、AI とユーザの過去のチャットを要約した前提となるストーリーです:" +
|
"これは前提としての履歴チャットの要約です:" + content,
|
||||||
content,
|
|
||||||
Topic:
|
Topic:
|
||||||
"4~5文字でこの文章の簡潔な主題を返してください。説明、句読点、感嘆詞、余分なテキストは無しで。もし主題がない場合は、「おしゃべり」を返してください",
|
"この文の簡潔なテーマを四から五文字で返してください。説明、句読点、感嘆詞、余計なテキストは不要です。太字も不要です。テーマがない場合は「雑談」と返してください",
|
||||||
Summarize:
|
Summarize:
|
||||||
"あなたとユーザの会話を簡潔にまとめて、後続のコンテキストプロンプトとして使ってください。200字以内に抑えてください。",
|
"対話の内容を簡潔に要約し、後続のコンテキストプロンプトとして使用します。200文字以内に抑えてください",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Copy: {
|
Copy: {
|
||||||
Success: "クリップボードに書き込みました",
|
Success: "クリップボードに書き込みました",
|
||||||
Failed: "コピーに失敗しました。クリップボード許可を与えてください。",
|
Failed: "コピーに失敗しました。クリップボードの権限を付与してください",
|
||||||
|
},
|
||||||
|
Download: {
|
||||||
|
Success: "内容がダウンロードされました",
|
||||||
|
Failed: "ダウンロードに失敗しました",
|
||||||
},
|
},
|
||||||
Context: {
|
Context: {
|
||||||
Toast: (x: any) => `キャラクターが ${x} 件設定されました`,
|
Toast: (x: any) => `${x} 件のプリセットプロンプトが含まれています`,
|
||||||
Edit: "キャラクタープリセットとモデル設定",
|
Edit: "現在の対話設定",
|
||||||
Add: "追加",
|
Add: "対話を追加",
|
||||||
|
Clear: "コンテキストがクリアされました",
|
||||||
|
Revert: "コンテキストを元に戻す",
|
||||||
},
|
},
|
||||||
Plugin: { Name: "プラグイン" },
|
Plugin: {
|
||||||
FineTuned: { Sysmessage: "あなたはアシスタントです" },
|
Name: "プラグイン",
|
||||||
Mask: {
|
},
|
||||||
Name: "キャラクタープリセット",
|
Discovery: {
|
||||||
|
Name: "発見",
|
||||||
|
},
|
||||||
|
FineTuned: {
|
||||||
|
Sysmessage: "あなたはアシスタントです",
|
||||||
|
},
|
||||||
|
SearchChat: {
|
||||||
|
Name: "検索",
|
||||||
Page: {
|
Page: {
|
||||||
Title: "キャラクタープリセット",
|
Title: "チャット履歴を検索",
|
||||||
SubTitle: (count: number) => `${count} 件見つかりました。`,
|
Search: "検索キーワードを入力",
|
||||||
Search: "検索",
|
NoResult: "結果が見つかりませんでした",
|
||||||
Create: "新規",
|
NoData: "データがありません",
|
||||||
|
Loading: "読み込み中",
|
||||||
|
|
||||||
|
SubTitle: (count: number) => `${count} 件の結果が見つかりました`,
|
||||||
},
|
},
|
||||||
Item: {
|
Item: {
|
||||||
Info: (count: number) => `包含 ${count} 条预设对话`,
|
View: "表示",
|
||||||
Chat: "会話",
|
},
|
||||||
View: "詳細",
|
},
|
||||||
|
Mask: {
|
||||||
|
Name: "マスク",
|
||||||
|
Page: {
|
||||||
|
Title: "プリセットキャラクターマスク",
|
||||||
|
SubTitle: (count: number) => `${count} 件のプリセットキャラクター定義`,
|
||||||
|
Search: "キャラクターマスクを検索",
|
||||||
|
Create: "新規作成",
|
||||||
|
},
|
||||||
|
Item: {
|
||||||
|
Info: (count: number) => `${count} 件のプリセット対話が含まれています`,
|
||||||
|
Chat: "対話",
|
||||||
|
View: "表示",
|
||||||
Edit: "編集",
|
Edit: "編集",
|
||||||
Delete: "削除",
|
Delete: "削除",
|
||||||
DeleteConfirm: "本当に削除しますか?",
|
DeleteConfirm: "削除してもよろしいですか?",
|
||||||
},
|
},
|
||||||
EditModal: {
|
EditModal: {
|
||||||
Title: (readonly: boolean) =>
|
Title: (readonly: boolean) =>
|
||||||
`キャラクタープリセットを編集 ${readonly ? "(読み取り専用)" : ""}`,
|
`プリセットマスクの編集 ${readonly ? "(読み取り専用)" : ""}`,
|
||||||
Download: "ダウンロード",
|
Download: "プリセットをダウンロード",
|
||||||
Clone: "複製",
|
Clone: "プリセットをクローン",
|
||||||
},
|
},
|
||||||
Config: {
|
Config: {
|
||||||
Avatar: "キャラクターのアイコン",
|
Avatar: "キャラクターアバター",
|
||||||
Name: "キャラクターの名前",
|
Name: "キャラクター名",
|
||||||
Sync: {
|
Sync: {
|
||||||
Title: "グローバル設定を利用する",
|
Title: "グローバル設定を使用",
|
||||||
SubTitle: "このチャットでグローバル設定を利用します。",
|
SubTitle: "現在の対話でグローバルモデル設定を使用するかどうか",
|
||||||
Confirm:
|
Confirm:
|
||||||
"カスタム設定を上書きしてグローバル設定を使用します、よろしいですか?",
|
"現在の対話のカスタム設定が自動的に上書きされます。グローバル設定を有効にしてもよろしいですか?",
|
||||||
},
|
},
|
||||||
HideContext: {
|
HideContext: {
|
||||||
Title: "キャラクター設定を表示しない",
|
Title: "プリセット対話を非表示",
|
||||||
SubTitle: "チャット画面でのキャラクター設定を非表示にします。",
|
SubTitle:
|
||||||
|
"非表示にすると、プリセット対話はチャット画面に表示されません",
|
||||||
|
},
|
||||||
|
Share: {
|
||||||
|
Title: "このマスクを共有",
|
||||||
|
SubTitle: "このマスクの直リンクを生成",
|
||||||
|
Action: "リンクをコピー",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
NewChat: {
|
NewChat: {
|
||||||
Return: "戻る",
|
Return: "戻る",
|
||||||
Skip: "スキップ",
|
Skip: "直接開始",
|
||||||
Title: "キャラクター",
|
NotShow: "今後表示しない",
|
||||||
SubTitle: "さあ、AIにキャラクターを設定して会話を始めてみましょう",
|
ConfirmNoShow:
|
||||||
More: "もっと探す",
|
"無効にしてもよろしいですか?無効にした後、設定でいつでも再度有効にできます。",
|
||||||
NotShow: "今後は表示しない",
|
Title: "マスクを選択",
|
||||||
ConfirmNoShow: "いつでも設定から有効化できます。",
|
SubTitle: "今すぐ始めよう、マスクの背後にある魂と思考の衝突",
|
||||||
|
More: "すべて表示",
|
||||||
|
},
|
||||||
|
|
||||||
|
URLCommand: {
|
||||||
|
Code: "リンクにアクセスコードが含まれています。自動入力しますか?",
|
||||||
|
Settings: "リンクにプリセット設定が含まれています。自動入力しますか?",
|
||||||
},
|
},
|
||||||
|
|
||||||
UI: {
|
UI: {
|
||||||
Confirm: "確認",
|
Confirm: "確認",
|
||||||
Cancel: "キャンセル",
|
Cancel: "キャンセル",
|
||||||
Close: "閉じる",
|
Close: "閉じる",
|
||||||
Create: "新規",
|
Create: "新規作成",
|
||||||
Edit: "編集",
|
Edit: "編集",
|
||||||
|
Export: "エクスポート",
|
||||||
|
Import: "インポート",
|
||||||
|
Sync: "同期",
|
||||||
|
Config: "設定",
|
||||||
},
|
},
|
||||||
|
|
||||||
Exporter: {
|
Exporter: {
|
||||||
|
Description: {
|
||||||
|
Title: "コンテキストをクリアした後のメッセージのみが表示されます",
|
||||||
|
},
|
||||||
Model: "モデル",
|
Model: "モデル",
|
||||||
Messages: "メッセージ",
|
Messages: "メッセージ",
|
||||||
Topic: "トピック",
|
Topic: "テーマ",
|
||||||
Time: "時間",
|
Time: "時間",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,76 +1,169 @@
|
||||||
import { SubmitKey } from "../store/config";
|
import { SubmitKey } from "../store/config";
|
||||||
|
|
||||||
import type { PartialLocaleType } from "./index";
|
import type { PartialLocaleType } from "./index";
|
||||||
|
import { getClientConfig } from "../config/client";
|
||||||
|
import { SAAS_CHAT_UTM_URL } from "@/app/constant";
|
||||||
|
const isApp = !!getClientConfig()?.isApp;
|
||||||
|
|
||||||
const ko: PartialLocaleType = {
|
const ko: PartialLocaleType = {
|
||||||
WIP: "곧 출시 예정...",
|
WIP: "곧 출시 예정...",
|
||||||
Error: {
|
Error: {
|
||||||
Unauthorized:
|
Unauthorized: isApp
|
||||||
"권한이 없습니다. 설정 페이지에서 액세스 코드를 [입력하세요](/#/auth).",
|
? `😆 대화 중 문제가 발생했습니다, 걱정하지 마세요:
|
||||||
|
\\ 1️⃣ 제로 구성으로 시작하고 싶다면, [여기를 클릭하여 즉시 대화를 시작하세요 🚀](${SAAS_CHAT_UTM_URL})
|
||||||
|
\\ 2️⃣ 자신의 OpenAI 리소스를 사용하고 싶다면, [여기를 클릭하여](/#/settings) 설정을 수정하세요 ⚙️`
|
||||||
|
: `😆 대화 중 문제가 발생했습니다, 걱정하지 마세요:
|
||||||
|
\ 1️⃣ 제로 구성으로 시작하고 싶다면, [여기를 클릭하여 즉시 대화를 시작하세요 🚀](${SAAS_CHAT_UTM_URL})
|
||||||
|
\ 2️⃣ 개인 배포 버전을 사용하고 있다면, [여기를 클릭하여](/#/auth) 접근 키를 입력하세요 🔑
|
||||||
|
\ 3️⃣ 자신의 OpenAI 리소스를 사용하고 싶다면, [여기를 클릭하여](/#/settings) 설정을 수정하세요 ⚙️
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
Auth: {
|
||||||
|
Title: "비밀번호 필요",
|
||||||
|
Tips: "관리자가 비밀번호 인증을 활성화했습니다. 아래에 접근 코드를 입력하십시오.",
|
||||||
|
SubTips: "또는 OpenAI 또는 Google API 키를 입력하십시오.",
|
||||||
|
Input: "여기에 접근 코드를 입력하십시오.",
|
||||||
|
Confirm: "확인",
|
||||||
|
Later: "나중에 하기",
|
||||||
|
Return: "돌아가기",
|
||||||
|
SaasTips: "설정이 너무 복잡합니다. 즉시 사용하고 싶습니다.",
|
||||||
|
TopTips:
|
||||||
|
"🥳 NextChat AI 출시 기념 할인, 지금 OpenAI o1, GPT-4o, Claude-3.5 및 최신 대형 모델을 해제하세요",
|
||||||
},
|
},
|
||||||
ChatItem: {
|
ChatItem: {
|
||||||
ChatItemCount: (count: number) => `${count}개의 메시지`,
|
ChatItemCount: (count: number) => `${count} 개의 대화`,
|
||||||
},
|
},
|
||||||
Chat: {
|
Chat: {
|
||||||
SubTitle: (count: number) => `ChatGPT와의 ${count}개의 메시지`,
|
SubTitle: (count: number) => `총 ${count} 개의 대화`,
|
||||||
|
EditMessage: {
|
||||||
|
Title: "메시지 기록 편집",
|
||||||
|
Topic: {
|
||||||
|
Title: "채팅 주제",
|
||||||
|
SubTitle: "현재 채팅 주제 변경",
|
||||||
|
},
|
||||||
|
},
|
||||||
Actions: {
|
Actions: {
|
||||||
ChatList: "채팅 목록으로 이동",
|
ChatList: "메시지 목록 보기",
|
||||||
CompressedHistory: "압축된 기억력 메모리 프롬프트",
|
CompressedHistory: "압축된 히스토리 프롬프트 보기",
|
||||||
Export: "모든 메시지를 Markdown으로 내보내기",
|
Export: "채팅 기록 내보내기",
|
||||||
Copy: "복사",
|
Copy: "복사",
|
||||||
Stop: "중지",
|
Stop: "정지",
|
||||||
Retry: "다시 시도",
|
Retry: "다시 시도",
|
||||||
|
Pin: "고정",
|
||||||
|
PinToastContent: "1 개의 대화를 프롬프트에 고정했습니다.",
|
||||||
|
PinToastAction: "보기",
|
||||||
Delete: "삭제",
|
Delete: "삭제",
|
||||||
|
Edit: "편집",
|
||||||
|
RefreshTitle: "제목 새로고침",
|
||||||
|
RefreshToast: "제목 새로고침 요청이 전송되었습니다",
|
||||||
|
},
|
||||||
|
Commands: {
|
||||||
|
new: "새 채팅",
|
||||||
|
newm: "마스크에서 새 채팅",
|
||||||
|
next: "다음 채팅",
|
||||||
|
prev: "이전 채팅",
|
||||||
|
clear: "컨텍스트 지우기",
|
||||||
|
del: "채팅 삭제",
|
||||||
|
},
|
||||||
|
InputActions: {
|
||||||
|
Stop: "응답 중지",
|
||||||
|
ToBottom: "최신으로 스크롤",
|
||||||
|
Theme: {
|
||||||
|
auto: "자동 테마",
|
||||||
|
light: "라이트 모드",
|
||||||
|
dark: "다크 모드",
|
||||||
|
},
|
||||||
|
Prompt: "빠른 명령",
|
||||||
|
Masks: "모든 마스크",
|
||||||
|
Clear: "채팅 지우기",
|
||||||
|
Settings: "채팅 설정",
|
||||||
|
UploadImage: "이미지 업로드",
|
||||||
},
|
},
|
||||||
Rename: "채팅 이름 변경",
|
Rename: "채팅 이름 변경",
|
||||||
Typing: "입력 중...",
|
Typing: "입력 중…",
|
||||||
Input: (submitKey: string) => {
|
Input: (submitKey: string) => {
|
||||||
var inputHints = `${submitKey}를 눌러 보내기`;
|
var inputHints = `${submitKey} 전송`;
|
||||||
if (submitKey === String(SubmitKey.Enter)) {
|
if (submitKey === String(SubmitKey.Enter)) {
|
||||||
inputHints += ", Shift + Enter로 줄 바꿈";
|
inputHints += ",Shift + Enter 줄 바꿈";
|
||||||
}
|
}
|
||||||
return inputHints + ", 프롬프트 검색을 위해 / 입력";
|
return inputHints + ",/ 자동 완성,: 명령어 입력";
|
||||||
},
|
},
|
||||||
Send: "보내기",
|
Send: "전송",
|
||||||
Config: {
|
Config: {
|
||||||
Reset: "기본값으로 재설정",
|
Reset: "기억 지우기",
|
||||||
SaveAs: "마스크로 저장",
|
SaveAs: "마스크로 저장",
|
||||||
},
|
},
|
||||||
|
IsContext: "프롬프트 설정",
|
||||||
},
|
},
|
||||||
Export: {
|
Export: {
|
||||||
Title: "모든 메시지",
|
Title: "채팅 기록 공유",
|
||||||
Copy: "모두 복사",
|
Copy: "모두 복사",
|
||||||
Download: "다운로드",
|
Download: "파일 다운로드",
|
||||||
MessageFromYou: "나의 메시지",
|
Share: "ShareGPT에 공유",
|
||||||
MessageFromChatGPT: "ChatGPT의 메시지",
|
MessageFromYou: "사용자",
|
||||||
|
MessageFromChatGPT: "ChatGPT",
|
||||||
|
Format: {
|
||||||
|
Title: "내보내기 형식",
|
||||||
|
SubTitle: "Markdown 텍스트 또는 PNG 이미지로 내보낼 수 있습니다.",
|
||||||
|
},
|
||||||
|
IncludeContext: {
|
||||||
|
Title: "프롬프트 컨텍스트 포함",
|
||||||
|
SubTitle: "메시지에 프롬프트 컨텍스트를 표시할지 여부",
|
||||||
|
},
|
||||||
|
Steps: {
|
||||||
|
Select: "선택",
|
||||||
|
Preview: "미리보기",
|
||||||
|
},
|
||||||
|
Image: {
|
||||||
|
Toast: "스크린샷 생성 중",
|
||||||
|
Modal: "길게 누르거나 오른쪽 클릭하여 이미지를 저장하십시오.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Select: {
|
||||||
|
Search: "메시지 검색",
|
||||||
|
All: "모두 선택",
|
||||||
|
Latest: "최근 몇 개",
|
||||||
|
Clear: "선택 지우기",
|
||||||
},
|
},
|
||||||
Memory: {
|
Memory: {
|
||||||
Title: "기억 프롬프트",
|
Title: "기록 요약",
|
||||||
EmptyContent: "아직 내용이 없습니다.",
|
EmptyContent: "대화 내용이 너무 짧아 요약할 필요 없음",
|
||||||
Send: "기억 보내기",
|
Send: "자동으로 채팅 기록을 압축하여 컨텍스트로 전송",
|
||||||
Copy: "기억 복사",
|
Copy: "요약 복사",
|
||||||
Reset: "세션 재설정",
|
Reset: "[사용되지 않음]",
|
||||||
ResetConfirm:
|
ResetConfirm: "기록 요약을 지우겠습니까?",
|
||||||
"재설정하면 현재 대화 기록과 기억력이 삭제됩니다. 정말 재설정하시겠습니까?",
|
|
||||||
},
|
},
|
||||||
Home: {
|
Home: {
|
||||||
NewChat: "새로운 채팅",
|
NewChat: "새 채팅",
|
||||||
DeleteChat: "선택한 대화를 삭제하시겠습니까?",
|
DeleteChat: "선택한 대화를 삭제하시겠습니까?",
|
||||||
DeleteToast: "채팅이 삭제되었습니다.",
|
DeleteToast: "대화가 삭제되었습니다.",
|
||||||
Revert: "되돌리기",
|
Revert: "되돌리기",
|
||||||
},
|
},
|
||||||
Settings: {
|
Settings: {
|
||||||
Title: "설정",
|
Title: "설정",
|
||||||
SubTitle: "모든 설정",
|
SubTitle: "모든 설정 옵션",
|
||||||
|
|
||||||
|
Danger: {
|
||||||
|
Reset: {
|
||||||
|
Title: "모든 설정 초기화",
|
||||||
|
SubTitle: "모든 설정 항목을 기본값으로 초기화",
|
||||||
|
Action: "지금 초기화",
|
||||||
|
Confirm: "모든 설정을 초기화하시겠습니까?",
|
||||||
|
},
|
||||||
|
Clear: {
|
||||||
|
Title: "모든 데이터 지우기",
|
||||||
|
SubTitle: "모든 채팅 및 설정 데이터 지우기",
|
||||||
|
Action: "지금 지우기",
|
||||||
|
Confirm: "모든 채팅 및 설정 데이터를 지우시겠습니까?",
|
||||||
|
},
|
||||||
|
},
|
||||||
Lang: {
|
Lang: {
|
||||||
Name: "Language", // ATTENTION: if you wanna add a new translation, please do not translate this value, leave it as `Language`
|
Name: "Language", // 주의: 새 번역을 추가하려면 이 값을 번역하지 말고 그대로 유지하세요.
|
||||||
All: "All Languages",
|
All: "모든 언어",
|
||||||
},
|
},
|
||||||
Avatar: "아바타",
|
Avatar: "아바타",
|
||||||
FontSize: {
|
FontSize: {
|
||||||
Title: "글꼴 크기",
|
Title: "글꼴 크기",
|
||||||
SubTitle: "채팅 내용의 글꼴 크기 조정",
|
SubTitle: "채팅 내용의 글꼴 크기",
|
||||||
},
|
},
|
||||||
FontFamily: {
|
FontFamily: {
|
||||||
Title: "채팅 폰트",
|
Title: "채팅 폰트",
|
||||||
|
@ -78,43 +171,100 @@ const ko: PartialLocaleType = {
|
||||||
Placeholder: "폰트 이름",
|
Placeholder: "폰트 이름",
|
||||||
},
|
},
|
||||||
InjectSystemPrompts: {
|
InjectSystemPrompts: {
|
||||||
Title: "시스템 프롬프트 주입",
|
Title: "시스템 수준 프롬프트 삽입",
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"각 요청의 메시지 목록의 시작에 ChatGPT 시스템 프롬프트를 강제로 추가합니다",
|
"각 요청 메시지 목록의 시작 부분에 ChatGPT 시스템 프롬프트를 강제로 추가",
|
||||||
},
|
},
|
||||||
|
InputTemplate: {
|
||||||
|
Title: "사용자 입력 전처리",
|
||||||
|
SubTitle: "사용자의 최신 메시지가 이 템플릿에 채워집니다.",
|
||||||
|
},
|
||||||
|
|
||||||
Update: {
|
Update: {
|
||||||
Version: (x: string) => `버전: ${x}`,
|
Version: (x: string) => `현재 버전: ${x}`,
|
||||||
IsLatest: "최신 버전",
|
IsLatest: "최신 버전입니다.",
|
||||||
CheckUpdate: "업데이트 확인",
|
CheckUpdate: "업데이트 확인",
|
||||||
IsChecking: "업데이트 확인 중...",
|
IsChecking: "업데이트 확인 중...",
|
||||||
FoundUpdate: (x: string) => `새 버전 발견: ${x}`,
|
FoundUpdate: (x: string) => `새 버전 발견: ${x}`,
|
||||||
GoToUpdate: "업데이트",
|
GoToUpdate: "업데이트로 이동",
|
||||||
},
|
},
|
||||||
SendKey: "전송 키",
|
SendKey: "전송 키",
|
||||||
Theme: "테마",
|
Theme: "테마",
|
||||||
TightBorder: "조밀한 테두리",
|
TightBorder: "테두리 없는 모드",
|
||||||
SendPreviewBubble: {
|
SendPreviewBubble: {
|
||||||
Title: "미리 보기 버블 전송",
|
Title: "미리보기 버블",
|
||||||
SubTitle: "버블에서 마크다운 미리 보기",
|
SubTitle: "미리보기 버블에서 Markdown 콘텐츠 미리보기",
|
||||||
|
},
|
||||||
|
AutoGenerateTitle: {
|
||||||
|
Title: "제목 자동 생성",
|
||||||
|
SubTitle: "대화 내용에 따라 적절한 제목 생성",
|
||||||
|
},
|
||||||
|
Sync: {
|
||||||
|
CloudState: "클라우드 데이터",
|
||||||
|
NotSyncYet: "아직 동기화되지 않았습니다.",
|
||||||
|
Success: "동기화 성공",
|
||||||
|
Fail: "동기화 실패",
|
||||||
|
|
||||||
|
Config: {
|
||||||
|
Modal: {
|
||||||
|
Title: "클라우드 동기화 구성",
|
||||||
|
Check: "사용 가능성 확인",
|
||||||
|
},
|
||||||
|
SyncType: {
|
||||||
|
Title: "동기화 유형",
|
||||||
|
SubTitle: "선호하는 동기화 서버 선택",
|
||||||
|
},
|
||||||
|
Proxy: {
|
||||||
|
Title: "프록시 사용",
|
||||||
|
SubTitle:
|
||||||
|
"브라우저에서 동기화할 때 프록시를 활성화하여 교차 출처 제한을 피해야 함",
|
||||||
|
},
|
||||||
|
ProxyUrl: {
|
||||||
|
Title: "프록시 주소",
|
||||||
|
SubTitle: "이 프로젝트에서 제공하는 교차 출처 프록시만 해당",
|
||||||
|
},
|
||||||
|
|
||||||
|
WebDav: {
|
||||||
|
Endpoint: "WebDAV 주소",
|
||||||
|
UserName: "사용자 이름",
|
||||||
|
Password: "비밀번호",
|
||||||
|
},
|
||||||
|
|
||||||
|
UpStash: {
|
||||||
|
Endpoint: "UpStash Redis REST URL",
|
||||||
|
UserName: "백업 이름",
|
||||||
|
Password: "UpStash Redis REST Token",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
LocalState: "로컬 데이터",
|
||||||
|
Overview: (overview: any) => {
|
||||||
|
return `${overview.chat} 회의 대화, ${overview.message} 개의 메시지, ${overview.prompt} 개의 프롬프트, ${overview.mask} 개의 마스크`;
|
||||||
|
},
|
||||||
|
ImportFailed: "가져오기 실패",
|
||||||
},
|
},
|
||||||
Mask: {
|
Mask: {
|
||||||
Splash: {
|
Splash: {
|
||||||
Title: "마스크 시작 화면",
|
Title: "마스크 시작 페이지",
|
||||||
SubTitle: "새로운 채팅 시작 전에 마스크 시작 화면 표시",
|
SubTitle: "새 채팅 시 마스크 시작 페이지 표시",
|
||||||
|
},
|
||||||
|
Builtin: {
|
||||||
|
Title: "내장 마스크 숨기기",
|
||||||
|
SubTitle: "모든 마스크 목록에서 내장 마스크 숨기기",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Prompt: {
|
Prompt: {
|
||||||
Disable: {
|
Disable: {
|
||||||
Title: "자동 완성 비활성화",
|
Title: "프롬프트 자동 완성 비활성화",
|
||||||
SubTitle: "자동 완성을 활성화하려면 /를 입력하세요.",
|
SubTitle: "입력 상자 시작 부분에 / 를 입력하여 자동 완성 활성화",
|
||||||
},
|
},
|
||||||
List: "프롬프트 목록",
|
List: "사용자 정의 프롬프트 목록",
|
||||||
ListCount: (builtin: number, custom: number) =>
|
ListCount: (builtin: number, custom: number) =>
|
||||||
`내장 ${builtin}개, 사용자 정의 ${custom}개`,
|
`내장 ${builtin} 개, 사용자 정의 ${custom} 개`,
|
||||||
Edit: "편집",
|
Edit: "편집",
|
||||||
Modal: {
|
Modal: {
|
||||||
Title: "프롬프트 목록",
|
Title: "프롬프트 목록",
|
||||||
Add: "추가",
|
Add: "새로 만들기",
|
||||||
Search: "프롬프트 검색",
|
Search: "프롬프트 검색",
|
||||||
},
|
},
|
||||||
EditModal: {
|
EditModal: {
|
||||||
|
@ -122,116 +272,305 @@ const ko: PartialLocaleType = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
HistoryCount: {
|
HistoryCount: {
|
||||||
Title: "첨부된 메시지 수",
|
Title: "히스토리 메시지 수",
|
||||||
SubTitle: "요청당 첨부된 전송된 메시지 수",
|
SubTitle: "각 요청에 포함된 히스토리 메시지 수",
|
||||||
},
|
},
|
||||||
CompressThreshold: {
|
CompressThreshold: {
|
||||||
Title: "기록 압축 임계값",
|
Title: "히스토리 메시지 길이 압축 임계값",
|
||||||
SubTitle: "미압축 메시지 길이가 임계값을 초과하면 압축됨",
|
SubTitle: "압축되지 않은 히스토리 메시지가 이 값을 초과하면 압축 수행",
|
||||||
},
|
},
|
||||||
|
|
||||||
Usage: {
|
Usage: {
|
||||||
Title: "계정 잔액",
|
Title: "잔액 조회",
|
||||||
SubTitle(used: any, total: any) {
|
SubTitle(used: any, total: any) {
|
||||||
return `이번 달 사용액 ${used}, 구독액 ${total}`;
|
return `이번 달 사용된 금액: $${used},총 구독 금액: $${total}`;
|
||||||
},
|
},
|
||||||
IsChecking: "확인 중...",
|
IsChecking: "확인 중...",
|
||||||
Check: "확인",
|
Check: "다시 확인",
|
||||||
NoAccess: "잔액 확인을 위해 API 키를 입력하세요.",
|
NoAccess: "잔액을 보려면 API 키 또는 접근 비밀번호를 입력하십시오.",
|
||||||
},
|
},
|
||||||
|
|
||||||
Model: "모델",
|
Access: {
|
||||||
|
SaasStart: {
|
||||||
|
Title: "NextChat AI 사용하기",
|
||||||
|
Label: "(가장 비용 효율적인 솔루션)",
|
||||||
|
SubTitle:
|
||||||
|
"NextChat에 의해 공식적으로 유지 관리되며, 제로 구성으로 즉시 사용할 수 있으며, OpenAI o1, GPT-4o, Claude-3.5와 같은 최신 대형 모델을 지원합니다",
|
||||||
|
ChatNow: "지금 채팅하기",
|
||||||
|
},
|
||||||
|
|
||||||
|
AccessCode: {
|
||||||
|
Title: "접근 비밀번호",
|
||||||
|
SubTitle: "관리자가 암호화된 접근을 활성화했습니다.",
|
||||||
|
Placeholder: "접근 비밀번호를 입력하십시오.",
|
||||||
|
},
|
||||||
|
CustomEndpoint: {
|
||||||
|
Title: "커스텀 엔드포인트",
|
||||||
|
SubTitle: "커스텀 Azure 또는 OpenAI 서비스를 사용할지 여부",
|
||||||
|
},
|
||||||
|
Provider: {
|
||||||
|
Title: "모델 서비스 제공업체",
|
||||||
|
SubTitle: "다른 서비스 제공업체로 전환",
|
||||||
|
},
|
||||||
|
OpenAI: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "API 키",
|
||||||
|
SubTitle: "커스텀 OpenAI 키를 사용하여 비밀번호 접근 제한 우회",
|
||||||
|
Placeholder: "OpenAI API 키",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "엔드포인트 주소",
|
||||||
|
SubTitle: "기본 주소 외에 http(s)://을 포함해야 함",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Azure: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "엔드포인트 키",
|
||||||
|
SubTitle: "커스텀 Azure 키를 사용하여 비밀번호 접근 제한 우회",
|
||||||
|
Placeholder: "Azure API 키",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "엔드포인트 주소",
|
||||||
|
SubTitle: "예: ",
|
||||||
|
},
|
||||||
|
|
||||||
|
ApiVerion: {
|
||||||
|
Title: "API 버전 (azure api version)",
|
||||||
|
SubTitle: "특정 부분 버전 선택",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Anthropic: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "엔드포인트 키",
|
||||||
|
SubTitle: "커스텀 Anthropic 키를 사용하여 비밀번호 접근 제한 우회",
|
||||||
|
Placeholder: "Anthropic API 키",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "엔드포인트 주소",
|
||||||
|
SubTitle: "예: ",
|
||||||
|
},
|
||||||
|
|
||||||
|
ApiVerion: {
|
||||||
|
Title: "API 버전 (claude api version)",
|
||||||
|
SubTitle: "특정 API 버전 입력",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Google: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "API 키",
|
||||||
|
SubTitle: "Google AI에서 API 키를 가져오세요.",
|
||||||
|
Placeholder: "Google AI Studio API 키 입력",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "엔드포인트 주소",
|
||||||
|
SubTitle: "예: ",
|
||||||
|
},
|
||||||
|
|
||||||
|
ApiVersion: {
|
||||||
|
Title: "API 버전 (gemini-pro 전용)",
|
||||||
|
SubTitle: "특정 API 버전 선택",
|
||||||
|
},
|
||||||
|
GoogleSafetySettings: {
|
||||||
|
Title: "Google 안전 필터링 수준",
|
||||||
|
SubTitle: "콘텐츠 필터링 수준 설정",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Baidu: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "API 키",
|
||||||
|
SubTitle: "커스텀 Baidu API 키 사용",
|
||||||
|
Placeholder: "Baidu API 키",
|
||||||
|
},
|
||||||
|
SecretKey: {
|
||||||
|
Title: "Secret 키",
|
||||||
|
SubTitle: "커스텀 Baidu Secret 키 사용",
|
||||||
|
Placeholder: "Baidu Secret 키",
|
||||||
|
},
|
||||||
|
Endpoint: {
|
||||||
|
Title: "엔드포인트 주소",
|
||||||
|
SubTitle: "커스터마이즈는 .env에서 설정",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ByteDance: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "엔드포인트 키",
|
||||||
|
SubTitle: "커스텀 ByteDance API 키 사용",
|
||||||
|
Placeholder: "ByteDance API 키",
|
||||||
|
},
|
||||||
|
Endpoint: {
|
||||||
|
Title: "엔드포인트 주소",
|
||||||
|
SubTitle: "예: ",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Alibaba: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "엔드포인트 키",
|
||||||
|
SubTitle: "커스텀 Alibaba Cloud API 키 사용",
|
||||||
|
Placeholder: "Alibaba Cloud API 키",
|
||||||
|
},
|
||||||
|
Endpoint: {
|
||||||
|
Title: "엔드포인트 주소",
|
||||||
|
SubTitle: "예: ",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CustomModel: {
|
||||||
|
Title: "커스텀 모델 이름",
|
||||||
|
SubTitle: "커스텀 모델 옵션 추가, 영어 쉼표로 구분",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
Model: "모델 (model)",
|
||||||
|
CompressModel: {
|
||||||
|
Title: "압축 모델",
|
||||||
|
SubTitle: "기록을 압축하는 데 사용되는 모델",
|
||||||
|
},
|
||||||
Temperature: {
|
Temperature: {
|
||||||
Title: "온도 (temperature)",
|
Title: "무작위성 (temperature)",
|
||||||
SubTitle: "값이 클수록 더 무작위한 출력이 생성됩니다.",
|
SubTitle: "값이 클수록 응답이 더 무작위적",
|
||||||
|
},
|
||||||
|
TopP: {
|
||||||
|
Title: "탑 P 샘플링 (top_p)",
|
||||||
|
SubTitle: "무작위성과 유사하지만, 무작위성과 함께 변경하지 마십시오.",
|
||||||
},
|
},
|
||||||
MaxTokens: {
|
MaxTokens: {
|
||||||
Title: "최대 토큰 수 (max_tokens)",
|
Title: "단일 응답 제한 (max_tokens)",
|
||||||
SubTitle: "입력 토큰과 생성된 토큰의 최대 길이",
|
SubTitle: "단일 상호작용에 사용되는 최대 토큰 수",
|
||||||
},
|
},
|
||||||
PresencePenalty: {
|
PresencePenalty: {
|
||||||
Title: "존재 페널티 (presence_penalty)",
|
Title: "주제 신선도 (presence_penalty)",
|
||||||
SubTitle: "값이 클수록 새로운 주제에 대해 대화할 가능성이 높아집니다.",
|
SubTitle: "값이 클수록 새로운 주제로 확장할 가능성이 높음",
|
||||||
},
|
},
|
||||||
FrequencyPenalty: {
|
FrequencyPenalty: {
|
||||||
Title: "빈도 페널티(frequency penalty)",
|
Title: "빈도 벌점 (frequency_penalty)",
|
||||||
SubTitle: "값이 클수록 같은 줄이 반복될 가능성이 줄어듭니다.",
|
SubTitle: "값이 클수록 중복 단어 감소 가능성 높음",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Store: {
|
Store: {
|
||||||
DefaultTopic: "새 대화",
|
DefaultTopic: "새 채팅",
|
||||||
BotHello: "안녕하세요! 오늘 도움이 필요하신가요?",
|
BotHello: "무엇을 도와드릴까요?",
|
||||||
Error: "문제가 발생했습니다. 나중에 다시 시도해주세요.",
|
Error: "오류가 발생했습니다. 나중에 다시 시도해 주세요.",
|
||||||
Prompt: {
|
Prompt: {
|
||||||
History: (content: string) =>
|
History: (content: string) => "이것은 이전 채팅 요약입니다: " + content,
|
||||||
"이것은 AI와 사용자 간의 대화 기록을 요약한 내용입니다: " + content,
|
|
||||||
Topic:
|
Topic:
|
||||||
"다음과 같이 대화 내용을 요약하는 4~5단어 제목을 생성해주세요. 따옴표, 구두점, 인용부호, 기호 또는 추가 텍스트를 제거하십시오. 따옴표로 감싸진 부분을 제거하십시오.",
|
"네 글자에서 다섯 글자로 이 문장의 간략한 주제를 반환하세요. 설명이나 문장 부호, 어미, 불필요한 텍스트, 굵은 글씨는 필요 없습니다. 주제가 없다면 '잡담'이라고만 반환하세요.",
|
||||||
Summarize:
|
Summarize:
|
||||||
"200단어 이내로 저희 토론을 간략히 요약하여 앞으로의 맥락으로 사용할 수 있는 프롬프트로 만들어주세요.",
|
"대화 내용을 간략히 요약하여 후속 컨텍스트 프롬프트로 사용하세요. 200자 이내로 작성하세요.",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Copy: {
|
Copy: {
|
||||||
Success: "클립보드에 복사되었습니다.",
|
Success: "클립보드에 복사되었습니다.",
|
||||||
Failed: "복사 실패, 클립보드 접근 권한을 허용해주세요.",
|
Failed: "복사 실패, 클립보드 권한을 부여해주세요.",
|
||||||
|
},
|
||||||
|
Download: {
|
||||||
|
Success: "내용이 디렉토리에 다운로드되었습니다.",
|
||||||
|
Failed: "다운로드 실패.",
|
||||||
},
|
},
|
||||||
Context: {
|
Context: {
|
||||||
Toast: (x: any) => `컨텍스트 프롬프트 ${x}개 사용`,
|
Toast: (x: any) => ` ${x} 개의 프리셋 프롬프트 포함됨`,
|
||||||
Edit: "컨텍스트 및 메모리 프롬프트",
|
Edit: "현재 대화 설정",
|
||||||
Add: "프롬프트 추가",
|
Add: "대화 추가",
|
||||||
|
Clear: "컨텍스트가 지워졌습니다.",
|
||||||
|
Revert: "컨텍스트 복원",
|
||||||
},
|
},
|
||||||
Plugin: {
|
Plugin: {
|
||||||
Name: "플러그인",
|
Name: "플러그인",
|
||||||
},
|
},
|
||||||
FineTuned: {
|
FineTuned: {
|
||||||
Sysmessage: "당신은 어시스턴트입니다",
|
Sysmessage: "당신은 보조자입니다.",
|
||||||
|
},
|
||||||
|
SearchChat: {
|
||||||
|
Name: "검색",
|
||||||
|
Page: {
|
||||||
|
Title: "채팅 기록 검색",
|
||||||
|
Search: "검색어 입력",
|
||||||
|
NoResult: "결과를 찾을 수 없습니다",
|
||||||
|
NoData: "데이터가 없습니다",
|
||||||
|
Loading: "로딩 중",
|
||||||
|
|
||||||
|
SubTitle: (count: number) => `${count}개의 결과를 찾았습니다`,
|
||||||
|
},
|
||||||
|
Item: {
|
||||||
|
View: "보기",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Mask: {
|
Mask: {
|
||||||
Name: "마스크",
|
Name: "마스크",
|
||||||
Page: {
|
Page: {
|
||||||
Title: "프롬프트 템플릿",
|
Title: "프리셋 캐릭터 마스크",
|
||||||
SubTitle: (count: number) => `${count}개의 프롬프트 템플릿`,
|
SubTitle: (count: number) => `${count} 개의 프리셋 캐릭터 정의`,
|
||||||
Search: "템플릿 검색",
|
Search: "캐릭터 마스크 검색",
|
||||||
Create: "생성",
|
Create: "새로 만들기",
|
||||||
},
|
},
|
||||||
Item: {
|
Item: {
|
||||||
Info: (count: number) => `${count}개의 프롬프롬프트`,
|
Info: (count: number) => ` ${count} 개의 프리셋 대화 포함`,
|
||||||
Chat: "채팅",
|
Chat: "대화",
|
||||||
View: "보기",
|
View: "보기",
|
||||||
Edit: "편집",
|
Edit: "편집",
|
||||||
Delete: "삭제",
|
Delete: "삭제",
|
||||||
DeleteConfirm: "삭제하시겠습니까?",
|
DeleteConfirm: "삭제를 확인하시겠습니까?",
|
||||||
},
|
},
|
||||||
EditModal: {
|
EditModal: {
|
||||||
Title: (readonly: boolean) =>
|
Title: (readonly: boolean) =>
|
||||||
`프롬프트 템플릿 편집 ${readonly ? "(읽기 전용)" : ""}`,
|
`프리셋 마스크 편집 ${readonly ? "(읽기 전용)" : ""}`,
|
||||||
Download: "다운로드",
|
Download: "프리셋 다운로드",
|
||||||
Clone: "복제",
|
Clone: "프리셋 복제",
|
||||||
},
|
},
|
||||||
Config: {
|
Config: {
|
||||||
Avatar: "봇 아바타",
|
Avatar: "캐릭터 아바타",
|
||||||
Name: "봇 이름",
|
Name: "캐릭터 이름",
|
||||||
|
Sync: {
|
||||||
|
Title: "전역 설정 사용",
|
||||||
|
SubTitle: "현재 대화가 전역 모델 설정을 사용하는지 여부",
|
||||||
|
Confirm:
|
||||||
|
"현재 대화의 사용자 정의 설정이 자동으로 덮어쓰여질 것입니다. 전역 설정을 활성화하시겠습니까?",
|
||||||
|
},
|
||||||
|
HideContext: {
|
||||||
|
Title: "프리셋 대화 숨기기",
|
||||||
|
SubTitle: "숨기면 프리셋 대화가 채팅 화면에 나타나지 않습니다.",
|
||||||
|
},
|
||||||
|
Share: {
|
||||||
|
Title: "이 마스크 공유하기",
|
||||||
|
SubTitle: "이 마스크의 직접 링크 생성",
|
||||||
|
Action: "링크 복사",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
NewChat: {
|
NewChat: {
|
||||||
Return: "돌아가기",
|
Return: "돌아가기",
|
||||||
Skip: "건너뛰기",
|
Skip: "바로 시작",
|
||||||
Title: "마스크 선택",
|
NotShow: "다시 보지 않기",
|
||||||
SubTitle: "마스크 뒤의 영혼과 대화하세요",
|
|
||||||
More: "더 보기",
|
|
||||||
NotShow: "다시 표시하지 않음",
|
|
||||||
ConfirmNoShow:
|
ConfirmNoShow:
|
||||||
"비활성화하시겠습니까? 나중에 설정에서 다시 활성화할 수 있습니다.",
|
"비활성화하시겠습니까? 비활성화 후 언제든지 설정에서 다시 활성화할 수 있습니다.",
|
||||||
|
Title: "마스크 선택",
|
||||||
|
SubTitle: "지금 시작하여 마스크 뒤의 사고와 교류해보세요.",
|
||||||
|
More: "모두 보기",
|
||||||
|
},
|
||||||
|
|
||||||
|
URLCommand: {
|
||||||
|
Code: "링크에 액세스 코드가 포함되어 있습니다. 자동으로 입력하시겠습니까?",
|
||||||
|
Settings:
|
||||||
|
"링크에 프리셋 설정이 포함되어 있습니다. 자동으로 입력하시겠습니까?",
|
||||||
},
|
},
|
||||||
|
|
||||||
UI: {
|
UI: {
|
||||||
Confirm: "확인",
|
Confirm: "확인",
|
||||||
Cancel: "취소",
|
Cancel: "취소",
|
||||||
Close: "닫기",
|
Close: "닫기",
|
||||||
Create: "생성",
|
Create: "새로 만들기",
|
||||||
Edit: "편집",
|
Edit: "편집",
|
||||||
|
Export: "내보내기",
|
||||||
|
Import: "가져오기",
|
||||||
|
Sync: "동기화",
|
||||||
|
Config: "구성",
|
||||||
},
|
},
|
||||||
Exporter: {
|
Exporter: {
|
||||||
|
Description: {
|
||||||
|
Title: "컨텍스트가 지워진 후의 메시지만 표시됩니다.",
|
||||||
|
},
|
||||||
Model: "모델",
|
Model: "모델",
|
||||||
Messages: "메시지",
|
Messages: "메시지",
|
||||||
Topic: "주제",
|
Topic: "주제",
|
||||||
|
|
|
@ -1,70 +1,174 @@
|
||||||
import { SubmitKey } from "../store/config";
|
import { SubmitKey } from "../store/config";
|
||||||
import type { PartialLocaleType } from "./index";
|
import type { PartialLocaleType } from "./index";
|
||||||
|
import { getClientConfig } from "../config/client";
|
||||||
|
import { SAAS_CHAT_UTM_URL } from "@/app/constant";
|
||||||
|
const isApp = !!getClientConfig()?.isApp;
|
||||||
|
|
||||||
const no: PartialLocaleType = {
|
const no: PartialLocaleType = {
|
||||||
WIP: "Arbeid pågår ...",
|
WIP: "Arbeid pågår ...",
|
||||||
Error: {
|
Error: {
|
||||||
Unauthorized:
|
Unauthorized: isApp
|
||||||
"Du har ikke tilgang. [Vennlig oppgi tildelt adgangskode](/#/auth).",
|
? `😆 Samtalen har støtt på noen problemer, ikke bekymre deg:
|
||||||
|
\\ 1️⃣ Hvis du vil starte uten konfigurasjon, [klikk her for å begynne å chatte umiddelbart 🚀](${SAAS_CHAT_UTM_URL})
|
||||||
|
\\ 2️⃣ Hvis du vil bruke dine egne OpenAI-ressurser, klikk [her](/#/settings) for å endre innstillingene ⚙️`
|
||||||
|
: `😆 Samtalen har støtt på noen problemer, ikke bekymre deg:
|
||||||
|
\ 1️⃣ Hvis du vil starte uten konfigurasjon, [klikk her for å begynne å chatte umiddelbart 🚀](${SAAS_CHAT_UTM_URL})
|
||||||
|
\ 2️⃣ Hvis du bruker en privat distribusjonsversjon, klikk [her](/#/auth) for å skrive inn tilgangsnøkkelen 🔑
|
||||||
|
\ 3️⃣ Hvis du vil bruke dine egne OpenAI-ressurser, klikk [her](/#/settings) for å endre innstillingene ⚙️
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
Auth: {
|
||||||
|
Title: "Passord påkrevd",
|
||||||
|
Tips: "Administrator har aktivert passordbeskyttelse. Vennligst skriv inn tilgangskoden nedenfor",
|
||||||
|
SubTips: "Eller skriv inn din OpenAI eller Google API-nøkkel",
|
||||||
|
Input: "Skriv tilgangskoden her",
|
||||||
|
Confirm: "Bekreft",
|
||||||
|
Later: "Kom tilbake senere",
|
||||||
|
Return: "Tilbake",
|
||||||
|
SaasTips:
|
||||||
|
"Konfigurasjonen er for komplisert, jeg vil bruke det med en gang",
|
||||||
|
TopTips:
|
||||||
|
"🥳 NextChat AI lanseringstilbud, lås opp OpenAI o1, GPT-4o, Claude-3.5 og de nyeste store modellene nå",
|
||||||
},
|
},
|
||||||
ChatItem: {
|
ChatItem: {
|
||||||
ChatItemCount: (count: number) => `${count} meldinger`,
|
ChatItemCount: (count: number) => `${count} samtaler`,
|
||||||
},
|
},
|
||||||
Chat: {
|
Chat: {
|
||||||
SubTitle: (count: number) => `${count} meldinger med ChatGPT`,
|
SubTitle: (count: number) => `Totalt ${count} samtaler`,
|
||||||
|
EditMessage: {
|
||||||
|
Title: "Rediger meldingshistorikk",
|
||||||
|
Topic: {
|
||||||
|
Title: "Samtaleemne",
|
||||||
|
SubTitle: "Endre nåværende samtaleemne",
|
||||||
|
},
|
||||||
|
},
|
||||||
Actions: {
|
Actions: {
|
||||||
ChatList: "Gå til chatlisten",
|
ChatList: "Vis meldingsliste",
|
||||||
CompressedHistory: "Komprimert historikk for instrukser",
|
CompressedHistory: "Vis komprimert historikk",
|
||||||
Export: "Eksporter alle meldinger i markdown-format",
|
Export: "Eksporter samtaler",
|
||||||
Copy: "Kopier",
|
Copy: "Kopier",
|
||||||
Stop: "Stopp",
|
Stop: "Stopp",
|
||||||
Retry: "Prøv igjen",
|
Retry: "Prøv igjen",
|
||||||
|
Pin: "Fest",
|
||||||
|
PinToastContent:
|
||||||
|
"1 samtale er festet til forhåndsdefinerte oppfordringer",
|
||||||
|
PinToastAction: "Se",
|
||||||
Delete: "Slett",
|
Delete: "Slett",
|
||||||
|
Edit: "Rediger",
|
||||||
|
RefreshTitle: "Oppdater tittel",
|
||||||
|
RefreshToast: "Forespørsel om titteloppdatering sendt",
|
||||||
},
|
},
|
||||||
Rename: "Gi nytt navn",
|
Commands: {
|
||||||
Typing: "Skriver …",
|
new: "Ny samtale",
|
||||||
|
newm: "Start samtale fra maske",
|
||||||
|
next: "Neste samtale",
|
||||||
|
prev: "Forrige samtale",
|
||||||
|
clear: "Rydd kontekst",
|
||||||
|
del: "Slett samtale",
|
||||||
|
},
|
||||||
|
InputActions: {
|
||||||
|
Stop: "Stopp respons",
|
||||||
|
ToBottom: "Rull til nyeste",
|
||||||
|
Theme: {
|
||||||
|
auto: "Automatisk tema",
|
||||||
|
light: "Lyst tema",
|
||||||
|
dark: "Mørkt tema",
|
||||||
|
},
|
||||||
|
Prompt: "Hurtigkommando",
|
||||||
|
Masks: "Alle masker",
|
||||||
|
Clear: "Rydd samtale",
|
||||||
|
Settings: "Samtaleinnstillinger",
|
||||||
|
UploadImage: "Last opp bilde",
|
||||||
|
},
|
||||||
|
Rename: "Gi nytt navn til samtale",
|
||||||
|
Typing: "Skriver…",
|
||||||
Input: (submitKey: string) => {
|
Input: (submitKey: string) => {
|
||||||
var inputHints = `${submitKey} for å sende`;
|
var inputHints = `${submitKey} send`;
|
||||||
if (submitKey === String(SubmitKey.Enter)) {
|
if (submitKey === String(SubmitKey.Enter)) {
|
||||||
inputHints += ", Shift + Enter for å omgi";
|
inputHints += ",Shift + Enter for linjeskift";
|
||||||
}
|
}
|
||||||
return inputHints + ", / for å søke instrukser";
|
return (
|
||||||
|
inputHints + ",/ for å utløse autoutfylling, : for å utløse kommando"
|
||||||
|
);
|
||||||
},
|
},
|
||||||
Send: "Send",
|
Send: "Send",
|
||||||
|
Config: {
|
||||||
|
Reset: "Fjern minne",
|
||||||
|
SaveAs: "Lagre som maske",
|
||||||
|
},
|
||||||
|
IsContext: "Forhåndsdefinerte oppfordringer",
|
||||||
},
|
},
|
||||||
Export: {
|
Export: {
|
||||||
Title: "Alle meldinger",
|
Title: "Del samtalehistorikk",
|
||||||
Copy: "Kopiere alle",
|
Copy: "Kopier alt",
|
||||||
Download: "Last ned",
|
Download: "Last ned fil",
|
||||||
MessageFromYou: "Melding fra deg",
|
Share: "Del til ShareGPT",
|
||||||
MessageFromChatGPT: "Melding fra ChatGPT",
|
MessageFromYou: "Bruker",
|
||||||
|
MessageFromChatGPT: "ChatGPT",
|
||||||
|
Format: {
|
||||||
|
Title: "Eksporterformat",
|
||||||
|
SubTitle: "Kan eksporteres som Markdown-tekst eller PNG-bilde",
|
||||||
|
},
|
||||||
|
IncludeContext: {
|
||||||
|
Title: "Inkluder maske kontekst",
|
||||||
|
SubTitle: "Skal maske kontekst vises i meldinger",
|
||||||
|
},
|
||||||
|
Steps: {
|
||||||
|
Select: "Velg",
|
||||||
|
Preview: "Forhåndsvis",
|
||||||
|
},
|
||||||
|
Image: {
|
||||||
|
Toast: "Genererer skjermbilde",
|
||||||
|
Modal: "Langtrykk eller høyreklikk for å lagre bilde",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Select: {
|
||||||
|
Search: "Søk meldinger",
|
||||||
|
All: "Velg alle",
|
||||||
|
Latest: "Siste meldinger",
|
||||||
|
Clear: "Fjern valg",
|
||||||
},
|
},
|
||||||
Memory: {
|
Memory: {
|
||||||
Title: "Minneinstruks",
|
Title: "Historisk sammendrag",
|
||||||
EmptyContent: "Ingen sålant.",
|
EmptyContent: "Samtaleinnholdet er for kort, ingen oppsummering nødvendig",
|
||||||
Send: "Send minne",
|
Send: "Automatisk komprimere samtalehistorikk og sende som kontekst",
|
||||||
Copy: "Kopiere minne",
|
Copy: "Kopier sammendrag",
|
||||||
Reset: "Nulstill sesjon",
|
Reset: "[unused]",
|
||||||
ResetConfirm:
|
ResetConfirm: "Er du sikker på at du vil tømme historisk sammendrag?",
|
||||||
"Om du nillstiller vil du slette hele historikken. Er du sikker på at du vil nullstille?",
|
|
||||||
},
|
},
|
||||||
Home: {
|
Home: {
|
||||||
NewChat: "Ny chat",
|
NewChat: "Ny samtale",
|
||||||
DeleteChat: "Bekreft for å slette det valgte dialogen",
|
DeleteChat: "Er du sikker på at du vil slette den valgte samtalen?",
|
||||||
DeleteToast: "Samtale slettet",
|
DeleteToast: "Samtale slettet",
|
||||||
Revert: "Tilbakestill",
|
Revert: "Angre",
|
||||||
},
|
},
|
||||||
Settings: {
|
Settings: {
|
||||||
Title: "Innstillinger",
|
Title: "Innstillinger",
|
||||||
SubTitle: "Alle innstillinger",
|
SubTitle: "Alle innstillingsalternativer",
|
||||||
|
|
||||||
|
Danger: {
|
||||||
|
Reset: {
|
||||||
|
Title: "Tilbakestill alle innstillinger",
|
||||||
|
SubTitle:
|
||||||
|
"Tilbakestill alle innstillingsalternativer til standardverdier",
|
||||||
|
Action: "Tilbakestill nå",
|
||||||
|
Confirm: "Bekreft tilbakestilling av alle innstillinger?",
|
||||||
|
},
|
||||||
|
Clear: {
|
||||||
|
Title: "Slett alle data",
|
||||||
|
SubTitle: "Slett alle samtaler og innstillingsdata",
|
||||||
|
Action: "Slett nå",
|
||||||
|
Confirm: "Bekreft sletting av alle samtaler og innstillingsdata?",
|
||||||
|
},
|
||||||
|
},
|
||||||
Lang: {
|
Lang: {
|
||||||
Name: "Language", // ATTENTION: if you wanna add a new translation, please do not translate this value, leave it as `Language`
|
Name: "Language", // ATTENTION: if you wanna add a new translation, please do not translate this value, leave it as `Language`
|
||||||
|
All: "Alle språk",
|
||||||
},
|
},
|
||||||
Avatar: "Avatar",
|
Avatar: "Profilbilde",
|
||||||
FontSize: {
|
FontSize: {
|
||||||
Title: "Fontstørrelsen",
|
Title: "Skriftstørrelse",
|
||||||
SubTitle: "Juster fontstørrelsen for samtaleinnholdet.",
|
SubTitle: "Skriftstørrelse for samtaleinnhold",
|
||||||
},
|
},
|
||||||
FontFamily: {
|
FontFamily: {
|
||||||
Title: "Chat-skrifttype",
|
Title: "Chat-skrifttype",
|
||||||
|
@ -73,92 +177,421 @@ const no: PartialLocaleType = {
|
||||||
Placeholder: "Skriftnavn",
|
Placeholder: "Skriftnavn",
|
||||||
},
|
},
|
||||||
InjectSystemPrompts: {
|
InjectSystemPrompts: {
|
||||||
Title: "Sett inn systemprompter",
|
Title: "Injiser systemprompter",
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Tving tillegg av en simulert ChatGPT-systemprompt i begynnelsen av meldingslisten for hver forespørsel",
|
"Tving inn et systemprompt som simulerer ChatGPT i starten av hver forespørsel",
|
||||||
},
|
},
|
||||||
|
InputTemplate: {
|
||||||
|
Title: "Forhåndsbehandling av brukerinput",
|
||||||
|
SubTitle:
|
||||||
|
"Den nyeste meldingen fra brukeren vil bli fylt ut i denne malen",
|
||||||
|
},
|
||||||
|
|
||||||
Update: {
|
Update: {
|
||||||
Version: (x: string) => `Versjon: ${x}`,
|
Version: (x: string) => `Nåværende versjon: ${x}`,
|
||||||
IsLatest: "Siste versjon",
|
IsLatest: "Er den nyeste versjonen",
|
||||||
CheckUpdate: "Se etter oppdatering",
|
CheckUpdate: "Sjekk oppdateringer",
|
||||||
IsChecking: "Ser etter oppdatering ...",
|
IsChecking: "Sjekker oppdateringer...",
|
||||||
FoundUpdate: (x: string) => `Fant ny versjon: ${x}`,
|
FoundUpdate: (x: string) => `Ny versjon oppdaget: ${x}`,
|
||||||
GoToUpdate: "Oppdater",
|
GoToUpdate: "Gå til oppdatering",
|
||||||
},
|
},
|
||||||
SendKey: "Send nøkkel",
|
SendKey: "Send-knapp",
|
||||||
Theme: "Tema",
|
Theme: "Tema",
|
||||||
TightBorder: "Stram innramming",
|
TightBorder: "Ingen ramme-modus",
|
||||||
|
SendPreviewBubble: {
|
||||||
|
Title: "Forhåndsvisningsboble",
|
||||||
|
SubTitle: "Forhåndsvis Markdown-innhold i en forhåndsvisningsboble",
|
||||||
|
},
|
||||||
|
AutoGenerateTitle: {
|
||||||
|
Title: "Automatisk generere tittel",
|
||||||
|
SubTitle: "Generer en passende tittel basert på samtaleinnholdet",
|
||||||
|
},
|
||||||
|
Sync: {
|
||||||
|
CloudState: "Skydatasynkronisering",
|
||||||
|
NotSyncYet: "Har ikke blitt synkronisert ennå",
|
||||||
|
Success: "Synkronisering vellykket",
|
||||||
|
Fail: "Synkronisering mislyktes",
|
||||||
|
|
||||||
|
Config: {
|
||||||
|
Modal: {
|
||||||
|
Title: "Konfigurer sky-synkronisering",
|
||||||
|
Check: "Sjekk tilgjengelighet",
|
||||||
|
},
|
||||||
|
SyncType: {
|
||||||
|
Title: "Synkroniseringstype",
|
||||||
|
SubTitle: "Velg ønsket synkroniseringsserver",
|
||||||
|
},
|
||||||
|
Proxy: {
|
||||||
|
Title: "Aktiver proxy",
|
||||||
|
SubTitle:
|
||||||
|
"Når du synkroniserer i nettleseren, må proxy være aktivert for å unngå CORS-restriksjoner",
|
||||||
|
},
|
||||||
|
ProxyUrl: {
|
||||||
|
Title: "Proxy-URL",
|
||||||
|
SubTitle: "Kun for prosjektets innebygde CORS-proxy",
|
||||||
|
},
|
||||||
|
|
||||||
|
WebDav: {
|
||||||
|
Endpoint: "WebDAV-adresse",
|
||||||
|
UserName: "Brukernavn",
|
||||||
|
Password: "Passord",
|
||||||
|
},
|
||||||
|
|
||||||
|
UpStash: {
|
||||||
|
Endpoint: "UpStash Redis REST-URL",
|
||||||
|
UserName: "Sikkerhetskopinavn",
|
||||||
|
Password: "UpStash Redis REST-token",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
LocalState: "Lokal data",
|
||||||
|
Overview: (overview: any) => {
|
||||||
|
return `${overview.chat} samtaler, ${overview.message} meldinger, ${overview.prompt} oppfordringer, ${overview.mask} masker`;
|
||||||
|
},
|
||||||
|
ImportFailed: "Import mislyktes",
|
||||||
|
},
|
||||||
|
Mask: {
|
||||||
|
Splash: {
|
||||||
|
Title: "Maskestartside",
|
||||||
|
SubTitle: "Vis maskestartside når du oppretter en ny samtale",
|
||||||
|
},
|
||||||
|
Builtin: {
|
||||||
|
Title: "Skjul innebygde masker",
|
||||||
|
SubTitle: "Skjul innebygde masker i alle maskelister",
|
||||||
|
},
|
||||||
|
},
|
||||||
Prompt: {
|
Prompt: {
|
||||||
Disable: {
|
Disable: {
|
||||||
Title: "Skru av autofullfør",
|
Title: "Deaktiver automatisk fullføring av oppfordringer",
|
||||||
SubTitle: "Skriv / for å trigge autofullfør",
|
SubTitle: "Skriv / i tekstboksen for å utløse automatisk fullføring",
|
||||||
},
|
},
|
||||||
List: "Instruksliste",
|
List: "Egendefinerte oppfordringer",
|
||||||
ListCount: (builtin: number, custom: number) =>
|
ListCount: (builtin: number, custom: number) =>
|
||||||
`${builtin} innebygde, ${custom} brukerdefinerte`,
|
`Innebygde ${builtin}, brukerdedefinerte ${custom}`,
|
||||||
Edit: "Endre",
|
Edit: "Rediger",
|
||||||
Modal: {
|
Modal: {
|
||||||
Title: "Instruksliste",
|
Title: "Oppfordringsliste",
|
||||||
Add: "Legg til",
|
Add: "Ny",
|
||||||
Search: "Søk instrukser",
|
Search: "Søk oppfordringer",
|
||||||
|
},
|
||||||
|
EditModal: {
|
||||||
|
Title: "Rediger oppfordring",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
HistoryCount: {
|
HistoryCount: {
|
||||||
Title: "Tall på tilhørende meldinger",
|
Title: "Antall historiske meldinger",
|
||||||
SubTitle: "Antall sendte meldinger tilknyttet hver spørring",
|
SubTitle: "Antall historiske meldinger som sendes med hver forespørsel",
|
||||||
},
|
},
|
||||||
CompressThreshold: {
|
CompressThreshold: {
|
||||||
Title: "Terskeverdi for komprimering av historikk",
|
Title: "Kompressterskel for historiske meldinger",
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Komprimer dersom ikke-komprimert lengde på meldinger overskrider denne verdien",
|
"Når ukomprimerte historiske meldinger overskrider denne verdien, vil de bli komprimert",
|
||||||
},
|
},
|
||||||
|
|
||||||
Usage: {
|
Usage: {
|
||||||
Title: "Saldo for konto",
|
Title: "Saldoforespørsel",
|
||||||
SubTitle(used: any, total: any) {
|
SubTitle(used: any, total: any) {
|
||||||
return `Brukt denne måneden $${used}, abonnement $${total}`;
|
return `Brukt denne måneden $${used}, total abonnementsbeløp $${total}`;
|
||||||
},
|
},
|
||||||
IsChecking: "Sjekker ...",
|
IsChecking: "Sjekker...",
|
||||||
Check: "Sjekk",
|
Check: "Sjekk på nytt",
|
||||||
NoAccess: "Skriv inn API-nøkkelen for å sjekke saldo",
|
NoAccess: "Skriv inn API-nøkkel eller tilgangspassord for å se saldo",
|
||||||
},
|
},
|
||||||
|
|
||||||
Model: "Model",
|
Access: {
|
||||||
|
SaasStart: {
|
||||||
|
Title: "Bruk NextChat AI",
|
||||||
|
Label: "(Den mest kostnadseffektive løsningen)",
|
||||||
|
SubTitle:
|
||||||
|
"Offisielt vedlikeholdt av NextChat, klar til bruk uten konfigurasjon, støtter de nyeste store modellene som OpenAI o1, GPT-4o og Claude-3.5",
|
||||||
|
ChatNow: "Chat nå",
|
||||||
|
},
|
||||||
|
|
||||||
|
AccessCode: {
|
||||||
|
Title: "Adgangskode",
|
||||||
|
SubTitle: "Administrator har aktivert kryptert tilgang",
|
||||||
|
Placeholder: "Skriv inn tilgangskoden",
|
||||||
|
},
|
||||||
|
CustomEndpoint: {
|
||||||
|
Title: "Egendefinert API",
|
||||||
|
SubTitle: "Bruk egendefinerte Azure- eller OpenAI-tjenester",
|
||||||
|
},
|
||||||
|
Provider: {
|
||||||
|
Title: "Modelltilbyder",
|
||||||
|
SubTitle: "Bytt til forskjellige tilbydere",
|
||||||
|
},
|
||||||
|
OpenAI: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "API-nøkkel",
|
||||||
|
SubTitle:
|
||||||
|
"Bruk egendefinert OpenAI-nøkkel for å omgå passordtilgangsbegrensninger",
|
||||||
|
Placeholder: "OpenAI API-nøkkel",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "API-adresse",
|
||||||
|
SubTitle: "Må inkludere http(s):// utenom standardadresse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Azure: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "API-nøkkel",
|
||||||
|
SubTitle:
|
||||||
|
"Bruk egendefinert Azure-nøkkel for å omgå passordtilgangsbegrensninger",
|
||||||
|
Placeholder: "Azure API-nøkkel",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "API-adresse",
|
||||||
|
SubTitle: "Eksempel:",
|
||||||
|
},
|
||||||
|
|
||||||
|
ApiVerion: {
|
||||||
|
Title: "API-versjon (azure api versjon)",
|
||||||
|
SubTitle: "Velg en spesifikk delversjon",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Anthropic: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "API-nøkkel",
|
||||||
|
SubTitle:
|
||||||
|
"Bruk egendefinert Anthropic-nøkkel for å omgå passordtilgangsbegrensninger",
|
||||||
|
Placeholder: "Anthropic API-nøkkel",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "API-adresse",
|
||||||
|
SubTitle: "Eksempel:",
|
||||||
|
},
|
||||||
|
|
||||||
|
ApiVerion: {
|
||||||
|
Title: "API-versjon (claude api versjon)",
|
||||||
|
SubTitle: "Velg en spesifikk API-versjon",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Google: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "API-nøkkel",
|
||||||
|
SubTitle: "Hent din API-nøkkel fra Google AI",
|
||||||
|
Placeholder: "Skriv inn din Google AI Studio API-nøkkel",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Endepunktadresse",
|
||||||
|
SubTitle: "Eksempel:",
|
||||||
|
},
|
||||||
|
|
||||||
|
ApiVersion: {
|
||||||
|
Title: "API-versjon (kun for gemini-pro)",
|
||||||
|
SubTitle: "Velg en spesifikk API-versjon",
|
||||||
|
},
|
||||||
|
GoogleSafetySettings: {
|
||||||
|
Title: "Google sikkerhetsfiltreringsnivå",
|
||||||
|
SubTitle: "Sett innholdsfiltreringsnivå",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Baidu: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "API-nøkkel",
|
||||||
|
SubTitle: "Bruk egendefinert Baidu API-nøkkel",
|
||||||
|
Placeholder: "Baidu API-nøkkel",
|
||||||
|
},
|
||||||
|
SecretKey: {
|
||||||
|
Title: "Hemmelig nøkkel",
|
||||||
|
SubTitle: "Bruk egendefinert Baidu hemmelig nøkkel",
|
||||||
|
Placeholder: "Baidu hemmelig nøkkel",
|
||||||
|
},
|
||||||
|
Endpoint: {
|
||||||
|
Title: "API-adresse",
|
||||||
|
SubTitle:
|
||||||
|
"Støtter ikke egendefinerte konfigurasjoner. Se .env-konfigurasjon.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ByteDance: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "API-nøkkel",
|
||||||
|
SubTitle: "Bruk egendefinert ByteDance API-nøkkel",
|
||||||
|
Placeholder: "ByteDance API-nøkkel",
|
||||||
|
},
|
||||||
|
Endpoint: {
|
||||||
|
Title: "API-adresse",
|
||||||
|
SubTitle: "Eksempel:",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Alibaba: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "API-nøkkel",
|
||||||
|
SubTitle: "Bruk egendefinert Alibaba Cloud API-nøkkel",
|
||||||
|
Placeholder: "Alibaba Cloud API-nøkkel",
|
||||||
|
},
|
||||||
|
Endpoint: {
|
||||||
|
Title: "API-adresse",
|
||||||
|
SubTitle: "Eksempel:",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CustomModel: {
|
||||||
|
Title: "Egendefinert modellnavn",
|
||||||
|
SubTitle: "Legg til egendefinerte modellalternativer, skill med komma",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
Model: "Modell",
|
||||||
|
CompressModel: {
|
||||||
|
Title: "Komprimeringsmodell",
|
||||||
|
SubTitle: "Modell brukt for å komprimere historikken",
|
||||||
|
},
|
||||||
Temperature: {
|
Temperature: {
|
||||||
Title: "Temperatur",
|
Title: "Tilfeldighet (temperature)",
|
||||||
SubTitle: "Høyere verdi gir mer kreative svar",
|
SubTitle: "Høyere verdi gir mer tilfeldige svar",
|
||||||
|
},
|
||||||
|
TopP: {
|
||||||
|
Title: "Kjerneprøvetaking (top_p)",
|
||||||
|
SubTitle:
|
||||||
|
"Ligner på tilfeldighet, men endre ikke sammen med tilfeldighet",
|
||||||
},
|
},
|
||||||
MaxTokens: {
|
MaxTokens: {
|
||||||
Title: "Maks tokens",
|
Title: "Maksimalt antall tokens per svar (max_tokens)",
|
||||||
SubTitle: "Maksimum lengde på tokens for instrukser og svar",
|
SubTitle: "Maksimalt antall tokens brukt i en enkelt interaksjon",
|
||||||
|
},
|
||||||
|
PresencePenalty: {
|
||||||
|
Title: "Emnens ferskhet (presence_penalty)",
|
||||||
|
SubTitle:
|
||||||
|
"Høyere verdi gir større sannsynlighet for å utvide til nye emner",
|
||||||
|
},
|
||||||
|
FrequencyPenalty: {
|
||||||
|
Title: "Frekvensstraff (frequency_penalty)",
|
||||||
|
SubTitle:
|
||||||
|
"Høyere verdi gir større sannsynlighet for å redusere gjentakende ord",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Store: {
|
Store: {
|
||||||
DefaultTopic: "Ny samtale",
|
DefaultTopic: "Ny samtale",
|
||||||
BotHello: "Hei! Hva kan jeg hjelpe deg med i dag?",
|
BotHello: "Hva kan jeg hjelpe deg med?",
|
||||||
Error: "Noe gikk galt, vennligst prøv igjen senere.",
|
Error: "Noe gikk galt, prøv igjen senere",
|
||||||
Prompt: {
|
Prompt: {
|
||||||
History: (content: string) =>
|
History: (content: string) =>
|
||||||
"Dette er et sammendrag av chatthistorikken mellom AI-en og brukeren som en oppsummering: " +
|
"Dette er oppsummeringen av historiske samtaler som bakgrunn:" +
|
||||||
content,
|
content,
|
||||||
Topic:
|
Topic:
|
||||||
"Vennligst lag en fire til fem ords tittel som oppsummerer samtalen vår uten innledning, punktsetting, anførselstegn, punktum, symboler eller tillegg tekst. Fjern innrammende anførselstegn.",
|
'Bruk fire til fem ord for å returnere en kort oppsummering av temaet, uten forklaring, uten tegnsetting, uten fyllord, uten ekstra tekst, uten fet skrift. Hvis det ikke er noe tema, returner bare "sladder".',
|
||||||
Summarize:
|
Summarize:
|
||||||
"Oppsummer diskusjonen vår kort i 200 ord eller mindre for å bruke som en oppfordring til fremtidig sammenheng.",
|
"Oppsummer samtalen kort som en kontekstuell prompt for fremtidige referanser, hold det innen 200 tegn",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Copy: {
|
Copy: {
|
||||||
Success: "Kopiert til utklippstavle",
|
Success: "Kopiert til utklippstavlen",
|
||||||
Failed: "Kopiering feilet. Vennligst gi tilgang til utklippstavlen.",
|
Failed: "Kopiering mislyktes, vennligst gi tillatelse til utklippstavlen",
|
||||||
|
},
|
||||||
|
Download: {
|
||||||
|
Success: "Innholdet er lastet ned til katalogen din.",
|
||||||
|
Failed: "Nedlasting mislyktes.",
|
||||||
},
|
},
|
||||||
Context: {
|
Context: {
|
||||||
Toast: (x: any) => `Med ${x} kontekstuelle instrukser`,
|
Toast: (x: any) => `Inneholder ${x} forhåndsinnstilte oppfordringer`,
|
||||||
Edit: "Kontekstuelle -og minneinstrukser",
|
Edit: "Nåværende samtaleinnstillinger",
|
||||||
Add: "Legg til",
|
Add: "Legg til en ny samtale",
|
||||||
|
Clear: "Konteksten er tømt",
|
||||||
|
Revert: "Gjenopprett konteksten",
|
||||||
|
},
|
||||||
|
Plugin: {
|
||||||
|
Name: "Plugin",
|
||||||
|
},
|
||||||
|
FineTuned: {
|
||||||
|
Sysmessage: "Du er en assistent",
|
||||||
|
},
|
||||||
|
SearchChat: {
|
||||||
|
Name: "Søk",
|
||||||
|
Page: {
|
||||||
|
Title: "Søk i chatthistorikk",
|
||||||
|
Search: "Skriv inn søkeord",
|
||||||
|
NoResult: "Ingen resultater funnet",
|
||||||
|
NoData: "Ingen data",
|
||||||
|
Loading: "Laster inn",
|
||||||
|
|
||||||
|
SubTitle: (count: number) => `Fant ${count} resultater`,
|
||||||
|
},
|
||||||
|
Item: {
|
||||||
|
View: "Vis",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Mask: {
|
||||||
|
Name: "Maske",
|
||||||
|
Page: {
|
||||||
|
Title: "Forhåndsdefinerte rollemasker",
|
||||||
|
SubTitle: (count: number) =>
|
||||||
|
`${count} forhåndsdefinerte rolledefinisjoner`,
|
||||||
|
Search: "Søk etter rollemasker",
|
||||||
|
Create: "Opprett ny",
|
||||||
|
},
|
||||||
|
Item: {
|
||||||
|
Info: (count: number) => `Inneholder ${count} forhåndsdefinerte samtaler`,
|
||||||
|
Chat: "Samtale",
|
||||||
|
View: "Vis",
|
||||||
|
Edit: "Rediger",
|
||||||
|
Delete: "Slett",
|
||||||
|
DeleteConfirm: "Bekreft sletting?",
|
||||||
|
},
|
||||||
|
EditModal: {
|
||||||
|
Title: (readonly: boolean) =>
|
||||||
|
`Rediger forhåndsdefinert maske ${readonly ? "(kun lesing)" : ""}`,
|
||||||
|
Download: "Last ned forhåndsdefinert",
|
||||||
|
Clone: "Kopier forhåndsdefinert",
|
||||||
|
},
|
||||||
|
Config: {
|
||||||
|
Avatar: "Rollebilde",
|
||||||
|
Name: "Rolle navn",
|
||||||
|
Sync: {
|
||||||
|
Title: "Bruk globale innstillinger",
|
||||||
|
SubTitle: "Bruker nåværende samtale globale modellinnstillinger",
|
||||||
|
Confirm:
|
||||||
|
"De tilpassede innstillingene for den nåværende samtalen vil bli overskrevet automatisk. Bekreft at du vil bruke globale innstillinger?",
|
||||||
|
},
|
||||||
|
HideContext: {
|
||||||
|
Title: "Skjul forhåndsdefinerte samtaler",
|
||||||
|
SubTitle:
|
||||||
|
"Når skjult, vil forhåndsdefinerte samtaler ikke vises i chat-grensesnittet",
|
||||||
|
},
|
||||||
|
Share: {
|
||||||
|
Title: "Del denne masken",
|
||||||
|
SubTitle: "Generer en direkte lenke til denne masken",
|
||||||
|
Action: "Kopier lenke",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
NewChat: {
|
||||||
|
Return: "Tilbake",
|
||||||
|
Skip: "Start direkte",
|
||||||
|
NotShow: "Vis ikke igjen",
|
||||||
|
ConfirmNoShow:
|
||||||
|
"Bekreft deaktivering? Du kan aktivere det igjen i innstillingene når som helst.",
|
||||||
|
Title: "Velg en maske",
|
||||||
|
SubTitle: "Start nå, og få tanker som kolliderer med sjelen bak masken",
|
||||||
|
More: "Se alle",
|
||||||
|
},
|
||||||
|
|
||||||
|
URLCommand: {
|
||||||
|
Code: "Oppdaget at lenken allerede inneholder tilgangskode. Ønsker du å fylle den ut automatisk?",
|
||||||
|
Settings:
|
||||||
|
"Oppdaget at lenken inneholder forhåndsinnstillinger. Ønsker du å fylle dem ut automatisk?",
|
||||||
|
},
|
||||||
|
|
||||||
|
UI: {
|
||||||
|
Confirm: "Bekreft",
|
||||||
|
Cancel: "Avbryt",
|
||||||
|
Close: "Lukk",
|
||||||
|
Create: "Opprett",
|
||||||
|
Edit: "Rediger",
|
||||||
|
Export: "Eksporter",
|
||||||
|
Import: "Importer",
|
||||||
|
Sync: "Synkroniser",
|
||||||
|
Config: "Konfigurer",
|
||||||
},
|
},
|
||||||
Exporter: {
|
Exporter: {
|
||||||
Model: "Model",
|
Description: {
|
||||||
Messages: "Meldingar",
|
Title: "Bare meldinger etter at konteksten er ryddet vil bli vist",
|
||||||
|
},
|
||||||
|
Model: "Modell",
|
||||||
|
Messages: "Meldinger",
|
||||||
Topic: "Emne",
|
Topic: "Emne",
|
||||||
Time: "Tid",
|
Time: "Tid",
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,15 +1,21 @@
|
||||||
import { SubmitKey } from "../store/config";
|
import { SubmitKey } from "../store/config";
|
||||||
import { PartialLocaleType } from "../locales/index";
|
import { PartialLocaleType } from "../locales/index";
|
||||||
import { getClientConfig } from "../config/client";
|
import { getClientConfig } from "../config/client";
|
||||||
|
import { SAAS_CHAT_UTM_URL } from "@/app/constant";
|
||||||
const isApp = !!getClientConfig()?.isApp;
|
const isApp = !!getClientConfig()?.isApp;
|
||||||
|
|
||||||
const pt: PartialLocaleType = {
|
const pt: PartialLocaleType = {
|
||||||
WIP: "Em breve...",
|
WIP: "Em breve...",
|
||||||
Error: {
|
Error: {
|
||||||
Unauthorized: isApp
|
Unauthorized: isApp
|
||||||
? "Chave API inválida, por favor verifique em [Configurações](/#/settings)."
|
? `😆 A conversa encontrou alguns problemas, não se preocupe:
|
||||||
: "Acesso não autorizado, por favor insira o código de acesso em [auth](/#/auth) ou insira sua Chave API OpenAI.",
|
\\ 1️⃣ Se você quiser começar sem configuração, [clique aqui para começar a conversar imediatamente 🚀](${SAAS_CHAT_UTM_URL})
|
||||||
|
\\ 2️⃣ Se você deseja usar seus próprios recursos OpenAI, clique [aqui](/#/settings) para modificar as configurações ⚙️`
|
||||||
|
: `😆 A conversa encontrou alguns problemas, não se preocupe:
|
||||||
|
\ 1️⃣ Se você quiser começar sem configuração, [clique aqui para começar a conversar imediatamente 🚀](${SAAS_CHAT_UTM_URL})
|
||||||
|
\ 2️⃣ Se você estiver usando uma versão de implantação privada, clique [aqui](/#/auth) para inserir a chave de acesso 🔑
|
||||||
|
\ 3️⃣ Se você deseja usar seus próprios recursos OpenAI, clique [aqui](/#/settings) para modificar as configurações ⚙️
|
||||||
|
`,
|
||||||
},
|
},
|
||||||
Auth: {
|
Auth: {
|
||||||
Title: "Necessário Código de Acesso",
|
Title: "Necessário Código de Acesso",
|
||||||
|
@ -18,6 +24,10 @@ const pt: PartialLocaleType = {
|
||||||
Input: "código de acesso",
|
Input: "código de acesso",
|
||||||
Confirm: "Confirmar",
|
Confirm: "Confirmar",
|
||||||
Later: "Depois",
|
Later: "Depois",
|
||||||
|
Return: "Voltar",
|
||||||
|
SaasTips: "A configuração é muito complicada, quero usá-la imediatamente",
|
||||||
|
TopTips:
|
||||||
|
"🥳 Oferta de Lançamento do NextChat AI, desbloqueie o OpenAI o1, GPT-4o, Claude-3.5 e os mais recentes grandes modelos agora",
|
||||||
},
|
},
|
||||||
ChatItem: {
|
ChatItem: {
|
||||||
ChatItemCount: (count: number) => `${count} mensagens`,
|
ChatItemCount: (count: number) => `${count} mensagens`,
|
||||||
|
@ -43,6 +53,8 @@ const pt: PartialLocaleType = {
|
||||||
PinToastAction: "Visualizar",
|
PinToastAction: "Visualizar",
|
||||||
Delete: "Deletar",
|
Delete: "Deletar",
|
||||||
Edit: "Editar",
|
Edit: "Editar",
|
||||||
|
RefreshTitle: "Atualizar Título",
|
||||||
|
RefreshToast: "Solicitação de atualização de título enviada",
|
||||||
},
|
},
|
||||||
Commands: {
|
Commands: {
|
||||||
new: "Iniciar um novo chat",
|
new: "Iniciar um novo chat",
|
||||||
|
@ -279,6 +291,14 @@ const pt: PartialLocaleType = {
|
||||||
NoAccess: "Insira a Chave API para verificar o saldo",
|
NoAccess: "Insira a Chave API para verificar o saldo",
|
||||||
},
|
},
|
||||||
Access: {
|
Access: {
|
||||||
|
SaasStart: {
|
||||||
|
Title: "Usar NextChat AI",
|
||||||
|
Label: "(A solução mais econômica)",
|
||||||
|
SubTitle:
|
||||||
|
"Mantido oficialmente pelo NextChat, pronto para uso sem configuração, suporta os mais recentes grandes modelos como OpenAI o1, GPT-4o e Claude-3.5",
|
||||||
|
ChatNow: "Conversar agora",
|
||||||
|
},
|
||||||
|
|
||||||
AccessCode: {
|
AccessCode: {
|
||||||
Title: "Código de Acesso",
|
Title: "Código de Acesso",
|
||||||
SubTitle: "Controle de Acesso Habilitado",
|
SubTitle: "Controle de Acesso Habilitado",
|
||||||
|
@ -346,6 +366,10 @@ const pt: PartialLocaleType = {
|
||||||
},
|
},
|
||||||
|
|
||||||
Model: "Modelo",
|
Model: "Modelo",
|
||||||
|
CompressModel: {
|
||||||
|
Title: "Modelo de Compressão",
|
||||||
|
SubTitle: "Modelo usado para comprimir o histórico",
|
||||||
|
},
|
||||||
Temperature: {
|
Temperature: {
|
||||||
Title: "Temperatura",
|
Title: "Temperatura",
|
||||||
SubTitle: "Um valor maior torna a saída mais aleatória",
|
SubTitle: "Um valor maior torna a saída mais aleatória",
|
||||||
|
@ -405,6 +429,21 @@ const pt: PartialLocaleType = {
|
||||||
FineTuned: {
|
FineTuned: {
|
||||||
Sysmessage: "Você é um assistente que",
|
Sysmessage: "Você é um assistente que",
|
||||||
},
|
},
|
||||||
|
SearchChat: {
|
||||||
|
Name: "Pesquisar",
|
||||||
|
Page: {
|
||||||
|
Title: "Pesquisar histórico de chat",
|
||||||
|
Search: "Digite palavras-chave para pesquisa",
|
||||||
|
NoResult: "Nenhum resultado encontrado",
|
||||||
|
NoData: "Sem dados",
|
||||||
|
Loading: "Carregando",
|
||||||
|
|
||||||
|
SubTitle: (count: number) => `Encontrado ${count} resultados`,
|
||||||
|
},
|
||||||
|
Item: {
|
||||||
|
View: "Ver",
|
||||||
|
},
|
||||||
|
},
|
||||||
Mask: {
|
Mask: {
|
||||||
Name: "Máscara",
|
Name: "Máscara",
|
||||||
Page: {
|
Page: {
|
||||||
|
|
|
@ -1,75 +1,169 @@
|
||||||
import { SubmitKey } from "../store/config";
|
import { SubmitKey } from "../store/config";
|
||||||
import type { PartialLocaleType } from "./index";
|
import { PartialLocaleType } from "../locales/index";
|
||||||
|
import { getClientConfig } from "../config/client";
|
||||||
|
import { SAAS_CHAT_UTM_URL } from "@/app/constant";
|
||||||
|
const isApp = !!getClientConfig()?.isApp;
|
||||||
|
|
||||||
const ru: PartialLocaleType = {
|
const ru: PartialLocaleType = {
|
||||||
WIP: "Скоро...",
|
WIP: "Скоро...",
|
||||||
Error: {
|
Error: {
|
||||||
Unauthorized:
|
Unauthorized: isApp
|
||||||
"Несанкционированный доступ. Пожалуйста, введите код доступа на [странице](/#/auth) настроек.",
|
? `😆 В разговоре возникли некоторые проблемы, не переживайте:
|
||||||
|
\\ 1️⃣ Если вы хотите начать без настройки, [нажмите здесь, чтобы немедленно начать разговор 🚀](${SAAS_CHAT_UTM_URL})
|
||||||
|
\\ 2️⃣ Если вы хотите использовать свои ресурсы OpenAI, нажмите [здесь](/#/settings), чтобы изменить настройки ⚙️`
|
||||||
|
: `😆 В разговоре возникли некоторые проблемы, не переживайте:
|
||||||
|
\ 1️⃣ Если вы хотите начать без настройки, [нажмите здесь, чтобы немедленно начать разговор 🚀](${SAAS_CHAT_UTM_URL})
|
||||||
|
\ 2️⃣ Если вы используете частную версию развертывания, нажмите [здесь](/#/auth), чтобы ввести ключ доступа 🔑
|
||||||
|
\ 3️⃣ Если вы хотите использовать свои ресурсы OpenAI, нажмите [здесь](/#/settings), чтобы изменить настройки ⚙️
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
Auth: {
|
||||||
|
Title: "Требуется пароль",
|
||||||
|
Tips: "Администратор включил проверку пароля. Пожалуйста, введите код доступа ниже",
|
||||||
|
SubTips: "Или введите ваш API-ключ OpenAI или Google",
|
||||||
|
Input: "Введите код доступа здесь",
|
||||||
|
Confirm: "Подтвердить",
|
||||||
|
Later: "Позже",
|
||||||
|
Return: "Назад",
|
||||||
|
SaasTips: "Настройка слишком сложна, я хочу использовать это немедленно",
|
||||||
|
TopTips:
|
||||||
|
"🥳 Предложение по запуску NextChat AI: разблокируйте OpenAI o1, GPT-4o, Claude-3.5 и новейшие большие модели прямо сейчас",
|
||||||
},
|
},
|
||||||
ChatItem: {
|
ChatItem: {
|
||||||
ChatItemCount: (count: number) => `${count} сообщений`,
|
ChatItemCount: (count: number) => `${count} бесед`,
|
||||||
},
|
},
|
||||||
Chat: {
|
Chat: {
|
||||||
SubTitle: (count: number) => `${count} сообщений с ChatGPT`,
|
SubTitle: (count: number) => `Всего ${count} бесед`,
|
||||||
|
EditMessage: {
|
||||||
|
Title: "Редактировать сообщение",
|
||||||
|
Topic: {
|
||||||
|
Title: "Тема чата",
|
||||||
|
SubTitle: "Изменить текущую тему чата",
|
||||||
|
},
|
||||||
|
},
|
||||||
Actions: {
|
Actions: {
|
||||||
ChatList: "Перейти к списку чатов",
|
ChatList: "Просмотреть список сообщений",
|
||||||
CompressedHistory: "Сжатая история памяти",
|
CompressedHistory: "Просмотреть сжатую историю подсказок",
|
||||||
Export: "Экспортировать все сообщения в формате Markdown",
|
Export: "Экспортировать чат",
|
||||||
Copy: "Копировать",
|
Copy: "Копировать",
|
||||||
Stop: "Остановить",
|
Stop: "Остановить",
|
||||||
Retry: "Повторить",
|
Retry: "Повторить",
|
||||||
|
Pin: "Закрепить",
|
||||||
|
PinToastContent: "1 беседа закреплена в предустановленных подсказках",
|
||||||
|
PinToastAction: "Просмотреть",
|
||||||
Delete: "Удалить",
|
Delete: "Удалить",
|
||||||
|
Edit: "Редактировать",
|
||||||
|
RefreshTitle: "Обновить заголовок",
|
||||||
|
RefreshToast: "Запрос на обновление заголовка отправлен",
|
||||||
|
},
|
||||||
|
Commands: {
|
||||||
|
new: "Новый чат",
|
||||||
|
newm: "Создать чат из маски",
|
||||||
|
next: "Следующий чат",
|
||||||
|
prev: "Предыдущий чат",
|
||||||
|
clear: "Очистить контекст",
|
||||||
|
del: "Удалить чат",
|
||||||
|
},
|
||||||
|
InputActions: {
|
||||||
|
Stop: "Остановить ответ",
|
||||||
|
ToBottom: "Перейти к последнему",
|
||||||
|
Theme: {
|
||||||
|
auto: "Автоматическая тема",
|
||||||
|
light: "Светлая тема",
|
||||||
|
dark: "Темная тема",
|
||||||
|
},
|
||||||
|
Prompt: "Быстрая команда",
|
||||||
|
Masks: "Все маски",
|
||||||
|
Clear: "Очистить чат",
|
||||||
|
Settings: "Настройки чата",
|
||||||
|
UploadImage: "Загрузить изображение",
|
||||||
},
|
},
|
||||||
Rename: "Переименовать чат",
|
Rename: "Переименовать чат",
|
||||||
Typing: "Печатает…",
|
Typing: "Печатает…",
|
||||||
Input: (submitKey: string) => {
|
Input: (submitKey: string) => {
|
||||||
var inputHints = `${submitKey} для отправки сообщения`;
|
var inputHints = `${submitKey} Отправить`;
|
||||||
if (submitKey === String(SubmitKey.Enter)) {
|
if (submitKey === String(SubmitKey.Enter)) {
|
||||||
inputHints += ", Shift + Enter для переноса строки";
|
inputHints += ",Shift + Enter для новой строки";
|
||||||
}
|
}
|
||||||
return inputHints + ", / для поиска подсказок";
|
return inputHints + ",/ для автозаполнения,: для команд";
|
||||||
},
|
},
|
||||||
Send: "Отправить",
|
Send: "Отправить",
|
||||||
Config: {
|
Config: {
|
||||||
Reset: "Сбросить настройки",
|
Reset: "Очистить память",
|
||||||
SaveAs: "Сохранить как маску",
|
SaveAs: "Сохранить как маску",
|
||||||
},
|
},
|
||||||
|
IsContext: "Предустановленные подсказки",
|
||||||
},
|
},
|
||||||
Export: {
|
Export: {
|
||||||
Title: "Все сообщения",
|
Title: "Поделиться историей чата",
|
||||||
Copy: "Копировать все",
|
Copy: "Копировать все",
|
||||||
Download: "Скачать",
|
Download: "Скачать файл",
|
||||||
MessageFromYou: "Сообщение от вас",
|
Share: "Поделиться в ShareGPT",
|
||||||
MessageFromChatGPT: "Сообщение от ChatGPT",
|
MessageFromYou: "Пользователь",
|
||||||
|
MessageFromChatGPT: "ChatGPT",
|
||||||
|
Format: {
|
||||||
|
Title: "Формат экспорта",
|
||||||
|
SubTitle: "Можно экспортировать как Markdown текст или PNG изображение",
|
||||||
|
},
|
||||||
|
IncludeContext: {
|
||||||
|
Title: "Включить контекст маски",
|
||||||
|
SubTitle: "Показывать ли контекст маски в сообщениях",
|
||||||
|
},
|
||||||
|
Steps: {
|
||||||
|
Select: "Выбрать",
|
||||||
|
Preview: "Предпросмотр",
|
||||||
|
},
|
||||||
|
Image: {
|
||||||
|
Toast: "Создание скриншота",
|
||||||
|
Modal: "Длительное нажатие или правый клик для сохранения изображения",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Select: {
|
||||||
|
Search: "Поиск сообщений",
|
||||||
|
All: "Выбрать все",
|
||||||
|
Latest: "Последние сообщения",
|
||||||
|
Clear: "Очистить выбор",
|
||||||
},
|
},
|
||||||
Memory: {
|
Memory: {
|
||||||
Title: "Память",
|
Title: "Историческое резюме",
|
||||||
EmptyContent: "Пусто.",
|
EmptyContent: "Содержимое чата слишком короткое, чтобы суммировать",
|
||||||
Send: "Отправить память",
|
Send: "Автоматически сжать историю чата и отправить как контекст",
|
||||||
Copy: "Копировать память",
|
Copy: "Копировать резюме",
|
||||||
Reset: "Сбросить сессию",
|
Reset: "[не используется]",
|
||||||
ResetConfirm:
|
ResetConfirm: "Подтвердить очистку исторического резюме?",
|
||||||
"При сбросе текущая история переписки и историческая память будут удалены. Вы уверены, что хотите сбросить?",
|
|
||||||
},
|
},
|
||||||
Home: {
|
Home: {
|
||||||
NewChat: "Новый чат",
|
NewChat: "Новый чат",
|
||||||
DeleteChat: "Вы действительно хотите удалить выбранный разговор?",
|
DeleteChat: "Подтвердить удаление выбранного чата?",
|
||||||
DeleteToast: "Чат удален",
|
DeleteToast: "Беседа удалена",
|
||||||
Revert: "Отмена",
|
Revert: "Отменить",
|
||||||
},
|
},
|
||||||
Settings: {
|
Settings: {
|
||||||
Title: "Настройки",
|
Title: "Настройки",
|
||||||
SubTitle: "Все настройки",
|
SubTitle: "Все параметры настроек",
|
||||||
|
|
||||||
|
Danger: {
|
||||||
|
Reset: {
|
||||||
|
Title: "Сброс всех настроек",
|
||||||
|
SubTitle: "Сброс всех параметров до значений по умолчанию",
|
||||||
|
Action: "Сбросить сейчас",
|
||||||
|
Confirm: "Подтвердить сброс всех настроек?",
|
||||||
|
},
|
||||||
|
Clear: {
|
||||||
|
Title: "Очистить все данные",
|
||||||
|
SubTitle: "Очистить все чаты и данные настроек",
|
||||||
|
Action: "Очистить сейчас",
|
||||||
|
Confirm: "Подтвердить очистку всех чатов и данных настроек?",
|
||||||
|
},
|
||||||
|
},
|
||||||
Lang: {
|
Lang: {
|
||||||
Name: "Language", // ATTENTION: if you wanna add a new translation, please do not translate this value, leave it as `Language`
|
Name: "Language", // ATTENTION: если вы хотите добавить новый перевод, не переводите это значение, оставьте его как `Language`
|
||||||
All: "Все языки",
|
All: "Все языки",
|
||||||
},
|
},
|
||||||
Avatar: "Аватар",
|
Avatar: "Аватар",
|
||||||
FontSize: {
|
FontSize: {
|
||||||
Title: "Размер шрифта",
|
Title: "Размер шрифта",
|
||||||
SubTitle: "Настроить размер шрифта контента чата",
|
SubTitle: "Размер шрифта в чате",
|
||||||
},
|
},
|
||||||
FontFamily: {
|
FontFamily: {
|
||||||
Title: "Шрифт чата",
|
Title: "Шрифт чата",
|
||||||
|
@ -80,156 +174,403 @@ const ru: PartialLocaleType = {
|
||||||
InjectSystemPrompts: {
|
InjectSystemPrompts: {
|
||||||
Title: "Вставить системные подсказки",
|
Title: "Вставить системные подсказки",
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Принудительно добавить симулированную системную подсказку ChatGPT в начало списка сообщений для каждого запроса",
|
"Принудительно добавлять системную подсказку, имитирующую ChatGPT, в начале каждого запроса",
|
||||||
},
|
},
|
||||||
|
InputTemplate: {
|
||||||
|
Title: "Предварительная обработка пользовательского ввода",
|
||||||
|
SubTitle:
|
||||||
|
"Последнее сообщение пользователя будет подставлено в этот шаблон",
|
||||||
|
},
|
||||||
|
|
||||||
Update: {
|
Update: {
|
||||||
Version: (x: string) => `Версия: ${x}`,
|
Version: (x: string) => `Текущая версия: ${x}`,
|
||||||
IsLatest: "Последняя версия",
|
IsLatest: "Установлена последняя версия",
|
||||||
CheckUpdate: "Проверить обновление",
|
CheckUpdate: "Проверить обновления",
|
||||||
IsChecking: "Проверка обновления...",
|
IsChecking: "Проверка обновлений...",
|
||||||
FoundUpdate: (x: string) => `Найдена новая версия: ${x}`,
|
FoundUpdate: (x: string) => `Найдено новое обновление: ${x}`,
|
||||||
GoToUpdate: "Обновить",
|
GoToUpdate: "Перейти к обновлению",
|
||||||
},
|
},
|
||||||
SendKey: "Клавиша отправки",
|
SendKey: "Кнопка отправки",
|
||||||
Theme: "Тема",
|
Theme: "Тема",
|
||||||
TightBorder: "Узкая граница",
|
TightBorder: "Режим без границ",
|
||||||
SendPreviewBubble: {
|
SendPreviewBubble: {
|
||||||
Title: "Отправить предпросмотр",
|
Title: "Предварительный просмотр пузырьков",
|
||||||
SubTitle: "Предварительный просмотр markdown в пузыре",
|
SubTitle:
|
||||||
|
"Просмотр содержимого Markdown в пузырьках предварительного просмотра",
|
||||||
|
},
|
||||||
|
AutoGenerateTitle: {
|
||||||
|
Title: "Автоматическое создание заголовка",
|
||||||
|
SubTitle: "Создание подходящего заголовка на основе содержания беседы",
|
||||||
|
},
|
||||||
|
Sync: {
|
||||||
|
CloudState: "Облачные данные",
|
||||||
|
NotSyncYet: "Синхронизация еще не проводилась",
|
||||||
|
Success: "Синхронизация успешна",
|
||||||
|
Fail: "Не удалось синхронизировать",
|
||||||
|
|
||||||
|
Config: {
|
||||||
|
Modal: {
|
||||||
|
Title: "Настройки облачной синхронизации",
|
||||||
|
Check: "Проверить доступность",
|
||||||
|
},
|
||||||
|
SyncType: {
|
||||||
|
Title: "Тип синхронизации",
|
||||||
|
SubTitle: "Выберите предпочитаемый сервер синхронизации",
|
||||||
|
},
|
||||||
|
Proxy: {
|
||||||
|
Title: "Включить прокси",
|
||||||
|
SubTitle:
|
||||||
|
"При синхронизации в браузере необходимо включить прокси для предотвращения ограничений кросс-домена",
|
||||||
|
},
|
||||||
|
ProxyUrl: {
|
||||||
|
Title: "Адрес прокси",
|
||||||
|
SubTitle: "Только для встроенного прокси в проекте",
|
||||||
|
},
|
||||||
|
|
||||||
|
WebDav: {
|
||||||
|
Endpoint: "WebDAV адрес",
|
||||||
|
UserName: "Имя пользователя",
|
||||||
|
Password: "Пароль",
|
||||||
|
},
|
||||||
|
|
||||||
|
UpStash: {
|
||||||
|
Endpoint: "UpStash Redis REST Url",
|
||||||
|
UserName: "Имя резервной копии",
|
||||||
|
Password: "UpStash Redis REST Token",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
LocalState: "Локальные данные",
|
||||||
|
Overview: (overview: any) => {
|
||||||
|
return `${overview.chat} бесед, ${overview.message} сообщений, ${overview.prompt} подсказок, ${overview.mask} масок`;
|
||||||
|
},
|
||||||
|
ImportFailed: "Не удалось импортировать",
|
||||||
},
|
},
|
||||||
Mask: {
|
Mask: {
|
||||||
Splash: {
|
Splash: {
|
||||||
Title: "Экран заставки маски",
|
Title: "Стартовая страница масок",
|
||||||
SubTitle: "Показывать экран заставки маски перед началом нового чата",
|
SubTitle:
|
||||||
|
"При создании нового чата отображать стартовую страницу масок",
|
||||||
|
},
|
||||||
|
Builtin: {
|
||||||
|
Title: "Скрыть встроенные маски",
|
||||||
|
SubTitle: "Скрыть встроенные маски во всех списках масок",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Prompt: {
|
Prompt: {
|
||||||
Disable: {
|
Disable: {
|
||||||
Title: "Отключить автозаполнение",
|
Title: "Отключить автозаполнение подсказок",
|
||||||
SubTitle: "Ввод / для запуска автозаполнения",
|
SubTitle: "Введите / в начале строки для активации автозаполнения",
|
||||||
},
|
},
|
||||||
List: "Список подсказок",
|
List: "Список пользовательских подсказок",
|
||||||
ListCount: (builtin: number, custom: number) =>
|
ListCount: (builtin: number, custom: number) =>
|
||||||
`${builtin} встроенных, ${custom} пользовательских`,
|
`Встроенные ${builtin}, пользовательские ${custom}`,
|
||||||
Edit: "Редактировать",
|
Edit: "Редактировать",
|
||||||
Modal: {
|
Modal: {
|
||||||
Title: "Список подсказок",
|
Title: "Список подсказок",
|
||||||
Add: "Добавить",
|
Add: "Создать",
|
||||||
Search: "Поиск подсказок",
|
Search: "Поиск подсказок",
|
||||||
},
|
},
|
||||||
EditModal: {
|
EditModal: {
|
||||||
Title: "Редактировать подсказку",
|
Title: "Редактировать подсказки",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
HistoryCount: {
|
HistoryCount: {
|
||||||
Title: "Количество прикрепляемых сообщений",
|
Title: "Количество истории сообщений",
|
||||||
SubTitle:
|
SubTitle: "Количество историй сообщений, отправляемых с каждым запросом",
|
||||||
"Количество отправляемых сообщений, прикрепляемых к каждому запросу",
|
|
||||||
},
|
},
|
||||||
CompressThreshold: {
|
CompressThreshold: {
|
||||||
Title: "Порог сжатия истории",
|
Title: "Порог сжатия длины истории сообщений",
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Будет сжимать, если длина несжатых сообщений превышает указанное значение",
|
"Когда не сжатая история сообщений превышает это значение, происходит сжатие",
|
||||||
},
|
},
|
||||||
|
|
||||||
Usage: {
|
Usage: {
|
||||||
Title: "Баланс аккаунта",
|
Title: "Проверка баланса",
|
||||||
SubTitle(used: any, total: any) {
|
SubTitle(used: any, total: any) {
|
||||||
return `Использовано в этом месяце $${used}, подписка $${total}`;
|
return `Использовано в этом месяце $${used}, всего по подписке $${total}`;
|
||||||
|
},
|
||||||
|
IsChecking: "Проверка…",
|
||||||
|
Check: "Проверить снова",
|
||||||
|
NoAccess: "Введите API-ключ или пароль доступа для просмотра баланса",
|
||||||
|
},
|
||||||
|
|
||||||
|
Access: {
|
||||||
|
SaasStart: {
|
||||||
|
Title: "Используйте NextChat AI",
|
||||||
|
Label: "(Самое экономичное решение)",
|
||||||
|
SubTitle:
|
||||||
|
"Официально поддерживается NextChat, готов к использованию без настройки, поддерживает последние крупные модели, такие как OpenAI o1, GPT-4o и Claude-3.5",
|
||||||
|
ChatNow: "Начать чат",
|
||||||
|
},
|
||||||
|
|
||||||
|
AccessCode: {
|
||||||
|
Title: "Пароль доступа",
|
||||||
|
SubTitle: "Администратор включил защиту паролем",
|
||||||
|
Placeholder: "Введите пароль доступа",
|
||||||
|
},
|
||||||
|
CustomEndpoint: {
|
||||||
|
Title: "Пользовательский интерфейс",
|
||||||
|
SubTitle: "Использовать ли пользовательский Azure или OpenAI сервис",
|
||||||
|
},
|
||||||
|
Provider: {
|
||||||
|
Title: "Провайдер модели",
|
||||||
|
SubTitle: "Переключиться на другого провайдера",
|
||||||
|
},
|
||||||
|
OpenAI: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "API-ключ",
|
||||||
|
SubTitle:
|
||||||
|
"Использовать пользовательский OpenAI-ключ для обхода ограничений пароля",
|
||||||
|
Placeholder: "OpenAI API-ключ",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Адрес интерфейса",
|
||||||
|
SubTitle: "Кроме адреса по умолчанию, должен содержать http(s)://",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Azure: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "Ключ интерфейса",
|
||||||
|
SubTitle:
|
||||||
|
"Использовать пользовательский Azure-ключ для обхода ограничений пароля",
|
||||||
|
Placeholder: "Azure API-ключ",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Адрес интерфейса",
|
||||||
|
SubTitle: "Пример:",
|
||||||
|
},
|
||||||
|
|
||||||
|
ApiVerion: {
|
||||||
|
Title: "Версия интерфейса (azure api version)",
|
||||||
|
SubTitle: "Выберите конкретную версию",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Anthropic: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "Ключ интерфейса",
|
||||||
|
SubTitle:
|
||||||
|
"Использовать пользовательский Anthropic-ключ для обхода ограничений пароля",
|
||||||
|
Placeholder: "Anthropic API-ключ",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Адрес интерфейса",
|
||||||
|
SubTitle: "Пример:",
|
||||||
|
},
|
||||||
|
|
||||||
|
ApiVerion: {
|
||||||
|
Title: "Версия интерфейса (claude api version)",
|
||||||
|
SubTitle: "Выберите конкретную версию API",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Google: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "API-ключ",
|
||||||
|
SubTitle: "Получите ваш API-ключ Google AI",
|
||||||
|
Placeholder: "Введите ваш API-ключ Google AI Studio",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Адрес интерфейса",
|
||||||
|
SubTitle: "Пример:",
|
||||||
|
},
|
||||||
|
|
||||||
|
ApiVersion: {
|
||||||
|
Title: "Версия API (только для gemini-pro)",
|
||||||
|
SubTitle: "Выберите конкретную версию API",
|
||||||
|
},
|
||||||
|
GoogleSafetySettings: {
|
||||||
|
Title: "Уровень фильтрации Google",
|
||||||
|
SubTitle: "Настроить уровень фильтрации контента",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Baidu: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "API-ключ",
|
||||||
|
SubTitle: "Использовать пользовательский Baidu API-ключ",
|
||||||
|
Placeholder: "Baidu API-ключ",
|
||||||
|
},
|
||||||
|
SecretKey: {
|
||||||
|
Title: "Секретный ключ",
|
||||||
|
SubTitle: "Использовать пользовательский Baidu Secret Key",
|
||||||
|
Placeholder: "Baidu Secret Key",
|
||||||
|
},
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Адрес интерфейса",
|
||||||
|
SubTitle:
|
||||||
|
"Не поддерживает пользовательскую настройку, перейдите в .env",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ByteDance: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "Ключ интерфейса",
|
||||||
|
SubTitle: "Использовать пользовательский ByteDance API-ключ",
|
||||||
|
Placeholder: "ByteDance API-ключ",
|
||||||
|
},
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Адрес интерфейса",
|
||||||
|
SubTitle: "Пример:",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Alibaba: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "Ключ интерфейса",
|
||||||
|
SubTitle: "Использовать пользовательский Alibaba Cloud API-ключ",
|
||||||
|
Placeholder: "Alibaba Cloud API-ключ",
|
||||||
|
},
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Адрес интерфейса",
|
||||||
|
SubTitle: "Пример:",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CustomModel: {
|
||||||
|
Title: "Название пользовательской модели",
|
||||||
|
SubTitle:
|
||||||
|
"Добавьте варианты пользовательских моделей, разделяя запятыми",
|
||||||
},
|
},
|
||||||
IsChecking: "Проверка...",
|
|
||||||
Check: "Проверить",
|
|
||||||
NoAccess: "Введите API ключ, чтобы проверить баланс",
|
|
||||||
},
|
},
|
||||||
|
|
||||||
Model: "Модель",
|
Model: "Модель",
|
||||||
|
CompressModel: {
|
||||||
|
Title: "Модель сжатия",
|
||||||
|
SubTitle: "Модель, используемая для сжатия истории",
|
||||||
|
},
|
||||||
Temperature: {
|
Temperature: {
|
||||||
Title: "Температура",
|
Title: "Случайность (temperature)",
|
||||||
SubTitle: "Чем выше значение, тем более случайный вывод",
|
SubTitle: "Чем больше значение, тем более случайные ответы",
|
||||||
|
},
|
||||||
|
TopP: {
|
||||||
|
Title: "Ядро выборки (top_p)",
|
||||||
|
SubTitle: "Похожие на случайность, но не изменяйте вместе с случайностью",
|
||||||
},
|
},
|
||||||
MaxTokens: {
|
MaxTokens: {
|
||||||
Title: "Максимальное количество токенов",
|
Title: "Ограничение на количество токенов за один раз (max_tokens)",
|
||||||
SubTitle: "Максимальная длина вводных и генерируемых токенов",
|
SubTitle: "Максимальное количество токенов на одно взаимодействие",
|
||||||
},
|
},
|
||||||
PresencePenalty: {
|
PresencePenalty: {
|
||||||
Title: "Штраф за повторения",
|
Title: "Наказание за новизну тем (presence_penalty)",
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Чем выше значение, тем больше вероятность общения на новые темы",
|
"Чем больше значение, тем выше вероятность расширения на новые темы",
|
||||||
},
|
},
|
||||||
FrequencyPenalty: {
|
FrequencyPenalty: {
|
||||||
Title: "Штраф за частоту",
|
Title: "Наказание за частоту (frequency_penalty)",
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Большее значение снижает вероятность повторения одной и той же строки",
|
"Чем больше значение, тем выше вероятность уменьшения повторяющихся слов",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Store: {
|
Store: {
|
||||||
DefaultTopic: "Новый разговор",
|
DefaultTopic: "Новый чат",
|
||||||
BotHello: "Здравствуйте! Как я могу вам помочь сегодня?",
|
BotHello: "Чем могу помочь?",
|
||||||
Error: "Что-то пошло не так. Пожалуйста, попробуйте еще раз позже.",
|
Error: "Произошла ошибка, попробуйте позже",
|
||||||
Prompt: {
|
Prompt: {
|
||||||
History: (content: string) =>
|
History: (content: string) =>
|
||||||
"Это краткое содержание истории чата между ИИ и пользователем: " +
|
"Это резюме истории чата как предыстория: " + content,
|
||||||
content,
|
|
||||||
Topic:
|
Topic:
|
||||||
"Пожалуйста, создайте заголовок из четырех или пяти слов, который кратко описывает нашу беседу, без введения, знаков пунктуации, кавычек, точек, символов или дополнительного текста. Удалите кавычки.",
|
"Укажите краткую тему этого сообщения в четырех-пяти словах, без объяснений, знаков препинания, междометий, лишнего текста или выделения. Если темы нет, просто напишите 'Болтовня'",
|
||||||
Summarize:
|
Summarize:
|
||||||
"Кратко изложите нашу дискуссию в 200 словах или менее для использования в будущем контексте.",
|
"Кратко подведите итоги содержимого беседы для использования в качестве последующего контекстного запроса, не более 200 слов",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Copy: {
|
Copy: {
|
||||||
Success: "Скопировано в буфер обмена",
|
Success: "Скопировано в буфер обмена",
|
||||||
Failed:
|
Failed: "Не удалось скопировать, предоставьте доступ к буферу обмена",
|
||||||
"Не удалось скопировать, пожалуйста, предоставьте разрешение на доступ к буферу обмена",
|
},
|
||||||
|
Download: {
|
||||||
|
Success: "Содержимое успешно загружено в вашу директорию.",
|
||||||
|
Failed: "Не удалось загрузить.",
|
||||||
},
|
},
|
||||||
Context: {
|
Context: {
|
||||||
Toast: (x: any) => `С ${x} контекстными подсказками`,
|
Toast: (x: any) => `Содержит ${x} предустановленных подсказок`,
|
||||||
Edit: "Контекстные и памятные подсказки",
|
Edit: "Текущие настройки чата",
|
||||||
Add: "Добавить подсказку",
|
Add: "Добавить новый чат",
|
||||||
|
Clear: "Контекст очищен",
|
||||||
|
Revert: "Восстановить контекст",
|
||||||
},
|
},
|
||||||
Plugin: {
|
Plugin: {
|
||||||
Name: "Плагин",
|
Name: "Плагины",
|
||||||
},
|
},
|
||||||
FineTuned: {
|
FineTuned: {
|
||||||
Sysmessage: "Вы - ассистент, который",
|
Sysmessage: "Вы - помощник",
|
||||||
|
},
|
||||||
|
SearchChat: {
|
||||||
|
Name: "Поиск",
|
||||||
|
Page: {
|
||||||
|
Title: "Поиск в истории чатов",
|
||||||
|
Search: "Введите ключевые слова для поиска",
|
||||||
|
NoResult: "Результатов не найдено",
|
||||||
|
NoData: "Нет данных",
|
||||||
|
Loading: "Загрузка",
|
||||||
|
|
||||||
|
SubTitle: (count: number) => `Найдено ${count} результатов`,
|
||||||
|
},
|
||||||
|
Item: {
|
||||||
|
View: "Просмотр",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Mask: {
|
Mask: {
|
||||||
Name: "Маска",
|
Name: "Маска",
|
||||||
Page: {
|
Page: {
|
||||||
Title: "Шаблон подсказки",
|
Title: "Предустановленные роли",
|
||||||
SubTitle: (count: number) => `${count} шаблонов подсказок`,
|
SubTitle: (count: number) => `${count} предустановленных ролей`,
|
||||||
Search: "Поиск шаблонов",
|
Search: "Поиск ролей",
|
||||||
Create: "Создать",
|
Create: "Создать",
|
||||||
},
|
},
|
||||||
Item: {
|
Item: {
|
||||||
Info: (count: number) => `${count} подсказок`,
|
Info: (count: number) => `Содержит ${count} предустановленных диалогов`,
|
||||||
Chat: "Чат",
|
Chat: "Диалог",
|
||||||
View: "Просмотр",
|
View: "Просмотреть",
|
||||||
Edit: "Редактировать",
|
Edit: "Редактировать",
|
||||||
Delete: "Удалить",
|
Delete: "Удалить",
|
||||||
DeleteConfirm: "Подтвердить удаление?",
|
DeleteConfirm: "Подтвердить удаление?",
|
||||||
},
|
},
|
||||||
EditModal: {
|
EditModal: {
|
||||||
Title: (readonly: boolean) =>
|
Title: (readonly: boolean) =>
|
||||||
`Редактирование шаблона подсказки ${
|
`Редактирование предустановленной маски ${
|
||||||
readonly ? "(только для чтения)" : ""
|
readonly ? "(только для чтения)" : ""
|
||||||
}`,
|
}`,
|
||||||
Download: "Скачать",
|
Download: "Скачать предустановку",
|
||||||
Clone: "Клонировать",
|
Clone: "Клонировать предустановку",
|
||||||
},
|
},
|
||||||
Config: {
|
Config: {
|
||||||
Avatar: "Аватар бота",
|
Avatar: "Аватар роли",
|
||||||
Name: "Имя бота",
|
Name: "Название роли",
|
||||||
|
Sync: {
|
||||||
|
Title: "Использовать глобальные настройки",
|
||||||
|
SubTitle:
|
||||||
|
"Будет ли текущий чат использовать глобальные настройки модели",
|
||||||
|
Confirm:
|
||||||
|
"Пользовательские настройки текущего чата будут автоматически заменены, подтвердите активацию глобальных настроек?",
|
||||||
|
},
|
||||||
|
HideContext: {
|
||||||
|
Title: "Скрыть предустановленные диалоги",
|
||||||
|
SubTitle:
|
||||||
|
"После скрытия предустановленные диалоги не будут отображаться в чате",
|
||||||
|
},
|
||||||
|
Share: {
|
||||||
|
Title: "Поделиться этой маской",
|
||||||
|
SubTitle: "Создать прямую ссылку на эту маску",
|
||||||
|
Action: "Скопировать ссылку",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
NewChat: {
|
NewChat: {
|
||||||
Return: "Вернуться",
|
Return: "Вернуться",
|
||||||
Skip: "Пропустить",
|
Skip: "Начать сразу",
|
||||||
Title: "Выберите маску",
|
NotShow: "Больше не показывать",
|
||||||
SubTitle: "Общайтесь с душой за маской",
|
|
||||||
More: "Найти еще",
|
|
||||||
NotShow: "Не показывать снова",
|
|
||||||
ConfirmNoShow:
|
ConfirmNoShow:
|
||||||
"Подтвердите отключение? Вы можете включить это позже в настройках.",
|
"Подтвердить отключение? После отключения вы всегда сможете включить его снова в настройках.",
|
||||||
|
Title: "Выберите маску",
|
||||||
|
SubTitle: "Начните сейчас, столкнитесь с мыслями за маской",
|
||||||
|
More: "Показать все",
|
||||||
|
},
|
||||||
|
|
||||||
|
URLCommand: {
|
||||||
|
Code: "Обнаружен код доступа в ссылке, автоматически заполнить?",
|
||||||
|
Settings:
|
||||||
|
"Обнаружены предустановленные настройки в ссылке, автоматически заполнить?",
|
||||||
},
|
},
|
||||||
|
|
||||||
UI: {
|
UI: {
|
||||||
|
@ -238,8 +579,15 @@ const ru: PartialLocaleType = {
|
||||||
Close: "Закрыть",
|
Close: "Закрыть",
|
||||||
Create: "Создать",
|
Create: "Создать",
|
||||||
Edit: "Редактировать",
|
Edit: "Редактировать",
|
||||||
|
Export: "Экспортировать",
|
||||||
|
Import: "Импортировать",
|
||||||
|
Sync: "Синхронизировать",
|
||||||
|
Config: "Настройки",
|
||||||
},
|
},
|
||||||
Exporter: {
|
Exporter: {
|
||||||
|
Description: {
|
||||||
|
Title: "Только сообщения после очистки контекста будут отображаться",
|
||||||
|
},
|
||||||
Model: "Модель",
|
Model: "Модель",
|
||||||
Messages: "Сообщения",
|
Messages: "Сообщения",
|
||||||
Topic: "Тема",
|
Topic: "Тема",
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import { getClientConfig } from "../config/client";
|
import { getClientConfig } from "../config/client";
|
||||||
import { SubmitKey } from "../store/config";
|
import { SubmitKey } from "../store/config";
|
||||||
import { LocaleType } from "./index";
|
|
||||||
import type { PartialLocaleType } from "./index";
|
import type { PartialLocaleType } from "./index";
|
||||||
|
import { SAAS_CHAT_UTM_URL } from "@/app/constant";
|
||||||
// if you are adding a new translation, please use PartialLocaleType instead of LocaleType
|
// if you are adding a new translation, please use PartialLocaleType instead of LocaleType
|
||||||
|
|
||||||
const isApp = !!getClientConfig()?.isApp;
|
const isApp = !!getClientConfig()?.isApp;
|
||||||
|
@ -10,8 +9,14 @@ const sk: PartialLocaleType = {
|
||||||
WIP: "Už čoskoro...",
|
WIP: "Už čoskoro...",
|
||||||
Error: {
|
Error: {
|
||||||
Unauthorized: isApp
|
Unauthorized: isApp
|
||||||
? "Neplatný API kľúč, prosím skontrolujte ho na stránke [Nastavenia](/#/settings)."
|
? `😆 Rozhovor narazil na nejaké problémy, nebojte sa:
|
||||||
: "Neoprávnený prístup, prosím zadajte prístupový kód na stránke [auth](/#/auth), alebo zadajte váš OpenAI API kľúč.",
|
\\ 1️⃣ Ak chcete začať bez konfigurácie, [kliknite sem, aby ste okamžite začali chatovať 🚀](${SAAS_CHAT_UTM_URL})
|
||||||
|
\\ 2️⃣ Ak chcete používať svoje vlastné zdroje OpenAI, kliknite [sem](/#/settings), aby ste upravili nastavenia ⚙️`
|
||||||
|
: `😆 Rozhovor narazil na nejaké problémy, nebojte sa:
|
||||||
|
\ 1️⃣ Ak chcete začať bez konfigurácie, [kliknite sem, aby ste okamžite začali chatovať 🚀](${SAAS_CHAT_UTM_URL})
|
||||||
|
\ 2️⃣ Ak používate verziu súkromného nasadenia, kliknite [sem](/#/auth), aby ste zadali prístupový kľúč 🔑
|
||||||
|
\ 3️⃣ Ak chcete používať svoje vlastné zdroje OpenAI, kliknite [sem](/#/settings), aby ste upravili nastavenia ⚙️
|
||||||
|
`,
|
||||||
},
|
},
|
||||||
Auth: {
|
Auth: {
|
||||||
Title: "Potrebný prístupový kód",
|
Title: "Potrebný prístupový kód",
|
||||||
|
@ -20,6 +25,10 @@ const sk: PartialLocaleType = {
|
||||||
Input: "prístupový kód",
|
Input: "prístupový kód",
|
||||||
Confirm: "Potvrdiť",
|
Confirm: "Potvrdiť",
|
||||||
Later: "Neskôr",
|
Later: "Neskôr",
|
||||||
|
Return: "Návrat",
|
||||||
|
SaasTips: "Nastavenie je príliš zložité, chcem to okamžite použiť",
|
||||||
|
TopTips:
|
||||||
|
"🥳 Uvítacia ponuka NextChat AI, okamžite odomknite OpenAI o1, GPT-4o, Claude-3.5 a najnovšie veľké modely",
|
||||||
},
|
},
|
||||||
ChatItem: {
|
ChatItem: {
|
||||||
ChatItemCount: (count: number) => `${count} správ`,
|
ChatItemCount: (count: number) => `${count} správ`,
|
||||||
|
@ -45,6 +54,8 @@ const sk: PartialLocaleType = {
|
||||||
PinToastAction: "Zobraziť",
|
PinToastAction: "Zobraziť",
|
||||||
Delete: "Vymazať",
|
Delete: "Vymazať",
|
||||||
Edit: "Upraviť",
|
Edit: "Upraviť",
|
||||||
|
RefreshTitle: "Obnoviť názov",
|
||||||
|
RefreshToast: "Požiadavka na obnovenie názvu bola odoslaná",
|
||||||
},
|
},
|
||||||
Commands: {
|
Commands: {
|
||||||
new: "Začať nový chat",
|
new: "Začať nový chat",
|
||||||
|
@ -280,6 +291,14 @@ const sk: PartialLocaleType = {
|
||||||
NoAccess: "Zadajte API kľúč na skontrolovanie zostatku",
|
NoAccess: "Zadajte API kľúč na skontrolovanie zostatku",
|
||||||
},
|
},
|
||||||
Access: {
|
Access: {
|
||||||
|
SaasStart: {
|
||||||
|
Title: "Použite NextChat AI",
|
||||||
|
Label: "(Najvýhodnejšie riešenie)",
|
||||||
|
SubTitle:
|
||||||
|
"Oficiálne udržiavané NextChat, pripravené na použitie bez konfigurácie, podporuje najnovšie veľké modely ako OpenAI o1, GPT-4o a Claude-3.5",
|
||||||
|
ChatNow: "Chatovať teraz",
|
||||||
|
},
|
||||||
|
|
||||||
AccessCode: {
|
AccessCode: {
|
||||||
Title: "Prístupový kód",
|
Title: "Prístupový kód",
|
||||||
SubTitle: "Povolený prístupový kód",
|
SubTitle: "Povolený prístupový kód",
|
||||||
|
@ -365,6 +384,10 @@ const sk: PartialLocaleType = {
|
||||||
},
|
},
|
||||||
|
|
||||||
Model: "Model",
|
Model: "Model",
|
||||||
|
CompressModel: {
|
||||||
|
Title: "Kompresný model",
|
||||||
|
SubTitle: "Model používaný na kompresiu histórie",
|
||||||
|
},
|
||||||
Temperature: {
|
Temperature: {
|
||||||
Title: "Teplota",
|
Title: "Teplota",
|
||||||
SubTitle: "Vyššia hodnota robí výstup náhodnejším",
|
SubTitle: "Vyššia hodnota robí výstup náhodnejším",
|
||||||
|
@ -423,6 +446,21 @@ const sk: PartialLocaleType = {
|
||||||
FineTuned: {
|
FineTuned: {
|
||||||
Sysmessage: "Ste asistent, ktorý",
|
Sysmessage: "Ste asistent, ktorý",
|
||||||
},
|
},
|
||||||
|
SearchChat: {
|
||||||
|
Name: "Hľadať",
|
||||||
|
Page: {
|
||||||
|
Title: "Hľadať v histórii chatu",
|
||||||
|
Search: "Zadajte kľúčové slová na vyhľadávanie",
|
||||||
|
NoResult: "Nenašli sa žiadne výsledky",
|
||||||
|
NoData: "Žiadne údaje",
|
||||||
|
Loading: "Načítava sa",
|
||||||
|
|
||||||
|
SubTitle: (count: number) => `Nájdených ${count} výsledkov`,
|
||||||
|
},
|
||||||
|
Item: {
|
||||||
|
View: "Zobraziť",
|
||||||
|
},
|
||||||
|
},
|
||||||
Mask: {
|
Mask: {
|
||||||
Name: "Maska",
|
Name: "Maska",
|
||||||
Page: {
|
Page: {
|
||||||
|
|
|
@ -1,75 +1,170 @@
|
||||||
import { SubmitKey } from "../store/config";
|
import { SubmitKey } from "../store/config";
|
||||||
import type { PartialLocaleType } from "./index";
|
import type { PartialLocaleType } from "./index";
|
||||||
|
import { getClientConfig } from "../config/client";
|
||||||
|
import { SAAS_CHAT_UTM_URL } from "@/app/constant";
|
||||||
|
const isApp = !!getClientConfig()?.isApp;
|
||||||
|
|
||||||
const tr: PartialLocaleType = {
|
const tr: PartialLocaleType = {
|
||||||
WIP: "Çalışma devam ediyor...",
|
WIP: "Çalışma devam ediyor...",
|
||||||
Error: {
|
Error: {
|
||||||
Unauthorized:
|
Unauthorized: isApp
|
||||||
"Yetkisiz erişim, lütfen erişim kodunu ayarlar [sayfasından](/#/auth) giriniz.",
|
? `😆 Sohbet bazı sorunlarla karşılaştı, endişelenmeyin:
|
||||||
|
\\ 1️⃣ Eğer sıfır yapılandırma ile başlamak istiyorsanız, [buraya tıklayarak hemen sohbete başlayın 🚀](${SAAS_CHAT_UTM_URL})
|
||||||
|
\\ 2️⃣ Kendi OpenAI kaynaklarınızı kullanmak istiyorsanız, [buraya tıklayarak](/#/settings) ayarları değiştirin ⚙️`
|
||||||
|
: `😆 Sohbet bazı sorunlarla karşılaştı, endişelenmeyin:
|
||||||
|
\ 1️⃣ Eğer sıfır yapılandırma ile başlamak istiyorsanız, [buraya tıklayarak hemen sohbete başlayın 🚀](${SAAS_CHAT_UTM_URL})
|
||||||
|
\ 2️⃣ Eğer özel dağıtım sürümü kullanıyorsanız, [buraya tıklayarak](/#/auth) erişim anahtarını girin 🔑
|
||||||
|
\ 3️⃣ Kendi OpenAI kaynaklarınızı kullanmak istiyorsanız, [buraya tıklayarak](/#/settings) ayarları değiştirin ⚙️
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
Auth: {
|
||||||
|
Title: "Şifre Gerekli",
|
||||||
|
Tips: "Yönetici şifre doğrulamasını etkinleştirdi, lütfen aşağıya erişim kodunu girin",
|
||||||
|
SubTips: "Veya OpenAI veya Google API anahtarınızı girin",
|
||||||
|
Input: "Erişim kodunu buraya girin",
|
||||||
|
Confirm: "Onayla",
|
||||||
|
Later: "Sonra",
|
||||||
|
Return: "Geri",
|
||||||
|
SaasTips: "Ayarlar çok karmaşık, hemen kullanmak istiyorum",
|
||||||
|
TopTips:
|
||||||
|
"🥳 NextChat AI lansman teklifi, OpenAI o1, GPT-4o, Claude-3.5 ve en son büyük modelleri şimdi açın",
|
||||||
},
|
},
|
||||||
ChatItem: {
|
ChatItem: {
|
||||||
ChatItemCount: (count: number) => `${count} mesaj`,
|
ChatItemCount: (count: number) => `${count} konuşma`,
|
||||||
},
|
},
|
||||||
Chat: {
|
Chat: {
|
||||||
SubTitle: (count: number) => `ChatGPT tarafından ${count} mesaj`,
|
SubTitle: (count: number) => `Toplam ${count} konuşma`,
|
||||||
|
EditMessage: {
|
||||||
|
Title: "Mesaj Kayıtlarını Düzenle",
|
||||||
|
Topic: {
|
||||||
|
Title: "Sohbet Konusu",
|
||||||
|
SubTitle: "Geçerli sohbet konusunu değiştir",
|
||||||
|
},
|
||||||
|
},
|
||||||
Actions: {
|
Actions: {
|
||||||
ChatList: "Sohbet Listesine Git",
|
ChatList: "Mesaj listesine bak",
|
||||||
CompressedHistory: "Sıkıştırılmış Geçmiş Bellek Komutu",
|
CompressedHistory: "Sıkıştırılmış geçmişi gör",
|
||||||
Export: "Tüm Mesajları Markdown Olarak Dışa Aktar",
|
Export: "Sohbet kayıtlarını dışa aktar",
|
||||||
Copy: "Kopyala",
|
Copy: "Kopyala",
|
||||||
Stop: "Durdur",
|
Stop: "Durdur",
|
||||||
Retry: "Tekrar Dene",
|
Retry: "Yeniden dene",
|
||||||
Delete: "Delete",
|
Pin: "Sabitlenmiş",
|
||||||
|
PinToastContent: "1 konuşma varsayılan ifadeye sabitlendi",
|
||||||
|
PinToastAction: "Görünüm",
|
||||||
|
Delete: "Sil",
|
||||||
|
Edit: "Düzenle",
|
||||||
|
RefreshTitle: "Başlığı Yenile",
|
||||||
|
RefreshToast: "Başlık yenileme isteği gönderildi",
|
||||||
},
|
},
|
||||||
Rename: "Sohbeti Yeniden Adlandır",
|
Commands: {
|
||||||
|
new: "Yeni sohbet",
|
||||||
|
newm: "Maske ile yeni sohbet oluştur",
|
||||||
|
next: "Sonraki sohbet",
|
||||||
|
prev: "Önceki sohbet",
|
||||||
|
clear: "Konteksti temizle",
|
||||||
|
del: "Sohbeti sil",
|
||||||
|
},
|
||||||
|
InputActions: {
|
||||||
|
Stop: "Yanıtı durdur",
|
||||||
|
ToBottom: "En alta git",
|
||||||
|
Theme: {
|
||||||
|
auto: "Otomatik tema",
|
||||||
|
light: "Açık mod",
|
||||||
|
dark: "Koyu mod",
|
||||||
|
},
|
||||||
|
Prompt: "Kısayol komutu",
|
||||||
|
Masks: "Tüm maskeler",
|
||||||
|
Clear: "Sohbeti temizle",
|
||||||
|
Settings: "Sohbet ayarları",
|
||||||
|
UploadImage: "Resim yükle",
|
||||||
|
},
|
||||||
|
Rename: "Sohbeti yeniden adlandır",
|
||||||
Typing: "Yazıyor…",
|
Typing: "Yazıyor…",
|
||||||
Input: (submitKey: string) => {
|
Input: (submitKey: string) => {
|
||||||
var inputHints = `Göndermek için ${submitKey}`;
|
var inputHints = `${submitKey} gönder`;
|
||||||
if (submitKey === String(SubmitKey.Enter)) {
|
if (submitKey === String(SubmitKey.Enter)) {
|
||||||
inputHints += ", kaydırmak için Shift + Enter";
|
inputHints += " Shift + Enter satır sonu için";
|
||||||
}
|
}
|
||||||
return inputHints + ", komutları aramak için / (eğik çizgi)";
|
return inputHints + " / tamamlama için, : komutlar için";
|
||||||
},
|
},
|
||||||
Send: "Gönder",
|
Send: "Gönder",
|
||||||
Config: {
|
Config: {
|
||||||
Reset: "Reset to Default",
|
Reset: "Hafızayı temizle",
|
||||||
SaveAs: "Save as Mask",
|
SaveAs: "Maske olarak kaydet",
|
||||||
},
|
},
|
||||||
|
IsContext: "Varsayılan ifade",
|
||||||
},
|
},
|
||||||
Export: {
|
Export: {
|
||||||
Title: "Tüm Mesajlar",
|
Title: "Sohbet kayıtlarını paylaş",
|
||||||
Copy: "Tümünü Kopyala",
|
Copy: "Hepsini kopyala",
|
||||||
Download: "İndir",
|
Download: "Dosyayı indir",
|
||||||
MessageFromYou: "Sizin Mesajınız",
|
Share: "ShareGPT'ye paylaş",
|
||||||
MessageFromChatGPT: "ChatGPT'nin Mesajı",
|
MessageFromYou: "Kullanıcı",
|
||||||
|
MessageFromChatGPT: "ChatGPT",
|
||||||
|
Format: {
|
||||||
|
Title: "Dışa aktarma formatı",
|
||||||
|
SubTitle: "Markdown metni veya PNG resmi olarak dışa aktarabilirsiniz",
|
||||||
|
},
|
||||||
|
IncludeContext: {
|
||||||
|
Title: "Maske bağlamını dahil et",
|
||||||
|
SubTitle: "Mesajlarda maske bağlamını göstermek ister misiniz",
|
||||||
|
},
|
||||||
|
Steps: {
|
||||||
|
Select: "Seç",
|
||||||
|
Preview: "Önizleme",
|
||||||
|
},
|
||||||
|
Image: {
|
||||||
|
Toast: "Ekran görüntüsü oluşturuluyor",
|
||||||
|
Modal: "Resmi uzun basın veya sağ tıklayın ve kaydedin",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Select: {
|
||||||
|
Search: "Mesajları ara",
|
||||||
|
All: "Hepsini seç",
|
||||||
|
Latest: "Son birkaç mesaj",
|
||||||
|
Clear: "Seçimi temizle",
|
||||||
},
|
},
|
||||||
Memory: {
|
Memory: {
|
||||||
Title: "Bellek Komutları",
|
Title: "Geçmiş Özeti",
|
||||||
EmptyContent: "Henüz değil.",
|
EmptyContent: "Sohbet içeriği çok kısa, özetleme gerek yok",
|
||||||
Send: "Belleği Gönder",
|
Send: "Sohbet kayıtlarını otomatik olarak sıkıştır ve bağlam olarak gönder",
|
||||||
Copy: "Belleği Kopyala",
|
Copy: "Özeti kopyala",
|
||||||
Reset: "Oturumu Sıfırla",
|
Reset: "[kullanılmadı]",
|
||||||
ResetConfirm:
|
ResetConfirm: "Geçmiş özetini temizlemek istediğinize emin misiniz?",
|
||||||
"Sıfırlama, geçerli görüşme geçmişini ve geçmiş belleği siler. Sıfırlamak istediğinizden emin misiniz?",
|
|
||||||
},
|
},
|
||||||
Home: {
|
Home: {
|
||||||
NewChat: "Yeni Sohbet",
|
NewChat: "Yeni sohbet",
|
||||||
DeleteChat: "Seçili sohbeti silmeyi onaylıyor musunuz?",
|
DeleteChat: "Seçilen sohbeti silmek istediğinize emin misiniz?",
|
||||||
DeleteToast: "Sohbet Silindi",
|
DeleteToast: "Sohbet silindi",
|
||||||
Revert: "Geri Al",
|
Revert: "Geri al",
|
||||||
},
|
},
|
||||||
Settings: {
|
Settings: {
|
||||||
Title: "Ayarlar",
|
Title: "Ayarlar",
|
||||||
SubTitle: "Tüm Ayarlar",
|
SubTitle: "Tüm ayar seçenekleri",
|
||||||
|
|
||||||
Lang: {
|
Danger: {
|
||||||
Name: "Language", // ATTENTION: if you wanna add a new translation, please do not translate this value, leave it as `Language`
|
Reset: {
|
||||||
All: "Tüm Diller",
|
Title: "Tüm ayarları sıfırla",
|
||||||
|
SubTitle: "Tüm ayarları varsayılan değerlere sıfırla",
|
||||||
|
Action: "Hemen sıfırla",
|
||||||
|
Confirm: "Tüm ayarları sıfırlamak istediğinizden emin misiniz?",
|
||||||
},
|
},
|
||||||
Avatar: "Avatar",
|
Clear: {
|
||||||
|
Title: "Tüm verileri temizle",
|
||||||
|
SubTitle: "Tüm sohbet ve ayar verilerini temizle",
|
||||||
|
Action: "Hemen temizle",
|
||||||
|
Confirm:
|
||||||
|
"Tüm sohbet ve ayar verilerini temizlemek istediğinizden emin misiniz?",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Lang: {
|
||||||
|
Name: "Language", // Dikkat: yeni bir çeviri eklemek isterseniz, bu değeri çevirmeyin, `Language` olarak bırakın
|
||||||
|
All: "Tüm diller",
|
||||||
|
},
|
||||||
|
Avatar: "Profil Resmi",
|
||||||
FontSize: {
|
FontSize: {
|
||||||
Title: "Yazı Boyutu",
|
Title: "Yazı Boyutu",
|
||||||
SubTitle: "Sohbet içeriğinin yazı boyutunu ayarlayın",
|
SubTitle: "Sohbet içeriğinin yazı boyutu",
|
||||||
},
|
},
|
||||||
FontFamily: {
|
FontFamily: {
|
||||||
Title: "Sohbet Yazı Tipi",
|
Title: "Sohbet Yazı Tipi",
|
||||||
|
@ -78,165 +173,419 @@ const tr: PartialLocaleType = {
|
||||||
Placeholder: "Yazı Tipi Adı",
|
Placeholder: "Yazı Tipi Adı",
|
||||||
},
|
},
|
||||||
InjectSystemPrompts: {
|
InjectSystemPrompts: {
|
||||||
Title: "Sistem İpucu Ekleyin",
|
Title: "Sistem Seviyesi İpucu Enjeksiyonu",
|
||||||
SubTitle:
|
SubTitle: "Her isteğin başına ChatGPT benzeri bir sistem ipucu ekle",
|
||||||
"Her istek için ileti listesinin başına simüle edilmiş bir ChatGPT sistem ipucu ekleyin",
|
|
||||||
},
|
},
|
||||||
|
InputTemplate: {
|
||||||
|
Title: "Kullanıcı Girdisi Ön İşleme",
|
||||||
|
SubTitle: "Kullanıcının en son mesajı bu şablona doldurulur",
|
||||||
|
},
|
||||||
|
|
||||||
Update: {
|
Update: {
|
||||||
Version: (x: string) => `Sürüm: ${x}`,
|
Version: (x: string) => `Mevcut sürüm: ${x}`,
|
||||||
IsLatest: "En son sürüm",
|
IsLatest: "En son sürüm",
|
||||||
CheckUpdate: "Güncellemeyi Kontrol Et",
|
CheckUpdate: "Güncellemeleri kontrol et",
|
||||||
IsChecking: "Güncelleme kontrol ediliyor...",
|
IsChecking: "Güncellemeler kontrol ediliyor...",
|
||||||
FoundUpdate: (x: string) => `Yeni sürüm bulundu: ${x}`,
|
FoundUpdate: (x: string) => `Yeni sürüm bulundu: ${x}`,
|
||||||
GoToUpdate: "Güncelle",
|
GoToUpdate: "Güncellemeye git",
|
||||||
},
|
},
|
||||||
SendKey: "Gönder Tuşu",
|
SendKey: "Gönderme Tuşu",
|
||||||
Theme: "Tema",
|
Theme: "Tema",
|
||||||
TightBorder: "Tam Ekran",
|
TightBorder: "Sınır Yok Modu",
|
||||||
SendPreviewBubble: {
|
SendPreviewBubble: {
|
||||||
Title: "Mesaj Önizleme Balonu",
|
Title: "Önizleme Balonu",
|
||||||
SubTitle: "Preview markdown in bubble",
|
SubTitle: "Markdown içeriğini önizleme balonunda görüntüle",
|
||||||
|
},
|
||||||
|
AutoGenerateTitle: {
|
||||||
|
Title: "Başlığı Otomatik Oluştur",
|
||||||
|
SubTitle: "Sohbet içeriğine göre uygun başlık oluştur",
|
||||||
|
},
|
||||||
|
Sync: {
|
||||||
|
CloudState: "Bulut Verisi",
|
||||||
|
NotSyncYet: "Henüz senkronize edilmedi",
|
||||||
|
Success: "Senkronizasyon başarılı",
|
||||||
|
Fail: "Senkronizasyon başarısız",
|
||||||
|
|
||||||
|
Config: {
|
||||||
|
Modal: {
|
||||||
|
Title: "Bulut Senkronizasyonu Yapılandır",
|
||||||
|
Check: "Kullanılabilirliği kontrol et",
|
||||||
|
},
|
||||||
|
SyncType: {
|
||||||
|
Title: "Senkronizasyon Türü",
|
||||||
|
SubTitle: "Tercih ettiğiniz senkronizasyon sunucusunu seçin",
|
||||||
|
},
|
||||||
|
Proxy: {
|
||||||
|
Title: "Proxy'yi Etkinleştir",
|
||||||
|
SubTitle:
|
||||||
|
"Tarayıcıda senkronize ederken proxy'yi etkinleştirin, aksi takdirde çapraz kaynak kısıtlamalarıyla karşılaşabilirsiniz",
|
||||||
|
},
|
||||||
|
ProxyUrl: {
|
||||||
|
Title: "Proxy Adresi",
|
||||||
|
SubTitle: "Sadece bu projeye ait çapraz kaynak proxy için",
|
||||||
|
},
|
||||||
|
|
||||||
|
WebDav: {
|
||||||
|
Endpoint: "WebDAV Adresi",
|
||||||
|
UserName: "Kullanıcı Adı",
|
||||||
|
Password: "Şifre",
|
||||||
|
},
|
||||||
|
|
||||||
|
UpStash: {
|
||||||
|
Endpoint: "UpStash Redis REST Url",
|
||||||
|
UserName: "Yedekleme Adı",
|
||||||
|
Password: "UpStash Redis REST Token",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
LocalState: "Yerel Veri",
|
||||||
|
Overview: (overview: any) => {
|
||||||
|
return `${overview.chat} konuşma, ${overview.message} mesaj, ${overview.prompt} ipucu, ${overview.mask} maske`;
|
||||||
|
},
|
||||||
|
ImportFailed: "İçeri aktarma başarısız",
|
||||||
},
|
},
|
||||||
Mask: {
|
Mask: {
|
||||||
Splash: {
|
Splash: {
|
||||||
Title: "Mask Splash Screen",
|
Title: "Maske Başlangıç Sayfası",
|
||||||
SubTitle: "Show a mask splash screen before starting new chat",
|
SubTitle:
|
||||||
|
"Yeni sohbet başlatıldığında maske başlangıç sayfasını göster",
|
||||||
|
},
|
||||||
|
Builtin: {
|
||||||
|
Title: "Yerleşik Maskeleri Gizle",
|
||||||
|
SubTitle: "Tüm maskeler listesindeki yerleşik maskeleri gizle",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Prompt: {
|
Prompt: {
|
||||||
Disable: {
|
Disable: {
|
||||||
Title: "Otomatik tamamlamayı devre dışı bırak",
|
Title: "İpucu Tamamlamayı Devre Dışı Bırak",
|
||||||
SubTitle: "Otomatik tamamlamayı kullanmak için / (eğik çizgi) girin",
|
SubTitle:
|
||||||
|
"Giriş kutusunun başına / yazarak otomatik tamamlamayı tetikle",
|
||||||
},
|
},
|
||||||
List: "Komut Listesi",
|
List: "Özelleştirilmiş İpucu Listesi",
|
||||||
ListCount: (builtin: number, custom: number) =>
|
ListCount: (builtin: number, custom: number) =>
|
||||||
`${builtin} yerleşik, ${custom} kullanıcı tanımlı`,
|
`Yerleşik ${builtin} tane, kullanıcı tanımlı ${custom} tane`,
|
||||||
Edit: "Düzenle",
|
Edit: "Düzenle",
|
||||||
Modal: {
|
Modal: {
|
||||||
Title: "Prompt List",
|
Title: "İpucu Listesi",
|
||||||
Add: "Add One",
|
Add: "Yeni Ekle",
|
||||||
Search: "Search Prompts",
|
Search: "İpucu Ara",
|
||||||
},
|
},
|
||||||
EditModal: {
|
EditModal: {
|
||||||
Title: "Edit Prompt",
|
Title: "İpucu Düzenle",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
HistoryCount: {
|
HistoryCount: {
|
||||||
Title: "Ekli Mesaj Sayısı",
|
Title: "Ekli Geçmiş Mesaj Sayısı",
|
||||||
SubTitle: "İstek başına ekli gönderilen mesaj sayısı",
|
SubTitle: "Her istekte taşınan geçmiş mesaj sayısı",
|
||||||
},
|
},
|
||||||
CompressThreshold: {
|
CompressThreshold: {
|
||||||
Title: "Geçmiş Sıkıştırma Eşiği",
|
Title: "Geçmiş Mesaj Uzunluğu Sıkıştırma Eşiği",
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Sıkıştırılmamış mesajların uzunluğu bu değeri aşarsa sıkıştırılır",
|
"Sıkıştırılmamış geçmiş mesaj bu değeri aştığında sıkıştırma yapılır",
|
||||||
},
|
},
|
||||||
|
|
||||||
Usage: {
|
Usage: {
|
||||||
Title: "Hesap Bakiyesi",
|
Title: "Bakiye Sorgulama",
|
||||||
SubTitle(used: any, total: any) {
|
SubTitle(used: any, total: any) {
|
||||||
return `Bu ay kullanılan $${used}, abonelik $${total}`;
|
return `Bu ay kullanılan $${used}, toplam abonelik ücreti $${total}`;
|
||||||
},
|
},
|
||||||
IsChecking: "Kontrol ediliyor...",
|
IsChecking: "Kontrol ediliyor…",
|
||||||
Check: "Tekrar Kontrol Et",
|
Check: "Yeniden kontrol et",
|
||||||
NoAccess: "Bakiyeyi kontrol etmek için API anahtarını girin",
|
NoAccess:
|
||||||
|
"Bakiye görüntülemek için API Anahtarı veya erişim şifresi girin",
|
||||||
},
|
},
|
||||||
|
|
||||||
Model: "Model",
|
Access: {
|
||||||
Temperature: {
|
SaasStart: {
|
||||||
Title: "Gerçeklik",
|
Title: "NextChat AI kullanın",
|
||||||
|
Label: "(En maliyet etkin çözüm)",
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Daha büyük bir değer girildiğinde gerçeklik oranı düşer ve daha rastgele çıktılar üretir",
|
"NextChat tarafından resmi olarak yönetilmektedir, yapılandırma olmadan hemen kullanıma hazırdır, OpenAI o1, GPT-4o, Claude-3.5 gibi en son büyük modelleri destekler",
|
||||||
|
ChatNow: "Şimdi sohbet et",
|
||||||
|
},
|
||||||
|
|
||||||
|
AccessCode: {
|
||||||
|
Title: "Erişim Şifresi",
|
||||||
|
SubTitle: "Yönetici şifreli erişimi etkinleştirdi",
|
||||||
|
Placeholder: "Erişim şifrenizi girin",
|
||||||
|
},
|
||||||
|
CustomEndpoint: {
|
||||||
|
Title: "Özelleştirilmiş API",
|
||||||
|
SubTitle:
|
||||||
|
"Özelleştirilmiş Azure veya OpenAI hizmeti kullanmak ister misiniz?",
|
||||||
|
},
|
||||||
|
Provider: {
|
||||||
|
Title: "Model Sağlayıcısı",
|
||||||
|
SubTitle: "Farklı sağlayıcılara geçiş yapın",
|
||||||
|
},
|
||||||
|
OpenAI: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "API Anahtarı",
|
||||||
|
SubTitle:
|
||||||
|
"Özelleştirilmiş OpenAI Anahtarı kullanarak şifreli erişim kısıtlamalarını atlayın",
|
||||||
|
Placeholder: "OpenAI API Anahtarı",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "API Adresi",
|
||||||
|
SubTitle: "Varsayılan adres dışında, http(s):// içermelidir",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Azure: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "API Anahtarı",
|
||||||
|
SubTitle:
|
||||||
|
"Özelleştirilmiş Azure Anahtarı kullanarak şifreli erişim kısıtlamalarını atlayın",
|
||||||
|
Placeholder: "Azure API Anahtarı",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "API Adresi",
|
||||||
|
SubTitle: "Örnek:",
|
||||||
|
},
|
||||||
|
|
||||||
|
ApiVerion: {
|
||||||
|
Title: "API Versiyonu (azure api version)",
|
||||||
|
SubTitle: "Belirli bir versiyonu seçin",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Anthropic: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "API Anahtarı",
|
||||||
|
SubTitle:
|
||||||
|
"Özelleştirilmiş Anthropic Anahtarı kullanarak şifreli erişim kısıtlamalarını atlayın",
|
||||||
|
Placeholder: "Anthropic API Anahtarı",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "API Adresi",
|
||||||
|
SubTitle: "Örnek:",
|
||||||
|
},
|
||||||
|
|
||||||
|
ApiVerion: {
|
||||||
|
Title: "API Versiyonu (claude api version)",
|
||||||
|
SubTitle: "Belirli bir API versiyonunu seçin",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Google: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "API Anahtarı",
|
||||||
|
SubTitle: "Google AI'den API Anahtarınızı alın",
|
||||||
|
Placeholder: "Google AI Studio API Anahtarınızı girin",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Uç Nokta Adresi",
|
||||||
|
SubTitle: "Örnek:",
|
||||||
|
},
|
||||||
|
|
||||||
|
ApiVersion: {
|
||||||
|
Title: "API Versiyonu (sadece gemini-pro)",
|
||||||
|
SubTitle: "Belirli bir API versiyonunu seçin",
|
||||||
|
},
|
||||||
|
GoogleSafetySettings: {
|
||||||
|
Title: "Google Güvenlik Filtreleme Seviyesi",
|
||||||
|
SubTitle: "İçerik filtreleme seviyesini ayarlayın",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Baidu: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "API Anahtarı",
|
||||||
|
SubTitle: "Özelleştirilmiş Baidu API Anahtarı kullanın",
|
||||||
|
Placeholder: "Baidu API Anahtarı",
|
||||||
|
},
|
||||||
|
SecretKey: {
|
||||||
|
Title: "Secret Anahtarı",
|
||||||
|
SubTitle: "Özelleştirilmiş Baidu Secret Anahtarı kullanın",
|
||||||
|
Placeholder: "Baidu Secret Anahtarı",
|
||||||
|
},
|
||||||
|
Endpoint: {
|
||||||
|
Title: "API Adresi",
|
||||||
|
SubTitle: "Özelleştirilmiş yapılandırma için .env'ye gidin",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ByteDance: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "API Anahtarı",
|
||||||
|
SubTitle: "Özelleştirilmiş ByteDance API Anahtarı kullanın",
|
||||||
|
Placeholder: "ByteDance API Anahtarı",
|
||||||
|
},
|
||||||
|
Endpoint: {
|
||||||
|
Title: "API Adresi",
|
||||||
|
SubTitle: "Örnek:",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Alibaba: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "API Anahtarı",
|
||||||
|
SubTitle: "Özelleştirilmiş Alibaba Cloud API Anahtarı kullanın",
|
||||||
|
Placeholder: "Alibaba Cloud API Anahtarı",
|
||||||
|
},
|
||||||
|
Endpoint: {
|
||||||
|
Title: "API Adresi",
|
||||||
|
SubTitle: "Örnek:",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CustomModel: {
|
||||||
|
Title: "Özelleştirilmiş Model Adı",
|
||||||
|
SubTitle:
|
||||||
|
"Özelleştirilmiş model seçenekleri ekleyin, İngilizce virgül ile ayırın",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
Model: "Model (model)",
|
||||||
|
CompressModel: {
|
||||||
|
Title: "Sıkıştırma Modeli",
|
||||||
|
SubTitle: "Geçmişi sıkıştırmak için kullanılan model",
|
||||||
|
},
|
||||||
|
Temperature: {
|
||||||
|
Title: "Rastgelelik (temperature)",
|
||||||
|
SubTitle: "Değer arttıkça yanıt daha rastgele olur",
|
||||||
|
},
|
||||||
|
TopP: {
|
||||||
|
Title: "Nükleer Örnekleme (top_p)",
|
||||||
|
SubTitle:
|
||||||
|
"Rastgeleliğe benzer, ancak rastgelelik ile birlikte değiştirmeyin",
|
||||||
},
|
},
|
||||||
MaxTokens: {
|
MaxTokens: {
|
||||||
Title: "Maksimum Belirteç",
|
Title: "Tek Yanıt Limiti (max_tokens)",
|
||||||
SubTitle:
|
SubTitle: "Tek etkileşimde kullanılan maksimum Token sayısı",
|
||||||
"Girdi belirteçlerinin ve oluşturulan belirteçlerin maksimum uzunluğu",
|
|
||||||
},
|
},
|
||||||
PresencePenalty: {
|
PresencePenalty: {
|
||||||
Title: "Varlık Cezası",
|
Title: "Konu Tazeliği (presence_penalty)",
|
||||||
SubTitle:
|
SubTitle: "Değer arttıkça, yeni konulara geçiş olasılığı artar",
|
||||||
"Daha büyük bir değer, yeni konular hakkında konuşma olasılığını artırır",
|
|
||||||
},
|
},
|
||||||
FrequencyPenalty: {
|
FrequencyPenalty: {
|
||||||
Title: "Frekans Cezası",
|
Title: "Frekans Cezası (frequency_penalty)",
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Aynı satırı tekrar etme olasılığını azaltan daha büyük bir değer",
|
"Değer arttıkça, tekrar eden kelimelerin azalması olasılığı artar",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Store: {
|
Store: {
|
||||||
DefaultTopic: "Yeni Konuşma",
|
DefaultTopic: "Yeni Sohbet",
|
||||||
BotHello: "Merhaba! Size bugün nasıl yardımcı olabilirim?",
|
BotHello: "Size nasıl yardımcı olabilirim?",
|
||||||
Error: "Bir şeyler yanlış gitti. Lütfen daha sonra tekrar deneyiniz.",
|
Error: "Bir hata oluştu, lütfen daha sonra tekrar deneyin",
|
||||||
Prompt: {
|
Prompt: {
|
||||||
History: (content: string) =>
|
History: (content: string) => "Bu, geçmiş sohbetin özeti: " + content,
|
||||||
"Bu, yapay zeka ile kullanıcı arasındaki sohbet geçmişinin bir özetidir: " +
|
|
||||||
content,
|
|
||||||
Topic:
|
Topic:
|
||||||
"Lütfen herhangi bir giriş, noktalama işareti, tırnak işareti, nokta, sembol veya ek metin olmadan konuşmamızı özetleyen dört ila beş kelimelik bir başlık oluşturun. Çevreleyen tırnak işaretlerini kaldırın.",
|
"Bu cümlenin dört ila beş kelimelik kısa başlığını doğrudan verin, açıklama yapmayın, noktalama işareti, duygu kelimesi veya fazla metin eklemeyin, kalın yapmayın. Başlık yoksa, doğrudan 'Sohbet' yanıtını verin.",
|
||||||
Summarize:
|
Summarize:
|
||||||
"Gelecekteki bağlam için bir bilgi istemi olarak kullanmak üzere tartışmamızı en fazla 200 kelimeyle özetleyin.",
|
"Sohbet içeriğini kısaca özetleyin, bu özet sonraki bağlam ipucu olarak kullanılacaktır, 200 kelime içinde tutun",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Copy: {
|
Copy: {
|
||||||
Success: "Panoya kopyalandı",
|
Success: "Panoya yazıldı",
|
||||||
Failed: "Kopyalama başarısız oldu, lütfen panoya erişim izni verin",
|
Failed: "Kopyalama başarısız, lütfen panoya erişim izni verin",
|
||||||
|
},
|
||||||
|
Download: {
|
||||||
|
Success: "İçerik dizininize indirildi.",
|
||||||
|
Failed: "İndirme başarısız.",
|
||||||
},
|
},
|
||||||
Context: {
|
Context: {
|
||||||
Toast: (x: any) => `${x} bağlamsal bellek komutu`,
|
Toast: (x: any) => `${x} tane önceden tanımlı ipucu içeriyor`,
|
||||||
Edit: "Bağlamsal ve Bellek Komutları",
|
Edit: "Mevcut sohbet ayarları",
|
||||||
Add: "Yeni Ekle",
|
Add: "Yeni bir sohbet ekle",
|
||||||
|
Clear: "Bağlam temizlendi",
|
||||||
|
Revert: "Bağlamı geri getir",
|
||||||
},
|
},
|
||||||
Plugin: {
|
Plugin: {
|
||||||
Name: "Plugin",
|
Name: "Eklenti",
|
||||||
},
|
},
|
||||||
FineTuned: {
|
FineTuned: {
|
||||||
Sysmessage: "Sen bir asistansın",
|
Sysmessage: "Sen bir asistansın",
|
||||||
},
|
},
|
||||||
Mask: {
|
SearchChat: {
|
||||||
Name: "Mask",
|
Name: "Ara",
|
||||||
Page: {
|
Page: {
|
||||||
Title: "Prompt Template",
|
Title: "Sohbet geçmişini ara",
|
||||||
SubTitle: (count: number) => `${count} prompt templates`,
|
Search: "Arama anahtar kelimelerini girin",
|
||||||
Search: "Search Templates",
|
NoResult: "Sonuç bulunamadı",
|
||||||
Create: "Create",
|
NoData: "Veri yok",
|
||||||
|
Loading: "Yükleniyor",
|
||||||
|
|
||||||
|
SubTitle: (count: number) => `${count} sonuç bulundu`,
|
||||||
},
|
},
|
||||||
Item: {
|
Item: {
|
||||||
Info: (count: number) => `${count} prompts`,
|
View: "Görüntüle",
|
||||||
Chat: "Chat",
|
},
|
||||||
View: "View",
|
},
|
||||||
Edit: "Edit",
|
Mask: {
|
||||||
Delete: "Delete",
|
Name: "Maske",
|
||||||
DeleteConfirm: "Confirm to delete?",
|
Page: {
|
||||||
|
Title: "Önceden Tanımlı Karakter Maskeleri",
|
||||||
|
SubTitle: (count: number) =>
|
||||||
|
`${count} tane önceden tanımlı karakter tanımı`,
|
||||||
|
Search: "Karakter maskesi ara",
|
||||||
|
Create: "Yeni oluştur",
|
||||||
|
},
|
||||||
|
Item: {
|
||||||
|
Info: (count: number) => `${count} tane önceden tanımlı sohbet içeriyor`,
|
||||||
|
Chat: "Sohbet",
|
||||||
|
View: "Görüntüle",
|
||||||
|
Edit: "Düzenle",
|
||||||
|
Delete: "Sil",
|
||||||
|
DeleteConfirm: "Silmek istediğinizden emin misiniz?",
|
||||||
},
|
},
|
||||||
EditModal: {
|
EditModal: {
|
||||||
Title: (readonly: boolean) =>
|
Title: (readonly: boolean) =>
|
||||||
`Edit Prompt Template ${readonly ? "(readonly)" : ""}`,
|
`Önceden Tanımlı Maskeyi Düzenle ${readonly ? " (Salt Okunur)" : ""}`,
|
||||||
Download: "Download",
|
Download: "Önceden Tanımlı Maskeyi İndir",
|
||||||
Clone: "Clone",
|
Clone: "Önceden Tanımlı Maskeyi Kopyala",
|
||||||
},
|
},
|
||||||
Config: {
|
Config: {
|
||||||
Avatar: "Bot Avatar",
|
Avatar: "Karakter Profil Resmi",
|
||||||
Name: "Bot Name",
|
Name: "Karakter Adı",
|
||||||
|
Sync: {
|
||||||
|
Title: "Küresel Ayarları Kullan",
|
||||||
|
SubTitle: "Mevcut sohbet küresel model ayarlarını mı kullanacak?",
|
||||||
|
Confirm:
|
||||||
|
"Mevcut sohbetin özelleştirilmiş ayarları otomatik olarak üzerine yazılacaktır, küresel ayarları etkinleştirmek istediğinizden emin misiniz?",
|
||||||
|
},
|
||||||
|
HideContext: {
|
||||||
|
Title: "Önceden Tanımlı Sohbetleri Gizle",
|
||||||
|
SubTitle:
|
||||||
|
"Gizlendiğinde, önceden tanımlı sohbetler sohbet ekranında görünmeyecek",
|
||||||
|
},
|
||||||
|
Share: {
|
||||||
|
Title: "Bu Maskeyi Paylaş",
|
||||||
|
SubTitle: "Bu maskenin doğrudan bağlantısını oluştur",
|
||||||
|
Action: "Bağlantıyı Kopyala",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
NewChat: {
|
NewChat: {
|
||||||
Return: "Return",
|
Return: "Geri dön",
|
||||||
Skip: "Skip",
|
Skip: "Doğrudan başla",
|
||||||
Title: "Pick a Mask",
|
NotShow: "Bir daha gösterme",
|
||||||
SubTitle: "Chat with the Soul behind the Mask",
|
ConfirmNoShow:
|
||||||
More: "Find More",
|
"Devre dışı bırakmak istediğinizden emin misiniz? Devre dışı bıraktıktan sonra ayarlardan tekrar etkinleştirebilirsiniz.",
|
||||||
NotShow: "Not Show Again",
|
Title: "Bir Maske Seçin",
|
||||||
ConfirmNoShow: "Confirm to disable?You can enable it in settings later.",
|
SubTitle:
|
||||||
|
"Şimdi başlayın ve maskenin arkasındaki zihinle etkileşimde bulunun",
|
||||||
|
More: "Tümünü Gör",
|
||||||
|
},
|
||||||
|
|
||||||
|
URLCommand: {
|
||||||
|
Code: "Bağlantıda erişim kodu bulundu, otomatik olarak doldurulsun mu?",
|
||||||
|
Settings:
|
||||||
|
"Bağlantıda önceden tanımlı ayarlar bulundu, otomatik olarak doldurulsun mu?",
|
||||||
},
|
},
|
||||||
|
|
||||||
UI: {
|
UI: {
|
||||||
Confirm: "Confirm",
|
Confirm: "Onayla",
|
||||||
Cancel: "Cancel",
|
Cancel: "İptal et",
|
||||||
Close: "Close",
|
Close: "Kapat",
|
||||||
Create: "Create",
|
Create: "Yeni oluştur",
|
||||||
Edit: "Edit",
|
Edit: "Düzenle",
|
||||||
|
Export: "Dışa Aktar",
|
||||||
|
Import: "İçe Aktar",
|
||||||
|
Sync: "Senkronize et",
|
||||||
|
Config: "Yapılandır",
|
||||||
},
|
},
|
||||||
Exporter: {
|
Exporter: {
|
||||||
|
Description: {
|
||||||
|
Title: "Sadece bağlam temizlendikten sonraki mesajlar gösterilecektir",
|
||||||
|
},
|
||||||
Model: "Model",
|
Model: "Model",
|
||||||
Messages: "Mesajlar",
|
Messages: "Mesajlar",
|
||||||
Topic: "Konu",
|
Topic: "Konu",
|
||||||
|
|
|
@ -1,14 +1,20 @@
|
||||||
import { getClientConfig } from "../config/client";
|
import { getClientConfig } from "../config/client";
|
||||||
import { SubmitKey } from "../store/config";
|
import { SubmitKey } from "../store/config";
|
||||||
|
import { SAAS_CHAT_UTM_URL } from "@/app/constant";
|
||||||
const isApp = !!getClientConfig()?.isApp;
|
const isApp = !!getClientConfig()?.isApp;
|
||||||
|
|
||||||
const tw = {
|
const tw = {
|
||||||
WIP: "此功能仍在開發中……",
|
WIP: "此功能仍在開發中……",
|
||||||
Error: {
|
Error: {
|
||||||
Unauthorized: isApp
|
Unauthorized: isApp
|
||||||
? "偵測到無效的 API Key,請前往[設定](/#/settings)頁面檢查 API Key 是否設定正確。"
|
? `😆 對話遇到了一些問題,不用慌:
|
||||||
: "存取密碼不正確或尚未填寫,請前往[登入](/#/auth)頁面輸入正確的存取密碼,或者在[設定](/#/settings)頁面填入你自己的 OpenAI API Key。",
|
\\ 1️⃣ 想要零配置開箱即用,[點擊這裡立刻開啟對話 🚀](${SAAS_CHAT_UTM_URL})
|
||||||
|
\\ 2️⃣ 如果你想消耗自己的 OpenAI 資源,點擊[這裡](/#/settings)修改設定 ⚙️`
|
||||||
|
: `😆 對話遇到了一些問題,不用慌:
|
||||||
|
\ 1️⃣ 想要零配置開箱即用,[點擊這裡立刻開啟對話 🚀](${SAAS_CHAT_UTM_URL})
|
||||||
|
\ 2️⃣ 如果你正在使用私有部署版本,點擊[這裡](/#/auth)輸入訪問秘鑰 🔑
|
||||||
|
\ 3️⃣ 如果你想消耗自己的 OpenAI 資源,點擊[這裡](/#/settings)修改設定 ⚙️
|
||||||
|
`,
|
||||||
},
|
},
|
||||||
|
|
||||||
Auth: {
|
Auth: {
|
||||||
|
@ -18,6 +24,10 @@ const tw = {
|
||||||
Input: "在此處填寫存取密碼",
|
Input: "在此處填寫存取密碼",
|
||||||
Confirm: "確認",
|
Confirm: "確認",
|
||||||
Later: "稍候再說",
|
Later: "稍候再說",
|
||||||
|
Return: "返回",
|
||||||
|
SaasTips: "配置太麻煩,想要立即使用",
|
||||||
|
TopTips:
|
||||||
|
"🥳 NextChat AI 首發優惠,立刻解鎖 OpenAI o1, GPT-4o, Claude-3.5 等最新大模型",
|
||||||
},
|
},
|
||||||
ChatItem: {
|
ChatItem: {
|
||||||
ChatItemCount: (count: number) => `${count} 則對話`,
|
ChatItemCount: (count: number) => `${count} 則對話`,
|
||||||
|
@ -43,6 +53,8 @@ const tw = {
|
||||||
PinToastAction: "檢視",
|
PinToastAction: "檢視",
|
||||||
Delete: "刪除",
|
Delete: "刪除",
|
||||||
Edit: "編輯",
|
Edit: "編輯",
|
||||||
|
RefreshTitle: "刷新標題",
|
||||||
|
RefreshToast: "已發送刷新標題請求",
|
||||||
},
|
},
|
||||||
Commands: {
|
Commands: {
|
||||||
new: "新建聊天",
|
new: "新建聊天",
|
||||||
|
@ -81,6 +93,14 @@ const tw = {
|
||||||
SaveAs: "另存新檔",
|
SaveAs: "另存新檔",
|
||||||
},
|
},
|
||||||
IsContext: "預設提示詞",
|
IsContext: "預設提示詞",
|
||||||
|
ShortcutKey: {
|
||||||
|
Title: "鍵盤快捷方式",
|
||||||
|
newChat: "打開新聊天",
|
||||||
|
focusInput: "聚焦輸入框",
|
||||||
|
copyLastMessage: "複製最後一個回覆",
|
||||||
|
copyLastCode: "複製最後一個代碼塊",
|
||||||
|
showShortcutKey: "顯示快捷方式",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Export: {
|
Export: {
|
||||||
Title: "將聊天記錄匯出為 Markdown",
|
Title: "將聊天記錄匯出為 Markdown",
|
||||||
|
@ -277,6 +297,14 @@ const tw = {
|
||||||
},
|
},
|
||||||
|
|
||||||
Access: {
|
Access: {
|
||||||
|
SaasStart: {
|
||||||
|
Title: "使用 NextChat AI",
|
||||||
|
Label: "(性價比最高的方案)",
|
||||||
|
SubTitle:
|
||||||
|
"由 NextChat 官方維護,零配置開箱即用,支持 OpenAI o1、GPT-4o、Claude-3.5 等最新大模型",
|
||||||
|
ChatNow: "立刻對話",
|
||||||
|
},
|
||||||
|
|
||||||
AccessCode: {
|
AccessCode: {
|
||||||
Title: "存取密碼",
|
Title: "存取密碼",
|
||||||
SubTitle: "管理員已開啟加密存取",
|
SubTitle: "管理員已開啟加密存取",
|
||||||
|
@ -360,6 +388,10 @@ const tw = {
|
||||||
},
|
},
|
||||||
|
|
||||||
Model: "模型 (model)",
|
Model: "模型 (model)",
|
||||||
|
CompressModel: {
|
||||||
|
Title: "壓縮模型",
|
||||||
|
SubTitle: "用於壓縮歷史記錄的模型",
|
||||||
|
},
|
||||||
Temperature: {
|
Temperature: {
|
||||||
Title: "隨機性 (temperature)",
|
Title: "隨機性 (temperature)",
|
||||||
SubTitle: "值越大,回應越隨機",
|
SubTitle: "值越大,回應越隨機",
|
||||||
|
@ -452,6 +484,21 @@ const tw = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
SearchChat: {
|
||||||
|
Name: "搜索",
|
||||||
|
Page: {
|
||||||
|
Title: "搜索聊天記錄",
|
||||||
|
Search: "輸入搜索關鍵詞",
|
||||||
|
NoResult: "沒有找到結果",
|
||||||
|
NoData: "沒有數據",
|
||||||
|
Loading: "加載中",
|
||||||
|
|
||||||
|
SubTitle: (count: number) => `找到 ${count} 條結果`,
|
||||||
|
},
|
||||||
|
Item: {
|
||||||
|
View: "查看",
|
||||||
|
},
|
||||||
|
},
|
||||||
NewChat: {
|
NewChat: {
|
||||||
Return: "返回",
|
Return: "返回",
|
||||||
Skip: "跳過",
|
Skip: "跳過",
|
||||||
|
|
|
@ -1,75 +1,169 @@
|
||||||
import { SubmitKey } from "../store/config";
|
import { SubmitKey } from "../store/config";
|
||||||
import type { PartialLocaleType } from "./index";
|
import type { PartialLocaleType } from "./index";
|
||||||
|
import { getClientConfig } from "../config/client";
|
||||||
|
import { SAAS_CHAT_UTM_URL } from "@/app/constant";
|
||||||
|
const isApp = !!getClientConfig()?.isApp;
|
||||||
|
|
||||||
const vi: PartialLocaleType = {
|
const vi: PartialLocaleType = {
|
||||||
WIP: "Sắp ra mắt...",
|
WIP: "Sắp ra mắt...",
|
||||||
Error: {
|
Error: {
|
||||||
Unauthorized:
|
Unauthorized: isApp
|
||||||
"Truy cập chưa xác thực, vui lòng nhập mã truy cập trong trang cài đặt.",
|
? `😆 Cuộc trò chuyện gặp một số vấn đề, đừng lo lắng:
|
||||||
|
\\ 1️⃣ Nếu bạn muốn bắt đầu mà không cần cấu hình, [nhấp vào đây để bắt đầu trò chuyện ngay lập tức 🚀](${SAAS_CHAT_UTM_URL})
|
||||||
|
\\ 2️⃣ Nếu bạn muốn sử dụng tài nguyên OpenAI của riêng mình, hãy nhấp [vào đây](/#/settings) để thay đổi cài đặt ⚙️`
|
||||||
|
: `😆 Cuộc trò chuyện gặp một số vấn đề, đừng lo lắng:
|
||||||
|
\ 1️⃣ Nếu bạn muốn bắt đầu mà không cần cấu hình, [nhấp vào đây để bắt đầu trò chuyện ngay lập tức 🚀](${SAAS_CHAT_UTM_URL})
|
||||||
|
\ 2️⃣ Nếu bạn đang sử dụng phiên bản triển khai riêng, hãy nhấp [vào đây](/#/auth) để nhập khóa truy cập 🔑
|
||||||
|
\ 3️⃣ Nếu bạn muốn sử dụng tài nguyên OpenAI của riêng mình, hãy nhấp [vào đây](/#/settings) để thay đổi cài đặt ⚙️
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
Auth: {
|
||||||
|
Title: "Cần mật khẩu",
|
||||||
|
Tips: "Quản trị viên đã bật xác thực mật khẩu, vui lòng nhập mã truy cập ở dưới",
|
||||||
|
SubTips: "Hoặc nhập khóa API OpenAI hoặc Google của bạn",
|
||||||
|
Input: "Nhập mã truy cập tại đây",
|
||||||
|
Confirm: "Xác nhận",
|
||||||
|
Later: "Để sau",
|
||||||
|
Return: "Trở lại",
|
||||||
|
SaasTips: "Cấu hình quá phức tạp, tôi muốn sử dụng ngay lập tức",
|
||||||
|
TopTips:
|
||||||
|
"🥳 Ưu đãi ra mắt NextChat AI, mở khóa OpenAI o1, GPT-4o, Claude-3.5 và các mô hình lớn mới nhất ngay bây giờ",
|
||||||
},
|
},
|
||||||
ChatItem: {
|
ChatItem: {
|
||||||
ChatItemCount: (count: number) => `${count} tin nhắn`,
|
ChatItemCount: (count: number) => `${count} cuộc trò chuyện`,
|
||||||
},
|
},
|
||||||
Chat: {
|
Chat: {
|
||||||
SubTitle: (count: number) => `${count} tin nhắn với ChatGPT`,
|
SubTitle: (count: number) => `Tổng cộng ${count} cuộc trò chuyện`,
|
||||||
Actions: {
|
EditMessage: {
|
||||||
ChatList: "Xem danh sách chat",
|
Title: "Chỉnh sửa ghi chép tin nhắn",
|
||||||
CompressedHistory: "Nén tin nhắn trong quá khứ",
|
Topic: {
|
||||||
Export: "Xuất tất cả tin nhắn dưới dạng Markdown",
|
Title: "Chủ đề trò chuyện",
|
||||||
Copy: "Sao chép",
|
SubTitle: "Thay đổi chủ đề trò chuyện hiện tại",
|
||||||
Stop: "Dừng",
|
|
||||||
Retry: "Thử lại",
|
|
||||||
Delete: "Xóa",
|
|
||||||
},
|
},
|
||||||
Rename: "Đổi tên",
|
},
|
||||||
|
Actions: {
|
||||||
|
ChatList: "Xem danh sách tin nhắn",
|
||||||
|
CompressedHistory: "Xem lịch sử Prompt đã nén",
|
||||||
|
Export: "Xuất khẩu ghi chép trò chuyện",
|
||||||
|
Copy: "Sao chép",
|
||||||
|
Stop: "Dừng lại",
|
||||||
|
Retry: "Thử lại",
|
||||||
|
Pin: "Ghim",
|
||||||
|
PinToastContent: "Đã ghim 1 cuộc trò chuyện vào lời nhắc đã đặt sẵn",
|
||||||
|
PinToastAction: "Xem",
|
||||||
|
Delete: "Xóa",
|
||||||
|
Edit: "Chỉnh sửa",
|
||||||
|
RefreshTitle: "Làm mới tiêu đề",
|
||||||
|
RefreshToast: "Đã gửi yêu cầu làm mới tiêu đề",
|
||||||
|
},
|
||||||
|
Commands: {
|
||||||
|
new: "Tạo cuộc trò chuyện mới",
|
||||||
|
newm: "Tạo cuộc trò chuyện từ mặt nạ",
|
||||||
|
next: "Cuộc trò chuyện tiếp theo",
|
||||||
|
prev: "Cuộc trò chuyện trước đó",
|
||||||
|
clear: "Xóa ngữ cảnh",
|
||||||
|
del: "Xóa cuộc trò chuyện",
|
||||||
|
},
|
||||||
|
InputActions: {
|
||||||
|
Stop: "Dừng phản hồi",
|
||||||
|
ToBottom: "Cuộn đến tin nhắn mới nhất",
|
||||||
|
Theme: {
|
||||||
|
auto: "Chủ đề tự động",
|
||||||
|
light: "Chế độ sáng",
|
||||||
|
dark: "Chế độ tối",
|
||||||
|
},
|
||||||
|
Prompt: "Lệnh tắt",
|
||||||
|
Masks: "Tất cả mặt nạ",
|
||||||
|
Clear: "Xóa cuộc trò chuyện",
|
||||||
|
Settings: "Cài đặt trò chuyện",
|
||||||
|
UploadImage: "Tải lên hình ảnh",
|
||||||
|
},
|
||||||
|
Rename: "Đổi tên cuộc trò chuyện",
|
||||||
Typing: "Đang nhập…",
|
Typing: "Đang nhập…",
|
||||||
Input: (submitKey: string) => {
|
Input: (submitKey: string) => {
|
||||||
var inputHints = `${submitKey} để gửi`;
|
var inputHints = `${submitKey} gửi`;
|
||||||
if (submitKey === String(SubmitKey.Enter)) {
|
if (submitKey === String(SubmitKey.Enter)) {
|
||||||
inputHints += ", Shift + Enter để xuống dòng";
|
inputHints += ",Shift + Enter xuống dòng";
|
||||||
}
|
}
|
||||||
return inputHints + ", / để tìm kiếm mẫu gợi ý";
|
return inputHints + ",/ kích hoạt hoàn chỉnh, : kích hoạt lệnh";
|
||||||
},
|
},
|
||||||
Send: "Gửi",
|
Send: "Gửi",
|
||||||
Config: {
|
Config: {
|
||||||
Reset: "Khôi phục cài đặt gốc",
|
Reset: "Xóa trí nhớ",
|
||||||
SaveAs: "Lưu dưới dạng Mẫu",
|
SaveAs: "Lưu dưới dạng mặt nạ",
|
||||||
},
|
},
|
||||||
|
IsContext: "Lời nhắc đã đặt sẵn",
|
||||||
},
|
},
|
||||||
Export: {
|
Export: {
|
||||||
Title: "Tất cả tin nhắn",
|
Title: "Chia sẻ ghi chép trò chuyện",
|
||||||
Copy: "Sao chép tất cả",
|
Copy: "Sao chép tất cả",
|
||||||
Download: "Tải xuống",
|
Download: "Tải xuống tệp",
|
||||||
MessageFromYou: "Tin nhắn của bạn",
|
Share: "Chia sẻ lên ShareGPT",
|
||||||
MessageFromChatGPT: "Tin nhắn từ ChatGPT",
|
MessageFromYou: "Người dùng",
|
||||||
|
MessageFromChatGPT: "ChatGPT",
|
||||||
|
Format: {
|
||||||
|
Title: "Định dạng xuất khẩu",
|
||||||
|
SubTitle: "Có thể xuất khẩu dưới dạng văn bản Markdown hoặc hình ảnh PNG",
|
||||||
|
},
|
||||||
|
IncludeContext: {
|
||||||
|
Title: "Bao gồm ngữ cảnh mặt nạ",
|
||||||
|
SubTitle: "Có hiển thị ngữ cảnh mặt nạ trong tin nhắn không",
|
||||||
|
},
|
||||||
|
Steps: {
|
||||||
|
Select: "Chọn",
|
||||||
|
Preview: "Xem trước",
|
||||||
|
},
|
||||||
|
Image: {
|
||||||
|
Toast: "Đang tạo ảnh chụp màn hình",
|
||||||
|
Modal: "Nhấn giữ hoặc nhấp chuột phải để lưu hình ảnh",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Select: {
|
||||||
|
Search: "Tìm kiếm tin nhắn",
|
||||||
|
All: "Chọn tất cả",
|
||||||
|
Latest: "Một vài tin nhắn gần đây",
|
||||||
|
Clear: "Xóa lựa chọn",
|
||||||
},
|
},
|
||||||
Memory: {
|
Memory: {
|
||||||
Title: "Lịch sử tin nhắn",
|
Title: "Tóm tắt lịch sử",
|
||||||
EmptyContent: "Chưa có tin nhắn",
|
EmptyContent: "Nội dung trò chuyện quá ngắn, không cần tóm tắt",
|
||||||
Send: "Gửi tin nhắn trong quá khứ",
|
Send: "Tự động nén ghi chép trò chuyện và gửi dưới dạng ngữ cảnh",
|
||||||
Copy: "Sao chép tin nhắn trong quá khứ",
|
Copy: "Sao chép tóm tắt",
|
||||||
Reset: "Đặt lại phiên",
|
Reset: "[unused]",
|
||||||
ResetConfirm:
|
ResetConfirm: "Xác nhận xóa tóm tắt lịch sử?",
|
||||||
"Đặt lại sẽ xóa toàn bộ lịch sử trò chuyện hiện tại và bộ nhớ. Bạn có chắc chắn muốn đặt lại không?",
|
|
||||||
},
|
},
|
||||||
Home: {
|
Home: {
|
||||||
NewChat: "Cuộc trò chuyện mới",
|
NewChat: "Cuộc trò chuyện mới",
|
||||||
DeleteChat: "Xác nhận xóa các cuộc trò chuyện đã chọn?",
|
DeleteChat: "Xác nhận xóa cuộc trò chuyện đã chọn?",
|
||||||
DeleteToast: "Đã xóa cuộc trò chuyện",
|
DeleteToast: "Đã xóa cuộc trò chuyện",
|
||||||
Revert: "Khôi phục",
|
Revert: "Hoàn tác",
|
||||||
},
|
},
|
||||||
Settings: {
|
Settings: {
|
||||||
Title: "Cài đặt",
|
Title: "Cài đặt",
|
||||||
SubTitle: "Tất cả cài đặt",
|
SubTitle: "Tất cả các tùy chọn cài đặt",
|
||||||
|
|
||||||
|
Danger: {
|
||||||
|
Reset: {
|
||||||
|
Title: "Đặt lại tất cả cài đặt",
|
||||||
|
SubTitle: "Đặt lại tất cả các mục cài đặt về giá trị mặc định",
|
||||||
|
Action: "Đặt lại ngay",
|
||||||
|
Confirm: "Xác nhận đặt lại tất cả cài đặt?",
|
||||||
|
},
|
||||||
|
Clear: {
|
||||||
|
Title: "Xóa tất cả dữ liệu",
|
||||||
|
SubTitle: "Xóa tất cả các cuộc trò chuyện và dữ liệu cài đặt",
|
||||||
|
Action: "Xóa ngay",
|
||||||
|
Confirm: "Xác nhận xóa tất cả cuộc trò chuyện và dữ liệu cài đặt?",
|
||||||
|
},
|
||||||
|
},
|
||||||
Lang: {
|
Lang: {
|
||||||
Name: "Language", // ATTENTION: if you wanna add a new translation, please do not translate this value, leave it as `Language`
|
Name: "Language", // CHÚ Ý: nếu bạn muốn thêm một bản dịch mới, vui lòng không dịch giá trị này, để nó là `Language`
|
||||||
All: "Tất cả ngôn ngữ",
|
All: "Tất cả ngôn ngữ",
|
||||||
},
|
},
|
||||||
Avatar: "Ảnh đại diện",
|
Avatar: "Hình đại diện",
|
||||||
FontSize: {
|
FontSize: {
|
||||||
Title: "Font chữ",
|
Title: "Kích thước chữ",
|
||||||
SubTitle: "Thay đổi font chữ của nội dung trò chuyện",
|
SubTitle: "Kích thước chữ của nội dung trò chuyện",
|
||||||
},
|
},
|
||||||
FontFamily: {
|
FontFamily: {
|
||||||
Title: "Phông Chữ Trò Chuyện",
|
Title: "Phông Chữ Trò Chuyện",
|
||||||
|
@ -78,108 +172,317 @@ const vi: PartialLocaleType = {
|
||||||
Placeholder: "Tên Phông Chữ",
|
Placeholder: "Tên Phông Chữ",
|
||||||
},
|
},
|
||||||
InjectSystemPrompts: {
|
InjectSystemPrompts: {
|
||||||
Title: "Tiêm Prompt Hệ thống",
|
Title: "Tiêm thông báo hệ thống",
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Bắt buộc thêm một prompt hệ thống giả lập ChatGPT ở đầu danh sách tin nhắn cho mỗi yêu cầu",
|
"Buộc thêm một thông báo hệ thống giả ChatGPT vào đầu danh sách tin nhắn mỗi lần yêu cầu",
|
||||||
},
|
},
|
||||||
|
InputTemplate: {
|
||||||
|
Title: "Xử lý đầu vào của người dùng",
|
||||||
|
SubTitle: "Tin nhắn mới nhất của người dùng sẽ được điền vào mẫu này",
|
||||||
|
},
|
||||||
|
|
||||||
Update: {
|
Update: {
|
||||||
Version: (x: string) => `Phiên bản: ${x}`,
|
Version: (x: string) => `Phiên bản hiện tại: ${x}`,
|
||||||
IsLatest: "Phiên bản mới nhất",
|
IsLatest: "Đã là phiên bản mới nhất",
|
||||||
CheckUpdate: "Kiểm tra bản cập nhật",
|
CheckUpdate: "Kiểm tra cập nhật",
|
||||||
IsChecking: "Kiểm tra bản cập nhật...",
|
IsChecking: "Đang kiểm tra cập nhật...",
|
||||||
FoundUpdate: (x: string) => `Phát hiện phiên bản mới: ${x}`,
|
FoundUpdate: (x: string) => `Tìm thấy phiên bản mới: ${x}`,
|
||||||
GoToUpdate: "Cập nhật",
|
GoToUpdate: "Đi đến cập nhật",
|
||||||
},
|
},
|
||||||
SendKey: "Phím gửi",
|
SendKey: "Phím gửi",
|
||||||
Theme: "Theme",
|
Theme: "Giao diện",
|
||||||
TightBorder: "Chế độ không viền",
|
TightBorder: "Chế độ không viền",
|
||||||
SendPreviewBubble: {
|
SendPreviewBubble: {
|
||||||
Title: "Gửi bong bóng xem trước",
|
Title: "Bong bóng xem trước",
|
||||||
SubTitle: "Xem trước nội dung markdown bằng bong bóng",
|
SubTitle: "Xem nội dung Markdown trong bong bóng xem trước",
|
||||||
|
},
|
||||||
|
AutoGenerateTitle: {
|
||||||
|
Title: "Tự động tạo tiêu đề",
|
||||||
|
SubTitle: "Tạo tiêu đề phù hợp dựa trên nội dung cuộc trò chuyện",
|
||||||
|
},
|
||||||
|
Sync: {
|
||||||
|
CloudState: "Dữ liệu đám mây",
|
||||||
|
NotSyncYet: "Chưa thực hiện đồng bộ",
|
||||||
|
Success: "Đồng bộ thành công",
|
||||||
|
Fail: "Đồng bộ thất bại",
|
||||||
|
|
||||||
|
Config: {
|
||||||
|
Modal: {
|
||||||
|
Title: "Cấu hình đồng bộ đám mây",
|
||||||
|
Check: "Kiểm tra khả dụng",
|
||||||
|
},
|
||||||
|
SyncType: {
|
||||||
|
Title: "Loại đồng bộ",
|
||||||
|
SubTitle: "Chọn máy chủ đồng bộ ưa thích",
|
||||||
|
},
|
||||||
|
Proxy: {
|
||||||
|
Title: "Kích hoạt proxy",
|
||||||
|
SubTitle:
|
||||||
|
"Khi đồng bộ qua trình duyệt, cần kích hoạt proxy để tránh hạn chế ngang miền",
|
||||||
|
},
|
||||||
|
ProxyUrl: {
|
||||||
|
Title: "Địa chỉ proxy",
|
||||||
|
SubTitle: "Chỉ áp dụng cho proxy ngang miền của dự án này",
|
||||||
|
},
|
||||||
|
|
||||||
|
WebDav: {
|
||||||
|
Endpoint: "Địa chỉ WebDAV",
|
||||||
|
UserName: "Tên người dùng",
|
||||||
|
Password: "Mật khẩu",
|
||||||
|
},
|
||||||
|
|
||||||
|
UpStash: {
|
||||||
|
Endpoint: "URL UpStash Redis REST",
|
||||||
|
UserName: "Tên sao lưu",
|
||||||
|
Password: "Token UpStash Redis REST",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
LocalState: "Dữ liệu cục bộ",
|
||||||
|
Overview: (overview: any) => {
|
||||||
|
return `${overview.chat} cuộc trò chuyện, ${overview.message} tin nhắn, ${overview.prompt} lệnh, ${overview.mask} mặt nạ`;
|
||||||
|
},
|
||||||
|
ImportFailed: "Nhập không thành công",
|
||||||
},
|
},
|
||||||
Mask: {
|
Mask: {
|
||||||
Splash: {
|
Splash: {
|
||||||
Title: "Mask Splash Screen",
|
Title: "Trang khởi động mặt nạ",
|
||||||
SubTitle: "Chớp màn hình khi bắt đầu cuộc trò chuyện mới",
|
SubTitle: "Hiển thị trang khởi động mặt nạ khi tạo cuộc trò chuyện mới",
|
||||||
|
},
|
||||||
|
Builtin: {
|
||||||
|
Title: "Ẩn mặt nạ tích hợp",
|
||||||
|
SubTitle: "Ẩn mặt nạ tích hợp trong danh sách tất cả mặt nạ",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Prompt: {
|
Prompt: {
|
||||||
Disable: {
|
Disable: {
|
||||||
Title: "Vô hiệu hóa chức năng tự động hoàn thành",
|
Title: "Vô hiệu hóa tự động hoàn thành lệnh",
|
||||||
SubTitle: "Nhập / để kích hoạt chức năng tự động hoàn thành",
|
SubTitle: "Nhập / ở đầu ô nhập để kích hoạt tự động hoàn thành",
|
||||||
},
|
},
|
||||||
List: "Danh sách mẫu gợi ý",
|
List: "Danh sách lệnh tùy chỉnh",
|
||||||
ListCount: (builtin: number, custom: number) =>
|
ListCount: (builtin: number, custom: number) =>
|
||||||
`${builtin} có sẵn, ${custom} do người dùng xác định`,
|
`Tích hợp ${builtin} mục, người dùng định nghĩa ${custom} mục`,
|
||||||
Edit: "Chỉnh sửa",
|
Edit: "Chỉnh sửa",
|
||||||
Modal: {
|
Modal: {
|
||||||
Title: "Danh sách mẫu gợi ý",
|
Title: "Danh sách lệnh",
|
||||||
Add: "Thêm",
|
Add: "Tạo mới",
|
||||||
Search: "Tìm kiếm mẫu",
|
Search: "Tìm kiếm lệnh",
|
||||||
},
|
},
|
||||||
EditModal: {
|
EditModal: {
|
||||||
Title: "Chỉnh sửa mẫu",
|
Title: "Chỉnh sửa lệnh",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
HistoryCount: {
|
HistoryCount: {
|
||||||
Title: "Số lượng tin nhắn đính kèm",
|
Title: "Số tin nhắn lịch sử kèm theo",
|
||||||
SubTitle: "Số lượng tin nhắn trong quá khứ được gửi kèm theo mỗi yêu cầu",
|
SubTitle: "Số tin nhắn lịch sử kèm theo mỗi yêu cầu",
|
||||||
},
|
},
|
||||||
CompressThreshold: {
|
CompressThreshold: {
|
||||||
Title: "Ngưỡng nén lịch sử tin nhắn",
|
Title: "Ngưỡng nén tin nhắn lịch sử",
|
||||||
SubTitle: "Thực hiện nén nếu số lượng tin nhắn chưa nén vượt quá ngưỡng",
|
SubTitle:
|
||||||
|
"Khi tin nhắn lịch sử chưa nén vượt quá giá trị này, sẽ thực hiện nén",
|
||||||
},
|
},
|
||||||
|
|
||||||
Usage: {
|
Usage: {
|
||||||
Title: "Hạn mức tài khoản",
|
Title: "Tra cứu số dư",
|
||||||
SubTitle(used: any, total: any) {
|
SubTitle(used: any, total: any) {
|
||||||
return `Đã sử dụng $${used} trong tháng này, hạn mức $${total}`;
|
return `Đã sử dụng trong tháng: $${used}, Tổng số đăng ký: $${total}`;
|
||||||
},
|
},
|
||||||
IsChecking: "Đang kiểm tra...",
|
IsChecking: "Đang kiểm tra…",
|
||||||
Check: "Kiểm tra",
|
Check: "Kiểm tra lại",
|
||||||
NoAccess: "Nhập API Key để kiểm tra hạn mức",
|
NoAccess: "Nhập khóa API hoặc mật khẩu truy cập để xem số dư",
|
||||||
},
|
},
|
||||||
|
|
||||||
Model: "Mô hình",
|
Access: {
|
||||||
|
SaasStart: {
|
||||||
|
Title: "Sử dụng NextChat AI",
|
||||||
|
Label: "(Giải pháp tiết kiệm chi phí nhất)",
|
||||||
|
SubTitle:
|
||||||
|
"Được NextChat chính thức duy trì, sẵn sàng sử dụng mà không cần cấu hình, hỗ trợ các mô hình lớn mới nhất như OpenAI o1, GPT-4o và Claude-3.5",
|
||||||
|
ChatNow: "Chat ngay",
|
||||||
|
},
|
||||||
|
|
||||||
|
AccessCode: {
|
||||||
|
Title: "Mật khẩu truy cập",
|
||||||
|
SubTitle: "Quản trị viên đã bật truy cập mã hóa",
|
||||||
|
Placeholder: "Nhập mật khẩu truy cập",
|
||||||
|
},
|
||||||
|
CustomEndpoint: {
|
||||||
|
Title: "Giao diện tùy chỉnh",
|
||||||
|
SubTitle: "Có sử dụng dịch vụ Azure hoặc OpenAI tùy chỉnh không",
|
||||||
|
},
|
||||||
|
Provider: {
|
||||||
|
Title: "Nhà cung cấp dịch vụ mô hình",
|
||||||
|
SubTitle: "Chuyển đổi giữa các nhà cung cấp khác nhau",
|
||||||
|
},
|
||||||
|
OpenAI: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "API Key",
|
||||||
|
SubTitle:
|
||||||
|
"Sử dụng khóa OpenAI tùy chỉnh để vượt qua hạn chế truy cập mật khẩu",
|
||||||
|
Placeholder: "Khóa API OpenAI",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Địa chỉ giao diện",
|
||||||
|
SubTitle: "Ngoài địa chỉ mặc định, phải bao gồm http(s)://",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Azure: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "Khóa giao diện",
|
||||||
|
SubTitle:
|
||||||
|
"Sử dụng khóa Azure tùy chỉnh để vượt qua hạn chế truy cập mật khẩu",
|
||||||
|
Placeholder: "Khóa API Azure",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Địa chỉ giao diện",
|
||||||
|
SubTitle: "Ví dụ:",
|
||||||
|
},
|
||||||
|
|
||||||
|
ApiVerion: {
|
||||||
|
Title: "Phiên bản giao diện (phiên bản API azure)",
|
||||||
|
SubTitle: "Chọn phiên bản phần cụ thể",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Anthropic: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "Khóa giao diện",
|
||||||
|
SubTitle:
|
||||||
|
"Sử dụng khóa Anthropic tùy chỉnh để vượt qua hạn chế truy cập mật khẩu",
|
||||||
|
Placeholder: "Khóa API Anthropic",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Địa chỉ giao diện",
|
||||||
|
SubTitle: "Ví dụ:",
|
||||||
|
},
|
||||||
|
|
||||||
|
ApiVerion: {
|
||||||
|
Title: "Phiên bản giao diện (phiên bản API claude)",
|
||||||
|
SubTitle: "Chọn một phiên bản API cụ thể để nhập",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Google: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "Khóa API",
|
||||||
|
SubTitle: "Lấy khóa API từ Google AI",
|
||||||
|
Placeholder: "Nhập khóa API Google AI Studio của bạn",
|
||||||
|
},
|
||||||
|
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Địa chỉ cuối",
|
||||||
|
SubTitle: "Ví dụ:",
|
||||||
|
},
|
||||||
|
|
||||||
|
ApiVersion: {
|
||||||
|
Title: "Phiên bản API (chỉ áp dụng cho gemini-pro)",
|
||||||
|
SubTitle: "Chọn một phiên bản API cụ thể",
|
||||||
|
},
|
||||||
|
GoogleSafetySettings: {
|
||||||
|
Title: "Mức độ lọc an toàn Google",
|
||||||
|
SubTitle: "Cài đặt mức độ lọc nội dung",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Baidu: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "API Key",
|
||||||
|
SubTitle: "Sử dụng khóa Baidu API tùy chỉnh",
|
||||||
|
Placeholder: "Khóa API Baidu",
|
||||||
|
},
|
||||||
|
SecretKey: {
|
||||||
|
Title: "Secret Key",
|
||||||
|
SubTitle: "Sử dụng khóa bí mật Baidu tùy chỉnh",
|
||||||
|
Placeholder: "Khóa bí mật Baidu",
|
||||||
|
},
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Địa chỉ giao diện",
|
||||||
|
SubTitle: "Không hỗ trợ tùy chỉnh, hãy cấu hình trong .env",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ByteDance: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "Khóa giao diện",
|
||||||
|
SubTitle: "Sử dụng khóa ByteDance API tùy chỉnh",
|
||||||
|
Placeholder: "Khóa API ByteDance",
|
||||||
|
},
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Địa chỉ giao diện",
|
||||||
|
SubTitle: "Ví dụ:",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Alibaba: {
|
||||||
|
ApiKey: {
|
||||||
|
Title: "Khóa giao diện",
|
||||||
|
SubTitle: "Sử dụng khóa Alibaba Cloud API tùy chỉnh",
|
||||||
|
Placeholder: "Khóa API Alibaba Cloud",
|
||||||
|
},
|
||||||
|
Endpoint: {
|
||||||
|
Title: "Địa chỉ giao diện",
|
||||||
|
SubTitle: "Ví dụ:",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CustomModel: {
|
||||||
|
Title: "Tên mô hình tùy chỉnh",
|
||||||
|
SubTitle:
|
||||||
|
"Thêm tùy chọn mô hình tùy chỉnh, sử dụng dấu phẩy để phân cách",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
Model: "Mô hình (model)",
|
||||||
|
CompressModel: {
|
||||||
|
Title: "Mô hình nén",
|
||||||
|
SubTitle: "Mô hình được sử dụng để nén lịch sử",
|
||||||
|
},
|
||||||
Temperature: {
|
Temperature: {
|
||||||
Title: "Tính ngẫu nhiên (temperature)",
|
Title: "Độ ngẫu nhiên (temperature)",
|
||||||
SubTitle: "Giá trị càng lớn, câu trả lời càng ngẫu nhiên",
|
SubTitle: "Giá trị càng lớn, câu trả lời càng ngẫu nhiên",
|
||||||
},
|
},
|
||||||
|
TopP: {
|
||||||
|
Title: "Lấy mẫu hạt nhân (top_p)",
|
||||||
|
SubTitle: "Tương tự như độ ngẫu nhiên, nhưng không thay đổi cùng một lúc",
|
||||||
|
},
|
||||||
MaxTokens: {
|
MaxTokens: {
|
||||||
Title: "Giới hạn số lượng token (max_tokens)",
|
Title: "Giới hạn phản hồi (max_tokens)",
|
||||||
SubTitle: "Số lượng token tối đa được sử dụng trong mỗi lần tương tác",
|
SubTitle: "Số Token tối đa cho mỗi tương tác",
|
||||||
},
|
},
|
||||||
PresencePenalty: {
|
PresencePenalty: {
|
||||||
Title: "Chủ đề mới (presence_penalty)",
|
Title: "Độ mới của chủ đề (presence_penalty)",
|
||||||
SubTitle: "Giá trị càng lớn tăng khả năng mở rộng sang các chủ đề mới",
|
SubTitle:
|
||||||
|
"Giá trị càng lớn, khả năng mở rộng đến các chủ đề mới càng cao",
|
||||||
},
|
},
|
||||||
FrequencyPenalty: {
|
FrequencyPenalty: {
|
||||||
Title: "Hình phạt tần suất",
|
Title: "Hình phạt tần suất (frequency_penalty)",
|
||||||
SubTitle: "Giá trị lớn hơn làm giảm khả năng lặp lại cùng một dòng",
|
SubTitle: "Giá trị càng lớn, khả năng giảm từ ngữ lặp lại càng cao",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Store: {
|
Store: {
|
||||||
DefaultTopic: "Cuộc trò chuyện mới",
|
DefaultTopic: "Trò chuyện mới",
|
||||||
BotHello: "Xin chào! Mình có thể giúp gì cho bạn?",
|
BotHello: "Có thể giúp gì cho bạn?",
|
||||||
Error: "Có lỗi xảy ra, vui lòng thử lại sau.",
|
Error: "Đã xảy ra lỗi, vui lòng thử lại sau",
|
||||||
Prompt: {
|
Prompt: {
|
||||||
History: (content: string) =>
|
History: (content: string) =>
|
||||||
"Tóm tắt ngắn gọn cuộc trò chuyện giữa người dùng và AI: " + content,
|
"Đây là tóm tắt cuộc trò chuyện lịch sử như tiền đề: " + content,
|
||||||
Topic:
|
Topic:
|
||||||
"Sử dụng 4 đến 5 từ tóm tắt cuộc trò chuyện này mà không có phần mở đầu, dấu chấm câu, dấu ngoặc kép, dấu chấm, ký hiệu hoặc văn bản bổ sung nào. Loại bỏ các dấu ngoặc kép kèm theo.",
|
'Sử dụng bốn đến năm từ để trả lại chủ đề tóm tắt của câu này, không giải thích, không dấu câu, không từ cảm thán, không văn bản thừa, không in đậm, nếu không có chủ đề, hãy trả lại "Tán gẫu"',
|
||||||
Summarize:
|
Summarize:
|
||||||
"Tóm tắt cuộc trò chuyện này một cách ngắn gọn trong 200 từ hoặc ít hơn để sử dụng làm gợi ý cho ngữ cảnh tiếp theo.",
|
"Tóm tắt nội dung cuộc trò chuyện một cách ngắn gọn, dùng làm gợi ý ngữ cảnh cho các lần sau, giữ trong vòng 200 từ",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Copy: {
|
Copy: {
|
||||||
Success: "Sao chép vào bộ nhớ tạm",
|
Success: "Đã sao chép vào clipboard",
|
||||||
Failed:
|
Failed: "Sao chép thất bại, vui lòng cấp quyền clipboard",
|
||||||
"Sao chép không thành công, vui lòng cấp quyền truy cập vào bộ nhớ tạm",
|
},
|
||||||
|
Download: {
|
||||||
|
Success: "Nội dung đã được tải xuống thư mục của bạn.",
|
||||||
|
Failed: "Tải xuống thất bại.",
|
||||||
},
|
},
|
||||||
Context: {
|
Context: {
|
||||||
Toast: (x: any) => `Sử dụng ${x} tin nhắn chứa ngữ cảnh`,
|
Toast: (x: any) => `Bao gồm ${x} lệnh gợi ý đã định sẵn`,
|
||||||
Edit: "Thiết lập ngữ cảnh và bộ nhớ",
|
Edit: "Cài đặt cuộc trò chuyện hiện tại",
|
||||||
Add: "Thêm tin nhắn",
|
Add: "Thêm một cuộc trò chuyện",
|
||||||
|
Clear: "Ngữ cảnh đã được xóa",
|
||||||
|
Revert: "Khôi phục ngữ cảnh",
|
||||||
},
|
},
|
||||||
Plugin: {
|
Plugin: {
|
||||||
Name: "Plugin",
|
Name: "Plugin",
|
||||||
|
@ -187,53 +490,99 @@ const vi: PartialLocaleType = {
|
||||||
FineTuned: {
|
FineTuned: {
|
||||||
Sysmessage: "Bạn là một trợ lý",
|
Sysmessage: "Bạn là một trợ lý",
|
||||||
},
|
},
|
||||||
Mask: {
|
SearchChat: {
|
||||||
Name: "Mẫu",
|
Name: "Tìm kiếm",
|
||||||
Page: {
|
Page: {
|
||||||
Title: "Mẫu trò chuyện",
|
Title: "Tìm kiếm lịch sử trò chuyện",
|
||||||
SubTitle: (count: number) => `${count} mẫu`,
|
Search: "Nhập từ khóa tìm kiếm",
|
||||||
Search: "Tìm kiếm mẫu",
|
NoResult: "Không tìm thấy kết quả",
|
||||||
Create: "Tạo",
|
NoData: "Không có dữ liệu",
|
||||||
|
Loading: "Đang tải",
|
||||||
|
|
||||||
|
SubTitle: (count: number) => `Tìm thấy ${count} kết quả`,
|
||||||
},
|
},
|
||||||
Item: {
|
Item: {
|
||||||
Info: (count: number) => `${count} tin nhắn`,
|
View: "Xem",
|
||||||
Chat: "Chat",
|
},
|
||||||
View: "Xem trước",
|
},
|
||||||
|
Mask: {
|
||||||
|
Name: "Mặt nạ",
|
||||||
|
Page: {
|
||||||
|
Title: "Mặt nạ vai trò đã định sẵn",
|
||||||
|
SubTitle: (count: number) => `${count} định nghĩa vai trò đã định sẵn`,
|
||||||
|
Search: "Tìm kiếm mặt nạ vai trò",
|
||||||
|
Create: "Tạo mới",
|
||||||
|
},
|
||||||
|
Item: {
|
||||||
|
Info: (count: number) => `Bao gồm ${count} cuộc trò chuyện đã định sẵn`,
|
||||||
|
Chat: "Trò chuyện",
|
||||||
|
View: "Xem",
|
||||||
Edit: "Chỉnh sửa",
|
Edit: "Chỉnh sửa",
|
||||||
Delete: "Xóa",
|
Delete: "Xóa",
|
||||||
DeleteConfirm: "Xác nhận xóa?",
|
DeleteConfirm: "Xác nhận xóa?",
|
||||||
},
|
},
|
||||||
EditModal: {
|
EditModal: {
|
||||||
Title: (readonly: boolean) =>
|
Title: (readonly: boolean) =>
|
||||||
`Chỉnh sửa mẫu ${readonly ? "(chỉ xem)" : ""}`,
|
`Chỉnh sửa mặt nạ định sẵn ${readonly ? "(chỉ đọc)" : ""}`,
|
||||||
Download: "Tải xuống",
|
Download: "Tải xuống mặt nạ",
|
||||||
Clone: "Tạo bản sao",
|
Clone: "Nhân bản mặt nạ",
|
||||||
},
|
},
|
||||||
Config: {
|
Config: {
|
||||||
Avatar: "Ảnh đại diện bot",
|
Avatar: "Hình đại diện vai trò",
|
||||||
Name: "Tên bot",
|
Name: "Tên vai trò",
|
||||||
|
Sync: {
|
||||||
|
Title: "Sử dụng cài đặt toàn cục",
|
||||||
|
SubTitle:
|
||||||
|
"Cuộc trò chuyện hiện tại có sử dụng cài đặt mô hình toàn cục không",
|
||||||
|
Confirm:
|
||||||
|
"Cài đặt tùy chỉnh của cuộc trò chuyện hiện tại sẽ bị ghi đè tự động, xác nhận bật cài đặt toàn cục?",
|
||||||
|
},
|
||||||
|
HideContext: {
|
||||||
|
Title: "Ẩn cuộc trò chuyện đã định sẵn",
|
||||||
|
SubTitle:
|
||||||
|
"Sau khi ẩn, cuộc trò chuyện đã định sẵn sẽ không xuất hiện trong giao diện trò chuyện",
|
||||||
|
},
|
||||||
|
Share: {
|
||||||
|
Title: "Chia sẻ mặt nạ này",
|
||||||
|
SubTitle: "Tạo liên kết trực tiếp đến mặt nạ này",
|
||||||
|
Action: "Sao chép liên kết",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
NewChat: {
|
NewChat: {
|
||||||
Return: "Quay lại",
|
Return: "Trở lại",
|
||||||
Skip: "Bỏ qua",
|
Skip: "Bắt đầu ngay",
|
||||||
Title: "Chọn 1 biểu tượng",
|
NotShow: "Không hiển thị nữa",
|
||||||
SubTitle: "Bắt đầu trò chuyện ẩn sau lớp mặt nạ",
|
ConfirmNoShow:
|
||||||
More: "Tìm thêm",
|
"Xác nhận vô hiệu hóa? Sau khi vô hiệu hóa, bạn có thể bật lại bất cứ lúc nào trong cài đặt.",
|
||||||
NotShow: "Không hiển thị lại",
|
Title: "Chọn một mặt nạ",
|
||||||
ConfirmNoShow: "Xác nhận tắt? Bạn có thể bật lại trong phần cài đặt.",
|
SubTitle: "Bắt đầu ngay, va chạm với suy nghĩ của linh hồn đứng sau mặt nạ",
|
||||||
|
More: "Xem tất cả",
|
||||||
|
},
|
||||||
|
|
||||||
|
URLCommand: {
|
||||||
|
Code: "Phát hiện mã truy cập trong liên kết, có tự động điền không?",
|
||||||
|
Settings:
|
||||||
|
"Phát hiện cài đặt định sẵn trong liên kết, có tự động điền không?",
|
||||||
},
|
},
|
||||||
|
|
||||||
UI: {
|
UI: {
|
||||||
Confirm: "Xác nhận",
|
Confirm: "Xác nhận",
|
||||||
Cancel: "Hủy",
|
Cancel: "Hủy",
|
||||||
Close: "Đóng",
|
Close: "Đóng",
|
||||||
Create: "Tạo",
|
Create: "Tạo mới",
|
||||||
Edit: "Chỉnh sửa",
|
Edit: "Chỉnh sửa",
|
||||||
|
Export: "Xuất",
|
||||||
|
Import: "Nhập",
|
||||||
|
Sync: "Đồng bộ",
|
||||||
|
Config: "Cấu hình",
|
||||||
},
|
},
|
||||||
Exporter: {
|
Exporter: {
|
||||||
|
Description: {
|
||||||
|
Title: "Chỉ tin nhắn sau khi xóa ngữ cảnh mới được hiển thị",
|
||||||
|
},
|
||||||
Model: "Mô hình",
|
Model: "Mô hình",
|
||||||
Messages: "Thông điệp",
|
Messages: "Tin nhắn",
|
||||||
Topic: "Chủ đề",
|
Topic: "Chủ đề",
|
||||||
Time: "Thời gian",
|
Time: "Thời gian",
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
import { Mask } from "../store/mask";
|
import { Mask } from "../store/mask";
|
||||||
import { CN_MASKS } from "./cn";
|
|
||||||
import { TW_MASKS } from "./tw";
|
|
||||||
import { EN_MASKS } from "./en";
|
|
||||||
|
|
||||||
import { type BuiltinMask } from "./typing";
|
import { type BuiltinMask } from "./typing";
|
||||||
export { type BuiltinMask } from "./typing";
|
export { type BuiltinMask } from "./typing";
|
||||||
|
|
|
@ -51,6 +51,10 @@ const DEFAULT_STABILITY_URL = isApp
|
||||||
? DEFAULT_API_HOST + "/api/proxy/stability"
|
? DEFAULT_API_HOST + "/api/proxy/stability"
|
||||||
: ApiPath.Stability;
|
: ApiPath.Stability;
|
||||||
|
|
||||||
|
const DEFAULT_IFLYTEK_URL = isApp
|
||||||
|
? DEFAULT_API_HOST + "/api/proxy/iflytek"
|
||||||
|
: ApiPath.Iflytek;
|
||||||
|
|
||||||
const DEFAULT_ACCESS_STATE = {
|
const DEFAULT_ACCESS_STATE = {
|
||||||
accessCode: "",
|
accessCode: "",
|
||||||
useCustomConfig: false,
|
useCustomConfig: false,
|
||||||
|
@ -103,6 +107,11 @@ const DEFAULT_ACCESS_STATE = {
|
||||||
tencentSecretKey: "",
|
tencentSecretKey: "",
|
||||||
tencentSecretId: "",
|
tencentSecretId: "",
|
||||||
|
|
||||||
|
// iflytek
|
||||||
|
iflytekUrl: DEFAULT_IFLYTEK_URL,
|
||||||
|
iflytekApiKey: "",
|
||||||
|
iflytekApiSecret: "",
|
||||||
|
|
||||||
// server config
|
// server config
|
||||||
needCode: true,
|
needCode: true,
|
||||||
hideUserApiKey: false,
|
hideUserApiKey: false,
|
||||||
|
@ -111,6 +120,9 @@ const DEFAULT_ACCESS_STATE = {
|
||||||
disableFastLink: false,
|
disableFastLink: false,
|
||||||
customModels: "",
|
customModels: "",
|
||||||
defaultModel: "",
|
defaultModel: "",
|
||||||
|
|
||||||
|
// tts config
|
||||||
|
edgeTTSVoiceName: "zh-CN-YunxiNeural",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useAccessStore = createPersistStore(
|
export const useAccessStore = createPersistStore(
|
||||||
|
@ -123,6 +135,12 @@ export const useAccessStore = createPersistStore(
|
||||||
return get().needCode;
|
return get().needCode;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
edgeVoiceName() {
|
||||||
|
this.fetch();
|
||||||
|
|
||||||
|
return get().edgeTTSVoiceName;
|
||||||
|
},
|
||||||
|
|
||||||
isValidOpenAI() {
|
isValidOpenAI() {
|
||||||
return ensure(get(), ["openaiApiKey"]);
|
return ensure(get(), ["openaiApiKey"]);
|
||||||
},
|
},
|
||||||
|
@ -158,6 +176,9 @@ export const useAccessStore = createPersistStore(
|
||||||
isValidMoonshot() {
|
isValidMoonshot() {
|
||||||
return ensure(get(), ["moonshotApiKey"]);
|
return ensure(get(), ["moonshotApiKey"]);
|
||||||
},
|
},
|
||||||
|
isValidIflytek() {
|
||||||
|
return ensure(get(), ["iflytekApiKey"]);
|
||||||
|
},
|
||||||
|
|
||||||
isAuthorized() {
|
isAuthorized() {
|
||||||
this.fetch();
|
this.fetch();
|
||||||
|
@ -171,8 +192,9 @@ export const useAccessStore = createPersistStore(
|
||||||
this.isValidBaidu() ||
|
this.isValidBaidu() ||
|
||||||
this.isValidByteDance() ||
|
this.isValidByteDance() ||
|
||||||
this.isValidAlibaba() ||
|
this.isValidAlibaba() ||
|
||||||
this.isValidTencent ||
|
this.isValidTencent() ||
|
||||||
this.isValidMoonshot() ||
|
this.isValidMoonshot() ||
|
||||||
|
this.isValidIflytek() ||
|
||||||
!this.enabledAccessControl() ||
|
!this.enabledAccessControl() ||
|
||||||
(this.enabledAccessControl() && ensure(get(), ["accessCode"]))
|
(this.enabledAccessControl() && ensure(get(), ["accessCode"]))
|
||||||
);
|
);
|
||||||
|
@ -189,10 +211,13 @@ export const useAccessStore = createPersistStore(
|
||||||
})
|
})
|
||||||
.then((res) => res.json())
|
.then((res) => res.json())
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
// Set default model from env request
|
const defaultModel = res.defaultModel ?? "";
|
||||||
let defaultModel = res.defaultModel ?? "";
|
if (defaultModel !== "") {
|
||||||
DEFAULT_CONFIG.modelConfig.model =
|
const [model, providerName] = defaultModel.split("@");
|
||||||
defaultModel !== "" ? defaultModel : "gpt-3.5-turbo";
|
DEFAULT_CONFIG.modelConfig.model = model;
|
||||||
|
DEFAULT_CONFIG.modelConfig.providerName = providerName;
|
||||||
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
})
|
})
|
||||||
.then((res: DangerConfig) => {
|
.then((res: DangerConfig) => {
|
||||||
|
|
|
@ -1,32 +1,43 @@
|
||||||
import { trimTopic, getMessageTextContent } from "../utils";
|
import { getMessageTextContent, trimTopic } from "../utils";
|
||||||
|
|
||||||
import Locale, { getLang } from "../locales";
|
import { indexedDBStorage } from "@/app/utils/indexedDB-storage";
|
||||||
|
import { nanoid } from "nanoid";
|
||||||
|
import type {
|
||||||
|
ClientApi,
|
||||||
|
MultimodalContent,
|
||||||
|
RequestMessage,
|
||||||
|
} from "../client/api";
|
||||||
|
import { getClientApi } from "../client/api";
|
||||||
|
import { ChatControllerPool } from "../client/controller";
|
||||||
import { showToast } from "../components/ui-lib";
|
import { showToast } from "../components/ui-lib";
|
||||||
import { ModelConfig, ModelType, useAppConfig } from "./config";
|
|
||||||
import { createEmptyMask, Mask } from "./mask";
|
|
||||||
import {
|
import {
|
||||||
DEFAULT_INPUT_TEMPLATE,
|
DEFAULT_INPUT_TEMPLATE,
|
||||||
DEFAULT_MODELS,
|
DEFAULT_MODELS,
|
||||||
DEFAULT_SYSTEM_TEMPLATE,
|
DEFAULT_SYSTEM_TEMPLATE,
|
||||||
KnowledgeCutOffDate,
|
KnowledgeCutOffDate,
|
||||||
StoreKey,
|
StoreKey,
|
||||||
SUMMARIZE_MODEL,
|
|
||||||
GEMINI_SUMMARIZE_MODEL,
|
|
||||||
} from "../constant";
|
} from "../constant";
|
||||||
import { getClientApi } from "../client/api";
|
import Locale, { getLang } from "../locales";
|
||||||
import type {
|
import { isDalle3, safeLocalStorage } from "../utils";
|
||||||
ClientApi,
|
|
||||||
RequestMessage,
|
|
||||||
MultimodalContent,
|
|
||||||
} from "../client/api";
|
|
||||||
import { ChatControllerPool } from "../client/controller";
|
|
||||||
import { prettyObject } from "../utils/format";
|
import { prettyObject } from "../utils/format";
|
||||||
import { estimateTokenLength } from "../utils/token";
|
|
||||||
import { nanoid } from "nanoid";
|
|
||||||
import { createPersistStore } from "../utils/store";
|
import { createPersistStore } from "../utils/store";
|
||||||
import { collectModelsWithDefaultModel } from "../utils/model";
|
import { estimateTokenLength } from "../utils/token";
|
||||||
import { useAccessStore } from "./access";
|
import { ModelConfig, ModelType, useAppConfig } from "./config";
|
||||||
import { isDalle3 } from "../utils";
|
import { createEmptyMask, Mask } from "./mask";
|
||||||
|
|
||||||
|
const localStorage = safeLocalStorage();
|
||||||
|
|
||||||
|
export type ChatMessageTool = {
|
||||||
|
id: string;
|
||||||
|
index?: number;
|
||||||
|
type?: string;
|
||||||
|
function?: {
|
||||||
|
name: string;
|
||||||
|
arguments?: string;
|
||||||
|
};
|
||||||
|
content?: string;
|
||||||
|
isError?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
export type ChatMessage = RequestMessage & {
|
export type ChatMessage = RequestMessage & {
|
||||||
date: string;
|
date: string;
|
||||||
|
@ -34,6 +45,7 @@ export type ChatMessage = RequestMessage & {
|
||||||
isError?: boolean;
|
isError?: boolean;
|
||||||
id: string;
|
id: string;
|
||||||
model?: ModelType;
|
model?: ModelType;
|
||||||
|
tools?: ChatMessageTool[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export function createMessage(override: Partial<ChatMessage>): ChatMessage {
|
export function createMessage(override: Partial<ChatMessage>): ChatMessage {
|
||||||
|
@ -90,27 +102,6 @@ function createEmptySession(): ChatSession {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSummarizeModel(currentModel: string) {
|
|
||||||
// if it is using gpt-* models, force to use 4o-mini to summarize
|
|
||||||
if (currentModel.startsWith("gpt")) {
|
|
||||||
const configStore = useAppConfig.getState();
|
|
||||||
const accessStore = useAccessStore.getState();
|
|
||||||
const allModel = collectModelsWithDefaultModel(
|
|
||||||
configStore.models,
|
|
||||||
[configStore.customModels, accessStore.customModels].join(","),
|
|
||||||
accessStore.defaultModel,
|
|
||||||
);
|
|
||||||
const summarizeModel = allModel.find(
|
|
||||||
(m) => m.name === SUMMARIZE_MODEL && m.available,
|
|
||||||
);
|
|
||||||
return summarizeModel?.name ?? currentModel;
|
|
||||||
}
|
|
||||||
if (currentModel.startsWith("gemini")) {
|
|
||||||
return GEMINI_SUMMARIZE_MODEL;
|
|
||||||
}
|
|
||||||
return currentModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
function countMessages(msgs: ChatMessage[]) {
|
function countMessages(msgs: ChatMessage[]) {
|
||||||
return msgs.reduce(
|
return msgs.reduce(
|
||||||
(pre, cur) => pre + estimateTokenLength(getMessageTextContent(cur)),
|
(pre, cur) => pre + estimateTokenLength(getMessageTextContent(cur)),
|
||||||
|
@ -165,6 +156,7 @@ function fillTemplateWith(input: string, modelConfig: ModelConfig) {
|
||||||
const DEFAULT_CHAT_STATE = {
|
const DEFAULT_CHAT_STATE = {
|
||||||
sessions: [createEmptySession()],
|
sessions: [createEmptySession()],
|
||||||
currentSessionIndex: 0,
|
currentSessionIndex: 0,
|
||||||
|
lastInput: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useChatStore = createPersistStore(
|
export const useChatStore = createPersistStore(
|
||||||
|
@ -178,6 +170,28 @@ export const useChatStore = createPersistStore(
|
||||||
}
|
}
|
||||||
|
|
||||||
const methods = {
|
const methods = {
|
||||||
|
forkSession() {
|
||||||
|
// 获取当前会话
|
||||||
|
const currentSession = get().currentSession();
|
||||||
|
if (!currentSession) return;
|
||||||
|
|
||||||
|
const newSession = createEmptySession();
|
||||||
|
|
||||||
|
newSession.topic = currentSession.topic;
|
||||||
|
newSession.messages = [...currentSession.messages];
|
||||||
|
newSession.mask = {
|
||||||
|
...currentSession.mask,
|
||||||
|
modelConfig: {
|
||||||
|
...currentSession.mask.modelConfig,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
set((state) => ({
|
||||||
|
currentSessionIndex: 0,
|
||||||
|
sessions: [newSession, ...state.sessions],
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
clearSessions() {
|
clearSessions() {
|
||||||
set(() => ({
|
set(() => ({
|
||||||
sessions: [createEmptySession()],
|
sessions: [createEmptySession()],
|
||||||
|
@ -389,8 +403,24 @@ export const useChatStore = createPersistStore(
|
||||||
}
|
}
|
||||||
ChatControllerPool.remove(session.id, botMessage.id);
|
ChatControllerPool.remove(session.id, botMessage.id);
|
||||||
},
|
},
|
||||||
|
onBeforeTool(tool: ChatMessageTool) {
|
||||||
|
(botMessage.tools = botMessage?.tools || []).push(tool);
|
||||||
|
get().updateCurrentSession((session) => {
|
||||||
|
session.messages = session.messages.concat();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onAfterTool(tool: ChatMessageTool) {
|
||||||
|
botMessage?.tools?.forEach((t, i, tools) => {
|
||||||
|
if (tool.id == t.id) {
|
||||||
|
tools[i] = { ...tool };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
get().updateCurrentSession((session) => {
|
||||||
|
session.messages = session.messages.concat();
|
||||||
|
});
|
||||||
|
},
|
||||||
onError(error) {
|
onError(error) {
|
||||||
const isAborted = error.message.includes("aborted");
|
const isAborted = error.message?.includes?.("aborted");
|
||||||
botMessage.content +=
|
botMessage.content +=
|
||||||
"\n\n" +
|
"\n\n" +
|
||||||
prettyObject({
|
prettyObject({
|
||||||
|
@ -446,7 +476,8 @@ export const useChatStore = createPersistStore(
|
||||||
// system prompts, to get close to OpenAI Web ChatGPT
|
// system prompts, to get close to OpenAI Web ChatGPT
|
||||||
const shouldInjectSystemPrompts =
|
const shouldInjectSystemPrompts =
|
||||||
modelConfig.enableInjectSystemPrompts &&
|
modelConfig.enableInjectSystemPrompts &&
|
||||||
session.mask.modelConfig.model.startsWith("gpt-");
|
(session.mask.modelConfig.model.startsWith("gpt-") ||
|
||||||
|
session.mask.modelConfig.model.startsWith("chatgpt-"));
|
||||||
|
|
||||||
var systemPrompts: ChatMessage[] = [];
|
var systemPrompts: ChatMessage[] = [];
|
||||||
systemPrompts = shouldInjectSystemPrompts
|
systemPrompts = shouldInjectSystemPrompts
|
||||||
|
@ -538,7 +569,7 @@ export const useChatStore = createPersistStore(
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
summarizeSession() {
|
summarizeSession(refreshTitle: boolean = false) {
|
||||||
const config = useAppConfig.getState();
|
const config = useAppConfig.getState();
|
||||||
const session = get().currentSession();
|
const session = get().currentSession();
|
||||||
const modelConfig = session.mask.modelConfig;
|
const modelConfig = session.mask.modelConfig;
|
||||||
|
@ -547,7 +578,7 @@ export const useChatStore = createPersistStore(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const providerName = modelConfig.providerName;
|
const providerName = modelConfig.compressProviderName;
|
||||||
const api: ClientApi = getClientApi(providerName);
|
const api: ClientApi = getClientApi(providerName);
|
||||||
|
|
||||||
// remove error messages if any
|
// remove error messages if any
|
||||||
|
@ -556,11 +587,21 @@ export const useChatStore = createPersistStore(
|
||||||
// should summarize topic after chating more than 50 words
|
// should summarize topic after chating more than 50 words
|
||||||
const SUMMARIZE_MIN_LEN = 50;
|
const SUMMARIZE_MIN_LEN = 50;
|
||||||
if (
|
if (
|
||||||
config.enableAutoGenerateTitle &&
|
(config.enableAutoGenerateTitle &&
|
||||||
session.topic === DEFAULT_TOPIC &&
|
session.topic === DEFAULT_TOPIC &&
|
||||||
countMessages(messages) >= SUMMARIZE_MIN_LEN
|
countMessages(messages) >= SUMMARIZE_MIN_LEN) ||
|
||||||
|
refreshTitle
|
||||||
) {
|
) {
|
||||||
const topicMessages = messages.concat(
|
const startIndex = Math.max(
|
||||||
|
0,
|
||||||
|
messages.length - modelConfig.historyMessageCount,
|
||||||
|
);
|
||||||
|
const topicMessages = messages
|
||||||
|
.slice(
|
||||||
|
startIndex < messages.length ? startIndex : messages.length - 1,
|
||||||
|
messages.length,
|
||||||
|
)
|
||||||
|
.concat(
|
||||||
createMessage({
|
createMessage({
|
||||||
role: "user",
|
role: "user",
|
||||||
content: Locale.Store.Prompt.Topic,
|
content: Locale.Store.Prompt.Topic,
|
||||||
|
@ -569,11 +610,12 @@ export const useChatStore = createPersistStore(
|
||||||
api.llm.chat({
|
api.llm.chat({
|
||||||
messages: topicMessages,
|
messages: topicMessages,
|
||||||
config: {
|
config: {
|
||||||
model: getSummarizeModel(session.mask.modelConfig.model),
|
model: modelConfig.compressModel,
|
||||||
stream: false,
|
stream: false,
|
||||||
providerName,
|
providerName,
|
||||||
},
|
},
|
||||||
onFinish(message) {
|
onFinish(message) {
|
||||||
|
if (!isValidMessage(message)) return;
|
||||||
get().updateCurrentSession(
|
get().updateCurrentSession(
|
||||||
(session) =>
|
(session) =>
|
||||||
(session.topic =
|
(session.topic =
|
||||||
|
@ -632,7 +674,7 @@ export const useChatStore = createPersistStore(
|
||||||
config: {
|
config: {
|
||||||
...modelcfg,
|
...modelcfg,
|
||||||
stream: true,
|
stream: true,
|
||||||
model: getSummarizeModel(session.mask.modelConfig.model),
|
model: modelConfig.compressModel,
|
||||||
},
|
},
|
||||||
onUpdate(message) {
|
onUpdate(message) {
|
||||||
session.memoryPrompt = message;
|
session.memoryPrompt = message;
|
||||||
|
@ -649,6 +691,10 @@ export const useChatStore = createPersistStore(
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isValidMessage(message: any): boolean {
|
||||||
|
return typeof message === "string" && !message.startsWith("```json");
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
updateStat(message: ChatMessage) {
|
updateStat(message: ChatMessage) {
|
||||||
|
@ -665,17 +711,23 @@ export const useChatStore = createPersistStore(
|
||||||
set(() => ({ sessions }));
|
set(() => ({ sessions }));
|
||||||
},
|
},
|
||||||
|
|
||||||
clearAllData() {
|
async clearAllData() {
|
||||||
|
await indexedDBStorage.clear();
|
||||||
localStorage.clear();
|
localStorage.clear();
|
||||||
location.reload();
|
location.reload();
|
||||||
},
|
},
|
||||||
|
setLastInput(lastInput: string) {
|
||||||
|
set({
|
||||||
|
lastInput,
|
||||||
|
});
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return methods;
|
return methods;
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: StoreKey.Chat,
|
name: StoreKey.Chat,
|
||||||
version: 3.1,
|
version: 3.2,
|
||||||
migrate(persistedState, version) {
|
migrate(persistedState, version) {
|
||||||
const state = persistedState as any;
|
const state = persistedState as any;
|
||||||
const newState = JSON.parse(
|
const newState = JSON.parse(
|
||||||
|
@ -722,6 +774,16 @@ export const useChatStore = createPersistStore(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add default summarize model for every session
|
||||||
|
if (version < 3.2) {
|
||||||
|
newState.sessions.forEach((s) => {
|
||||||
|
const config = useAppConfig.getState();
|
||||||
|
s.mask.modelConfig.compressModel = config.modelConfig.compressModel;
|
||||||
|
s.mask.modelConfig.compressProviderName =
|
||||||
|
config.modelConfig.compressProviderName;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return newState as any;
|
return newState as any;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,16 +1,25 @@
|
||||||
import { LLMModel } from "../client/api";
|
import { LLMModel } from "../client/api";
|
||||||
import { DalleSize } from "../typing";
|
import { DalleSize, DalleQuality, DalleStyle } from "../typing";
|
||||||
import { getClientConfig } from "../config/client";
|
import { getClientConfig } from "../config/client";
|
||||||
import {
|
import {
|
||||||
DEFAULT_INPUT_TEMPLATE,
|
DEFAULT_INPUT_TEMPLATE,
|
||||||
DEFAULT_MODELS,
|
DEFAULT_MODELS,
|
||||||
DEFAULT_SIDEBAR_WIDTH,
|
DEFAULT_SIDEBAR_WIDTH,
|
||||||
|
DEFAULT_TTS_ENGINE,
|
||||||
|
DEFAULT_TTS_ENGINES,
|
||||||
|
DEFAULT_TTS_MODEL,
|
||||||
|
DEFAULT_TTS_MODELS,
|
||||||
|
DEFAULT_TTS_VOICE,
|
||||||
|
DEFAULT_TTS_VOICES,
|
||||||
StoreKey,
|
StoreKey,
|
||||||
ServiceProvider,
|
ServiceProvider,
|
||||||
} from "../constant";
|
} from "../constant";
|
||||||
import { createPersistStore } from "../utils/store";
|
import { createPersistStore } from "../utils/store";
|
||||||
|
|
||||||
export type ModelType = (typeof DEFAULT_MODELS)[number]["name"];
|
export type ModelType = (typeof DEFAULT_MODELS)[number]["name"];
|
||||||
|
export type TTSModelType = (typeof DEFAULT_TTS_MODELS)[number];
|
||||||
|
export type TTSVoiceType = (typeof DEFAULT_TTS_VOICES)[number];
|
||||||
|
export type TTSEngineType = (typeof DEFAULT_TTS_ENGINES)[number];
|
||||||
|
|
||||||
export enum SubmitKey {
|
export enum SubmitKey {
|
||||||
Enter = "Enter",
|
Enter = "Enter",
|
||||||
|
@ -41,6 +50,8 @@ export const DEFAULT_CONFIG = {
|
||||||
enableAutoGenerateTitle: true,
|
enableAutoGenerateTitle: true,
|
||||||
sidebarWidth: DEFAULT_SIDEBAR_WIDTH,
|
sidebarWidth: DEFAULT_SIDEBAR_WIDTH,
|
||||||
|
|
||||||
|
enableArtifacts: true, // show artifacts config
|
||||||
|
|
||||||
disablePromptHint: false,
|
disablePromptHint: false,
|
||||||
|
|
||||||
dontShowMaskSplashScreen: false, // dont show splash screen when create chat
|
dontShowMaskSplashScreen: false, // dont show splash screen when create chat
|
||||||
|
@ -50,7 +61,7 @@ export const DEFAULT_CONFIG = {
|
||||||
models: DEFAULT_MODELS as any as LLMModel[],
|
models: DEFAULT_MODELS as any as LLMModel[],
|
||||||
|
|
||||||
modelConfig: {
|
modelConfig: {
|
||||||
model: "gpt-3.5-turbo" as ModelType,
|
model: "gpt-4o-mini" as ModelType,
|
||||||
providerName: "OpenAI" as ServiceProvider,
|
providerName: "OpenAI" as ServiceProvider,
|
||||||
temperature: 0.5,
|
temperature: 0.5,
|
||||||
top_p: 1,
|
top_p: 1,
|
||||||
|
@ -60,15 +71,29 @@ export const DEFAULT_CONFIG = {
|
||||||
sendMemory: true,
|
sendMemory: true,
|
||||||
historyMessageCount: 4,
|
historyMessageCount: 4,
|
||||||
compressMessageLengthThreshold: 1000,
|
compressMessageLengthThreshold: 1000,
|
||||||
|
compressModel: "gpt-4o-mini" as ModelType,
|
||||||
|
compressProviderName: "OpenAI" as ServiceProvider,
|
||||||
enableInjectSystemPrompts: true,
|
enableInjectSystemPrompts: true,
|
||||||
template: config?.template ?? DEFAULT_INPUT_TEMPLATE,
|
template: config?.template ?? DEFAULT_INPUT_TEMPLATE,
|
||||||
size: "1024x1024" as DalleSize,
|
size: "1024x1024" as DalleSize,
|
||||||
|
quality: "standard" as DalleQuality,
|
||||||
|
style: "vivid" as DalleStyle,
|
||||||
|
},
|
||||||
|
|
||||||
|
ttsConfig: {
|
||||||
|
enable: false,
|
||||||
|
autoplay: false,
|
||||||
|
engine: DEFAULT_TTS_ENGINE,
|
||||||
|
model: DEFAULT_TTS_MODEL,
|
||||||
|
voice: DEFAULT_TTS_VOICE,
|
||||||
|
speed: 1.0,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ChatConfig = typeof DEFAULT_CONFIG;
|
export type ChatConfig = typeof DEFAULT_CONFIG;
|
||||||
|
|
||||||
export type ModelConfig = ChatConfig["modelConfig"];
|
export type ModelConfig = ChatConfig["modelConfig"];
|
||||||
|
export type TTSConfig = ChatConfig["ttsConfig"];
|
||||||
|
|
||||||
export function limitNumber(
|
export function limitNumber(
|
||||||
x: number,
|
x: number,
|
||||||
|
@ -83,6 +108,21 @@ export function limitNumber(
|
||||||
return Math.min(max, Math.max(min, x));
|
return Math.min(max, Math.max(min, x));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const TTSConfigValidator = {
|
||||||
|
engine(x: string) {
|
||||||
|
return x as TTSEngineType;
|
||||||
|
},
|
||||||
|
model(x: string) {
|
||||||
|
return x as TTSModelType;
|
||||||
|
},
|
||||||
|
voice(x: string) {
|
||||||
|
return x as TTSVoiceType;
|
||||||
|
},
|
||||||
|
speed(x: number) {
|
||||||
|
return limitNumber(x, 0.25, 4.0, 1.0);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export const ModalConfigValidator = {
|
export const ModalConfigValidator = {
|
||||||
model(x: string) {
|
model(x: string) {
|
||||||
return x as ModelType;
|
return x as ModelType;
|
||||||
|
@ -138,7 +178,22 @@ export const useAppConfig = createPersistStore(
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
name: StoreKey.Config,
|
name: StoreKey.Config,
|
||||||
version: 3.9,
|
version: 4,
|
||||||
|
|
||||||
|
merge(persistedState, currentState) {
|
||||||
|
const state = persistedState as ChatConfig | undefined;
|
||||||
|
if (!state) return { ...currentState };
|
||||||
|
const models = currentState.models.slice();
|
||||||
|
state.models.forEach((pModel) => {
|
||||||
|
const idx = models.findIndex(
|
||||||
|
(v) => v.name === pModel.name && v.provider === pModel.provider,
|
||||||
|
);
|
||||||
|
if (idx !== -1) models[idx] = pModel;
|
||||||
|
else models.push(pModel);
|
||||||
|
});
|
||||||
|
return { ...currentState, ...state, models: models };
|
||||||
|
},
|
||||||
|
|
||||||
migrate(persistedState, version) {
|
migrate(persistedState, version) {
|
||||||
const state = persistedState as ChatConfig;
|
const state = persistedState as ChatConfig;
|
||||||
|
|
||||||
|
@ -176,6 +231,13 @@ export const useAppConfig = createPersistStore(
|
||||||
: config?.template ?? DEFAULT_INPUT_TEMPLATE;
|
: config?.template ?? DEFAULT_INPUT_TEMPLATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (version < 4) {
|
||||||
|
state.modelConfig.compressModel =
|
||||||
|
DEFAULT_CONFIG.modelConfig.compressModel;
|
||||||
|
state.modelConfig.compressProviderName =
|
||||||
|
DEFAULT_CONFIG.modelConfig.compressProviderName;
|
||||||
|
}
|
||||||
|
|
||||||
return state as any;
|
return state as any;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -2,3 +2,4 @@ export * from "./chat";
|
||||||
export * from "./update";
|
export * from "./update";
|
||||||
export * from "./access";
|
export * from "./access";
|
||||||
export * from "./config";
|
export * from "./config";
|
||||||
|
export * from "./plugin";
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { BUILTIN_MASKS } from "../masks";
|
||||||
import { getLang, Lang } from "../locales";
|
import { getLang, Lang } from "../locales";
|
||||||
import { DEFAULT_TOPIC, ChatMessage } from "./chat";
|
import { DEFAULT_TOPIC, ChatMessage } from "./chat";
|
||||||
import { ModelConfig, useAppConfig } from "./config";
|
import { ModelConfig, useAppConfig } from "./config";
|
||||||
import { StoreKey, Plugin } from "../constant";
|
import { StoreKey } from "../constant";
|
||||||
import { nanoid } from "nanoid";
|
import { nanoid } from "nanoid";
|
||||||
import { createPersistStore } from "../utils/store";
|
import { createPersistStore } from "../utils/store";
|
||||||
|
|
||||||
|
@ -17,14 +17,18 @@ export type Mask = {
|
||||||
modelConfig: ModelConfig;
|
modelConfig: ModelConfig;
|
||||||
lang: Lang;
|
lang: Lang;
|
||||||
builtin: boolean;
|
builtin: boolean;
|
||||||
plugin?: Plugin[];
|
plugin?: string[];
|
||||||
|
enableArtifacts?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DEFAULT_MASK_STATE = {
|
export const DEFAULT_MASK_STATE = {
|
||||||
masks: {} as Record<string, Mask>,
|
masks: {} as Record<string, Mask>,
|
||||||
|
language: undefined as Lang | undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
export type MaskState = typeof DEFAULT_MASK_STATE;
|
export type MaskState = typeof DEFAULT_MASK_STATE & {
|
||||||
|
language?: Lang | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
export const DEFAULT_MASK_AVATAR = "gpt-bot";
|
export const DEFAULT_MASK_AVATAR = "gpt-bot";
|
||||||
export const createEmptyMask = () =>
|
export const createEmptyMask = () =>
|
||||||
|
@ -38,7 +42,7 @@ export const createEmptyMask = () =>
|
||||||
lang: getLang(),
|
lang: getLang(),
|
||||||
builtin: false,
|
builtin: false,
|
||||||
createdAt: Date.now(),
|
createdAt: Date.now(),
|
||||||
plugin: [Plugin.Artifacts],
|
plugin: [],
|
||||||
}) as Mask;
|
}) as Mask;
|
||||||
|
|
||||||
export const useMaskStore = createPersistStore(
|
export const useMaskStore = createPersistStore(
|
||||||
|
@ -101,6 +105,11 @@ export const useMaskStore = createPersistStore(
|
||||||
search(text: string) {
|
search(text: string) {
|
||||||
return Object.values(get().masks);
|
return Object.values(get().masks);
|
||||||
},
|
},
|
||||||
|
setLanguage(language: Lang | undefined) {
|
||||||
|
set({
|
||||||
|
language,
|
||||||
|
});
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
name: StoreKey.Mask,
|
name: StoreKey.Mask,
|
||||||
|
|