i18n
This commit is contained in:
@@ -5,6 +5,7 @@ import { calculate_token_length, Message } from "./chatgpt";
|
||||
import getDefaultParams from "./getDefaultParam";
|
||||
import ChatBOX from "./chatbox";
|
||||
import models from "./models";
|
||||
import { Tr, langCodeContext, LANG_OPTIONS } from "./translate";
|
||||
|
||||
import CHATGPT_API_WEB_VERSION from "./CHATGPT_API_WEB_VERSION";
|
||||
|
||||
@@ -245,7 +246,7 @@ export function App() {
|
||||
className="bg-violet-300 p-1 rounded hover:bg-violet-400"
|
||||
onClick={handleNewChatStore}
|
||||
>
|
||||
NEW
|
||||
{Tr("NEW")}
|
||||
</button>
|
||||
<ul>
|
||||
{allChatStoreIndexes
|
||||
@@ -303,7 +304,7 @@ export function App() {
|
||||
setAllChatStoreIndexes([...newAllChatStoreIndexes]);
|
||||
}}
|
||||
>
|
||||
DEL
|
||||
{Tr("DEL")}
|
||||
</button>
|
||||
</div>
|
||||
<ChatBOX
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Tr, langCodeContext, LANG_OPTIONS } from "./translate";
|
||||
import structuredClone from "@ungap/structured-clone";
|
||||
import { createRef } from "preact";
|
||||
import { StateUpdater, useEffect, useState } from "preact/hooks";
|
||||
@@ -281,7 +282,7 @@ export default function ChatBOX(props: {
|
||||
: chatStore.systemMessageContent}
|
||||
</button>{" "}
|
||||
<button className="underline">
|
||||
{chatStore.streamMode ? "STREAM" : "FETCH"}
|
||||
{chatStore.streamMode ? Tr("STREAM") : Tr("FETCH")}
|
||||
</button>
|
||||
</div>
|
||||
<div className="text-xs">
|
||||
@@ -293,14 +294,14 @@ export default function ChatBOX(props: {
|
||||
</span>
|
||||
</span>{" "}
|
||||
<span>
|
||||
Cut:{" "}
|
||||
{Tr("Cut")}:{" "}
|
||||
<span className="underline">
|
||||
{chatStore.postBeginIndex}/
|
||||
{chatStore.history.filter(({ hide }) => !hide).length}
|
||||
</span>{" "}
|
||||
</span>{" "}
|
||||
<span>
|
||||
Cost:{" "}
|
||||
{Tr("Cost")}:{" "}
|
||||
<span className="underline">${chatStore.cost.toFixed(4)}</span>
|
||||
</span>
|
||||
</div>
|
||||
@@ -308,12 +309,12 @@ export default function ChatBOX(props: {
|
||||
<div className="grow overflow-scroll">
|
||||
{!chatStore.apiKey && (
|
||||
<p className="opacity-60 p-6 rounded bg-white my-3 text-left dark:text-black">
|
||||
请先在上方设置 (OPENAI) API KEY
|
||||
{Tr("Please click above to set")} (OpenAI) API KEY
|
||||
</p>
|
||||
)}
|
||||
{!chatStore.apiEndpoint && (
|
||||
<p className="opacity-60 p-6 rounded bg-white my-3 text-left dark:text-black">
|
||||
请先在上方设置 API Endpoint
|
||||
{Tr("Please click above to set")} API Endpoint
|
||||
</p>
|
||||
)}
|
||||
{templateAPIs.length > 0 &&
|
||||
@@ -321,7 +322,7 @@ export default function ChatBOX(props: {
|
||||
!chatStore.apiEndpoint ||
|
||||
!chatStore.apiKey) && (
|
||||
<p className="break-all opacity-80 p-3 rounded bg-white my-3 text-left dark:text-black">
|
||||
<h2>已保存的 API 模板</h2>
|
||||
<h2>{Tr("Saved API templates")}</h2>
|
||||
<hr className="my-2" />
|
||||
<div className="flex flex-wrap">
|
||||
{templateAPIs.map((t, index) => (
|
||||
@@ -377,7 +378,7 @@ export default function ChatBOX(props: {
|
||||
{templates.length > 0 &&
|
||||
chatStore.history.filter((msg) => !msg.example).length == 0 && (
|
||||
<p className="break-all opacity-80 p-3 rounded bg-white my-3 text-left dark:text-black">
|
||||
<h2>Templates</h2>
|
||||
<h2>{Tr("Saved prompt templates")}</h2>
|
||||
<hr className="my-2" />
|
||||
<div className="flex flex-wrap">
|
||||
{templates.map((t, index) => (
|
||||
@@ -439,21 +440,15 @@ export default function ChatBOX(props: {
|
||||
)}
|
||||
{chatStore.history.length === 0 && (
|
||||
<p className="break-all opacity-60 p-6 rounded bg-white my-3 text-left dark:text-black">
|
||||
暂无历史对话记录
|
||||
<br />
|
||||
⚙Model: {chatStore.model}
|
||||
<br />
|
||||
⬆点击上方更改此对话的参数(请勿泄漏)
|
||||
<br />
|
||||
↖点击左上角 NEW 新建对话
|
||||
<br />
|
||||
请注意,使用 ChatGPT API
|
||||
的生成文本质量和速度会受到会话上下文的影响,同时历史上下文过长会被裁切。API
|
||||
会根据发送的上下文总量进行计费,因此建议您为不相关的问题或者不需要上文的问题创建新的对话,以避免不必要的计费。
|
||||
<br />
|
||||
⚠所有历史对话与参数储存在浏览器本地
|
||||
<br />
|
||||
⚠详细文档与源代码:{" "}
|
||||
{Tr("No chat history here")}
|
||||
<br />⚙{Tr("Model")}: {chatStore.model}
|
||||
<br />⬆{Tr("Click above to change the settings of this chat")}
|
||||
<br />↖{Tr("Click the conor to create a new chat")}
|
||||
<br />⚠
|
||||
{Tr(
|
||||
"All chat history and settings are stored in the local browser"
|
||||
)}
|
||||
<br />⚠{Tr("Documents and source code are avaliable here")}:{" "}
|
||||
<a
|
||||
className="underline"
|
||||
href="https://github.com/heimoshuiyu/chatgpt-api-web"
|
||||
@@ -473,7 +468,7 @@ export default function ChatBOX(props: {
|
||||
))}
|
||||
{showGenerating && (
|
||||
<p className="p-2 my-2 animate-pulse dark:text-white message-content">
|
||||
{generatingMessage || "生成中,最长可能需要一分钟,请保持网络稳定"}
|
||||
{generatingMessage || Tr("Generating...")}
|
||||
...
|
||||
</p>
|
||||
)}
|
||||
@@ -495,7 +490,7 @@ export default function ChatBOX(props: {
|
||||
await complete();
|
||||
}}
|
||||
>
|
||||
Re-Generate
|
||||
{Tr("Re-Generate")}
|
||||
</button>
|
||||
)}
|
||||
{chatStore.develop_mode && chatStore.history.length > 0 && (
|
||||
@@ -506,25 +501,29 @@ export default function ChatBOX(props: {
|
||||
await complete();
|
||||
}}
|
||||
>
|
||||
Completion
|
||||
{Tr("Completion")}
|
||||
</button>
|
||||
)}
|
||||
</p>
|
||||
<p className="p-2 my-2 text-center opacity-50 dark:text-white">
|
||||
{chatStore.responseModelName && (
|
||||
<>Generated by {chatStore.responseModelName}</>
|
||||
<>
|
||||
{Tr("Generated by")} {chatStore.responseModelName}
|
||||
</>
|
||||
)}
|
||||
{chatStore.postBeginIndex !== 0 && (
|
||||
<>
|
||||
<br />
|
||||
提示:会话过长,已裁切前 {chatStore.postBeginIndex} 条消息
|
||||
{Tr("Info: chat history is too long, forget messages")}:{" "}
|
||||
{chatStore.postBeginIndex}
|
||||
</>
|
||||
)}
|
||||
</p>
|
||||
{chatStore.chatgpt_api_web_version < "v1.3.0" && (
|
||||
<p className="p-2 my-2 text-center dark:text-white">
|
||||
<br />
|
||||
提示:当前会话版本 {chatStore.chatgpt_api_web_version}。
|
||||
{Tr("Warning: current chatStore version")}:{" "}
|
||||
{chatStore.chatgpt_api_web_version} {"< v1.3.0"}
|
||||
<br />
|
||||
v1.3.0
|
||||
引入与旧版不兼容的消息裁切算法。继续使用旧版可能会导致消息裁切过多或过少(表现为失去上下文或输出不完整)。
|
||||
@@ -535,8 +534,8 @@ export default function ChatBOX(props: {
|
||||
{chatStore.chatgpt_api_web_version < "v1.4.0" && (
|
||||
<p className="p-2 my-2 text-center dark:text-white">
|
||||
<br />
|
||||
提示:当前会话版本 {chatStore.chatgpt_api_web_version} {"< v1.4.0"}
|
||||
。
|
||||
{Tr("Warning: current chatStore version")}:{" "}
|
||||
{chatStore.chatgpt_api_web_version} {"< v1.4.0"}
|
||||
<br />
|
||||
v1.4.0 增加了更多参数,继续使用旧版可能因参数确实导致未定义的行为
|
||||
<br />
|
||||
@@ -546,7 +545,9 @@ export default function ChatBOX(props: {
|
||||
{chatStore.chatgpt_api_web_version < "v1.6.0" && (
|
||||
<p className="p-2 my-2 text-center dark:text-white">
|
||||
<br />
|
||||
提示:当前会话版本 {chatStore.chatgpt_api_web_version} {"< v1.6.0"}
|
||||
提示:当前会话版本 {chatStore.chatgpt_api_web_version}
|
||||
{Tr("Warning: current chatStore version")}:{" "}
|
||||
{chatStore.chatgpt_api_web_version} {"< v1.6.0"}
|
||||
。
|
||||
<br />
|
||||
v1.6.0 开始保存会话模板时会将 apiKey 和 apiEndpoint
|
||||
@@ -564,7 +565,7 @@ export default function ChatBOX(props: {
|
||||
await complete();
|
||||
}}
|
||||
>
|
||||
Retry
|
||||
{Tr("Retry")}
|
||||
</button>
|
||||
</p>
|
||||
)}
|
||||
@@ -594,7 +595,7 @@ export default function ChatBOX(props: {
|
||||
send(inputMsg);
|
||||
}}
|
||||
>
|
||||
Send
|
||||
{Tr("Send")}
|
||||
</button>
|
||||
{chatStore.whisper_api &&
|
||||
(chatStore.whisper_key || chatStore.apiKey) && (
|
||||
@@ -721,7 +722,7 @@ export default function ChatBOX(props: {
|
||||
setChatStore({ ...chatStore });
|
||||
}}
|
||||
>
|
||||
Assistant
|
||||
{Tr("Assistant")}
|
||||
</button>
|
||||
)}
|
||||
{chatStore.develop_mode && (
|
||||
@@ -741,7 +742,7 @@ export default function ChatBOX(props: {
|
||||
setChatStore({ ...chatStore });
|
||||
}}
|
||||
>
|
||||
User
|
||||
{Tr("User")}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
18
src/main.tsx
18
src/main.tsx
@@ -1,4 +1,16 @@
|
||||
import { render } from 'preact'
|
||||
import { App } from './app'
|
||||
import { render } from "preact";
|
||||
import { App } from "./app";
|
||||
import { useState } from "preact/hooks";
|
||||
import { Tr, langCodeContext, LANG_OPTIONS } from "./translate";
|
||||
|
||||
render(<App />, document.getElementById('app') as HTMLElement)
|
||||
function Base() {
|
||||
const [langCode, setLangCode] = useState("en-US");
|
||||
return (
|
||||
/* @ts-ignore */
|
||||
<langCodeContext.Provider value={{ langCode, setLangCode }}>
|
||||
<App />
|
||||
</langCodeContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
render(<Base />, document.getElementById("app") as HTMLElement);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Tr, langCodeContext, LANG_OPTIONS } from "./translate";
|
||||
import { useState, useEffect, StateUpdater } from "preact/hooks";
|
||||
import { ChatStore, ChatStoreMessage } from "./app";
|
||||
import { calculate_token_length } from "./chatgpt";
|
||||
@@ -54,7 +55,7 @@ function EditMessage(props: EditMessageProps) {
|
||||
setShowEdit(false);
|
||||
}}
|
||||
>
|
||||
Close
|
||||
{Tr("Close")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -101,7 +102,7 @@ export default function Message(props: Props) {
|
||||
"bg-purple-400 p-1 rounded shadow-md absolute z-20 left-1/2 top-3/4 transform -translate-x-1/2 -translate-y-1/2"
|
||||
}
|
||||
>
|
||||
Message Copied to clipboard!
|
||||
{Tr("Message copied to clipboard!")}
|
||||
</span>
|
||||
);
|
||||
|
||||
@@ -212,13 +213,13 @@ export default function Message(props: Props) {
|
||||
setChatStore({ ...chatStore });
|
||||
}}
|
||||
>
|
||||
<label className="dark:text-white">example</label>
|
||||
<label className="dark:text-white">{Tr("example")}</label>
|
||||
<input type="checkbox" checked={chat.example} />
|
||||
</span>
|
||||
<span
|
||||
onClick={(event: any) => setRenderWorkdown(!renderMarkdown)}
|
||||
>
|
||||
<label className="dark:text-white">render</label>
|
||||
<label className="dark:text-white">{Tr("render")}</label>
|
||||
<input type="checkbox" checked={renderMarkdown} />
|
||||
</span>
|
||||
</div>
|
||||
|
||||
113
src/settings.tsx
113
src/settings.tsx
@@ -1,8 +1,9 @@
|
||||
import { createRef } from "preact";
|
||||
import { StateUpdater, useEffect, useState } from "preact/hooks";
|
||||
import { StateUpdater, useContext, useEffect, useState } from "preact/hooks";
|
||||
import { ChatStore, TemplateAPI, clearTotalCost, getTotalCost } from "./app";
|
||||
import models from "./models";
|
||||
import { TemplateChatStore } from "./chatbox";
|
||||
import { tr, Tr, langCodeContext, LANG_OPTIONS } from "./translate";
|
||||
|
||||
const Help = (props: { children: any; help: string }) => {
|
||||
return (
|
||||
@@ -95,6 +96,43 @@ const Input = (props: {
|
||||
</Help>
|
||||
);
|
||||
};
|
||||
|
||||
const Slicer = (props: {
|
||||
chatStore: ChatStore;
|
||||
setChatStore: (cs: ChatStore) => void;
|
||||
field: "temperature";
|
||||
help: string;
|
||||
}) => {
|
||||
return (
|
||||
<Help help={props.help}>
|
||||
<label className="m-2 p-2">{props.field}</label>
|
||||
<span>
|
||||
<input
|
||||
type="range"
|
||||
min="0"
|
||||
max="1"
|
||||
step="0.01"
|
||||
value={props.chatStore[props.field]}
|
||||
onChange={(event: any) => {
|
||||
const value = parseFloat(event.target.value);
|
||||
props.chatStore[props.field] = value;
|
||||
props.setChatStore({ ...props.chatStore });
|
||||
}}
|
||||
/>
|
||||
<input
|
||||
type="number"
|
||||
value={props.chatStore[props.field]}
|
||||
onChange={(event: any) => {
|
||||
const value = parseFloat(event.target.value);
|
||||
props.chatStore[props.field] = value;
|
||||
props.setChatStore({ ...props.chatStore });
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
</Help>
|
||||
);
|
||||
};
|
||||
|
||||
const Number = (props: {
|
||||
chatStore: ChatStore;
|
||||
setChatStore: (cs: ChatStore) => void;
|
||||
@@ -103,7 +141,6 @@ const Number = (props: {
|
||||
| "maxTokens"
|
||||
| "tokenMargin"
|
||||
| "postBeginIndex"
|
||||
| "temperature"
|
||||
| "top_p"
|
||||
| "presence_penalty"
|
||||
| "frequency_penalty";
|
||||
@@ -179,6 +216,8 @@ export default (props: {
|
||||
|
||||
const importFileRef = createRef();
|
||||
const [totalCost, setTotalCost] = useState(getTotalCost());
|
||||
// @ts-ignore
|
||||
const { langCode, setLangCode } = useContext(langCodeContext);
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyPress = (event: any) => {
|
||||
@@ -206,26 +245,38 @@ export default (props: {
|
||||
}}
|
||||
className="m-2 p-2 bg-white rounded-lg h-fit lg:w-2/3 z-20"
|
||||
>
|
||||
<h3 className="text-xl text-center">Settings</h3>
|
||||
<h3 className="text-xl text-center">
|
||||
<span>{Tr("Settings")}</span>
|
||||
<select>
|
||||
{Object.keys(LANG_OPTIONS).map((opt) => (
|
||||
<option
|
||||
value={opt}
|
||||
selected={opt === (langCodeContext as any).langCode}
|
||||
onClick={(event: any) => {
|
||||
console.log("set lang code", event.target.value);
|
||||
setLangCode(event.target.value);
|
||||
}}
|
||||
>
|
||||
{LANG_OPTIONS[opt].name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</h3>
|
||||
<hr />
|
||||
<div className="flex justify-between">
|
||||
<button
|
||||
className="p-2 m-2 rounded bg-purple-600 text-white"
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(link);
|
||||
alert(`Copied link: ${link}`);
|
||||
alert(tr(`Copied link:`, langCode) + `${link}`);
|
||||
}}
|
||||
>
|
||||
Copy Link
|
||||
{Tr("Copy Setting Link")}
|
||||
</button>
|
||||
<button
|
||||
className="p-2 m-2 rounded bg-rose-600 text-white"
|
||||
onClick={() => {
|
||||
if (
|
||||
!confirm(
|
||||
`Are you sure to clear all ${props.chatStore.history.length} messages?`
|
||||
)
|
||||
)
|
||||
if (!confirm(tr("Are you sure to clear all history?", langCode)))
|
||||
return;
|
||||
props.chatStore.history = props.chatStore.history.filter(
|
||||
(msg) => msg.example && !msg.hide
|
||||
@@ -234,7 +285,7 @@ export default (props: {
|
||||
props.setChatStore({ ...props.chatStore });
|
||||
}}
|
||||
>
|
||||
Clear History
|
||||
{Tr("Clear History")}
|
||||
</button>
|
||||
<button
|
||||
className="p-2 m-2 rounded bg-cyan-600 text-white"
|
||||
@@ -242,11 +293,11 @@ export default (props: {
|
||||
props.setShow(false);
|
||||
}}
|
||||
>
|
||||
Close
|
||||
{Tr("Close")}
|
||||
</button>
|
||||
</div>
|
||||
<p className="m-2 p-2">
|
||||
Total cost in this session ${props.chatStore.cost.toFixed(4)}
|
||||
{Tr("Total cost in this session")} ${props.chatStore.cost.toFixed(4)}
|
||||
</p>
|
||||
<hr />
|
||||
<div className="box">
|
||||
@@ -303,7 +354,7 @@ export default (props: {
|
||||
readOnly={true}
|
||||
{...props}
|
||||
/>
|
||||
<Number field="temperature" help="温度" readOnly={false} {...props} />
|
||||
<Slicer field="temperature" help="温度" {...props} />
|
||||
<Number field="top_p" help="top_p" readOnly={false} {...props} />
|
||||
<Number
|
||||
field="presence_penalty"
|
||||
@@ -329,7 +380,7 @@ export default (props: {
|
||||
/>
|
||||
<div className="flex justify-between">
|
||||
<p className="m-2 p-2">
|
||||
Accumulated cost in all sessions ${totalCost.toFixed(4)}
|
||||
{Tr("Accumulated cost in all sessions")} ${totalCost.toFixed(4)}
|
||||
</p>
|
||||
<button
|
||||
className="p-2 m-2 rounded bg-emerald-500"
|
||||
@@ -338,7 +389,7 @@ export default (props: {
|
||||
setTotalCost(getTotalCost());
|
||||
}}
|
||||
>
|
||||
Reset
|
||||
{Tr("Reset")}
|
||||
</button>
|
||||
</div>
|
||||
<p className="flex justify-evenly">
|
||||
@@ -361,14 +412,14 @@ export default (props: {
|
||||
downloadAnchorNode.remove();
|
||||
}}
|
||||
>
|
||||
Export
|
||||
{Tr("Export")}
|
||||
</button>
|
||||
<button
|
||||
className="p-2 m-2 rounded bg-amber-500"
|
||||
onClick={() => {
|
||||
const name = prompt("Give this template a name:");
|
||||
const name = prompt(tr("Give this template a name:", langCode));
|
||||
if (!name) {
|
||||
alert("No template name specified");
|
||||
alert(tr("No template name specified", langCode));
|
||||
return;
|
||||
}
|
||||
const tmp: ChatStore = structuredClone(props.chatStore);
|
||||
@@ -382,7 +433,7 @@ export default (props: {
|
||||
props.setTemplates([...props.templates]);
|
||||
}}
|
||||
>
|
||||
As template
|
||||
{Tr("As template")}
|
||||
</button>
|
||||
<button
|
||||
className="p-2 m-2 rounded bg-amber-500"
|
||||
@@ -401,14 +452,17 @@ export default (props: {
|
||||
props.setTemplateAPIs([...props.templateAPIs]);
|
||||
}}
|
||||
>
|
||||
As API Template
|
||||
{Tr("As API Template")}
|
||||
</button>
|
||||
<button
|
||||
className="p-2 m-2 rounded bg-amber-500"
|
||||
onClick={() => {
|
||||
if (
|
||||
!confirm(
|
||||
"This will OVERWRITE the current chat history! Continue?"
|
||||
tr(
|
||||
"This will OVERWRITE the current chat history! Continue?",
|
||||
langCode
|
||||
)
|
||||
)
|
||||
)
|
||||
return;
|
||||
@@ -426,14 +480,14 @@ export default (props: {
|
||||
const file = importFileRef.current.files[0];
|
||||
console.log("file to import", file);
|
||||
if (!file || file.type !== "application/json") {
|
||||
alert("Please select a json file");
|
||||
alert(tr("Please select a json file", langCode));
|
||||
return;
|
||||
}
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => {
|
||||
console.log("import content", reader.result);
|
||||
if (!reader) {
|
||||
alert("Empty file");
|
||||
alert(tr("Empty file", langCode));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
@@ -441,11 +495,16 @@ export default (props: {
|
||||
reader.result as string
|
||||
);
|
||||
if (!newChatStore.chatgpt_api_web_version) {
|
||||
throw "This is not an exported chatgpt-api-web chatstore file. The key 'chatgpt_api_web_version' is missing!";
|
||||
throw tr(
|
||||
"This is not an exported chatgpt-api-web chatstore file. The key 'chatgpt_api_web_version' is missing!",
|
||||
langCode
|
||||
);
|
||||
}
|
||||
props.setChatStore({ ...newChatStore });
|
||||
} catch (e) {
|
||||
alert(`Import error on parsing json: ${e}`);
|
||||
alert(
|
||||
tr(`Import error on parsing json:`, langCode) + `${e}`
|
||||
);
|
||||
}
|
||||
};
|
||||
reader.readAsText(file);
|
||||
@@ -453,7 +512,7 @@ export default (props: {
|
||||
/>
|
||||
</p>
|
||||
<p className="text-center m-2 p-2">
|
||||
chatgpt-api-web ChatStore Version{" "}
|
||||
chatgpt-api-web ChatStore {Tr("Version")}{" "}
|
||||
{props.chatStore.chatgpt_api_web_version}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
51
src/translate/index.tsx
Normal file
51
src/translate/index.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
import { createContext } from "preact";
|
||||
import MAP_zh_CN from "./zh_CN";
|
||||
|
||||
interface LangOption {
|
||||
name: string;
|
||||
langMap: Record<string, string>;
|
||||
matches: string[];
|
||||
}
|
||||
|
||||
const LANG_OPTIONS: Record<string, LangOption> = {
|
||||
"en-US": {
|
||||
name: "English",
|
||||
langMap: {},
|
||||
matches: ["en-US", "en"],
|
||||
},
|
||||
"zh-CN": {
|
||||
name: "中文(简体)",
|
||||
langMap: MAP_zh_CN,
|
||||
matches: ["zh-CN", "zh"],
|
||||
},
|
||||
};
|
||||
|
||||
const langCodeContext = createContext("en-US");
|
||||
|
||||
function tr(text: string, langCode: "en-US" | "zh-CN") {
|
||||
const option = LANG_OPTIONS[langCode];
|
||||
if (option === undefined) {
|
||||
return text;
|
||||
}
|
||||
const langMap = LANG_OPTIONS[langCode].langMap;
|
||||
|
||||
const translatedText = langMap[text.toLowerCase()];
|
||||
if (translatedText === undefined) {
|
||||
return text;
|
||||
}
|
||||
|
||||
return translatedText;
|
||||
}
|
||||
|
||||
function Tr(text: string) {
|
||||
return (
|
||||
<langCodeContext.Consumer>
|
||||
{/* @ts-ignore */}
|
||||
{({ langCode }) => {
|
||||
return tr(text, langCode);
|
||||
}}
|
||||
</langCodeContext.Consumer>
|
||||
);
|
||||
}
|
||||
|
||||
export { tr, Tr, LANG_OPTIONS, langCodeContext };
|
||||
58
src/translate/zh_CN.ts
Normal file
58
src/translate/zh_CN.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
const LANG_MAP: Record<string, string> = {
|
||||
settings: "设置",
|
||||
model: "模型",
|
||||
"copy setting link": "复制设置链接",
|
||||
"are you sure to clear all history?": "确定要清除所有历史记录吗?",
|
||||
"clear history": "清除历史记录",
|
||||
new: "新",
|
||||
del: "删",
|
||||
cut: "遗忘",
|
||||
"please click above to set": "请点击上方进行设置",
|
||||
cost: "消费",
|
||||
stream: "流式返回",
|
||||
fetch: "一次获取",
|
||||
"saved api templates": "已保存的 API 模板",
|
||||
"saved prompt templates": "已保存的提示模板",
|
||||
"no chat history here": "暂无历史对话记录",
|
||||
"click above to change the settings of this chat":
|
||||
"点击上方更改此对话的参数(请勿泄漏)",
|
||||
"click the NEW to create a new chat": "点击左上角 NEW 新建对话",
|
||||
"all chat history and settings are stored in the local browser":
|
||||
"所有历史对话与参数储存在浏览器本地",
|
||||
"documents and source code are avaliable here":
|
||||
"详细文档与源代码可在此处获取",
|
||||
"generating...": "生成中,请保持网络稳定...",
|
||||
"re-generate": "重新生成",
|
||||
completion: "补全",
|
||||
"generated by": "生成模型: ",
|
||||
"info: chat history is too long, forget messages":
|
||||
"提示:对话历史过长,遗忘消息数量",
|
||||
"warning: current chatstore version": "警告:当前会话版本",
|
||||
retry: "重试",
|
||||
send: "发送",
|
||||
assistant: "AI消息",
|
||||
user: "用户消息",
|
||||
close: "关闭",
|
||||
"message copied to clipboard": "消息已复制到剪贴板",
|
||||
"total cost in this session": "本次会话总消费",
|
||||
"accumulated cost in all sessions": "所有会话总消费",
|
||||
export: "导出",
|
||||
"give this template a name:": "给此模板命名:",
|
||||
"no template name specified": "未指定模板名称",
|
||||
"as template": "保存为会话模板",
|
||||
"as api template": "保存为 API 模板",
|
||||
"this will overwrite the current chat history! continue?":
|
||||
"此操作将覆盖当前会话历史!继续?",
|
||||
"please select a json file": "请选择一个 JSON 文件",
|
||||
"empty file": "警告: 空文件",
|
||||
"this is not an exported chatgpt-api-web chatstore file. the key 'chatgpt_api_web_version' is missing!":
|
||||
"此文件不是 chatgpt-api-web 导出的会话文件,缺少 chatgpt_api_web_version 键值!",
|
||||
"import error on parsing json": "JSON 解析错误",
|
||||
version: "版本",
|
||||
"copied link:": "已复制链接:",
|
||||
reset: "重置",
|
||||
example: "示例",
|
||||
render: "渲染",
|
||||
};
|
||||
|
||||
export default LANG_MAP;
|
||||
Reference in New Issue
Block a user