feat: Adding react-i18next (preliminary support for en, de, zh, ja)

This commit is contained in:
brownrw8 2025-01-21 06:40:44 -10:00
parent 6ceda01b24
commit e643a4fbd4
18 changed files with 3271 additions and 5454 deletions

380
package-lock.json generated
View file

@ -2179,74 +2179,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
"integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"aix"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
"integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
"integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
"integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
@ -2264,312 +2196,6 @@
"node": ">=12"
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
"integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
"integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
"integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
"integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
"integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
"integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
"integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
"cpu": [
"loong64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
"integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
"cpu": [
"mips64el"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
"integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
"integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
"integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
"integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
"integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
"integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
"integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
"integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
"integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
"integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@eslint-community/eslint-utils": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
@ -2797,9 +2423,9 @@
"license": "MIT"
},
"node_modules/@mistralai/mistralai": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/@mistralai/mistralai/-/mistralai-1.3.6.tgz",
"integrity": "sha512-2y7U5riZq+cIjKpxGO9y417XuZv9CpBXEAvbjRMzWPGhXY7U1ZXj4VO4H9riS2kFZqTR2yLEKSE6/pGWVVIqgQ==",
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@mistralai/mistralai/-/mistralai-1.4.0.tgz",
"integrity": "sha512-xA3DAtIDh4Qgr1EoSuiGVE+2ABNrxpcTeC0kSXYbkDNUGdthalLAH7DgbG0fkKZ7TN8xdWXQq2WiIghp/O96Eg==",
"peerDependencies": {
"zod": ">= 3"
}

View file

@ -67,6 +67,7 @@ type GlobalStateKey =
| "browserSettings"
| "chatSettings"
| "vsCodeLmModelSelector"
| "localeLanguage"
export const GlobalFileNames = {
apiConversationHistory: "api_conversation_history.json",
@ -1027,6 +1028,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
autoApprovalSettings,
browserSettings,
chatSettings,
localeLanguage: vscode.env.language,
}
}
@ -1116,6 +1118,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
browserSettings,
chatSettings,
vsCodeLmModelSelector,
localeLanguage,
] = await Promise.all([
this.getGlobalState("apiProvider") as Promise<ApiProvider | undefined>,
this.getGlobalState("apiModelId") as Promise<string | undefined>,
@ -1150,6 +1153,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
this.getGlobalState("browserSettings") as Promise<BrowserSettings | undefined>,
this.getGlobalState("chatSettings") as Promise<ChatSettings | undefined>,
this.getGlobalState("vsCodeLmModelSelector") as Promise<vscode.LanguageModelChatSelector | undefined>,
this.getGlobalState("localeLanguage") as Promise<string | undefined>,
])
let apiProvider: ApiProvider

View file

@ -51,6 +51,7 @@ export interface ExtensionState {
autoApprovalSettings: AutoApprovalSettings
browserSettings: BrowserSettings
chatSettings: ChatSettings
localeLanguage: string
}
export interface ClineMessage {

File diff suppressed because it is too large Load diff

View file

@ -17,17 +17,21 @@
"pretty-bytes": "^6.1.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-i18next": "^15.4.0",
"react-remark": "^2.1.0",
"react-scripts": "5.0.1",
"react-scripts": "^5.0.1",
"react-textarea-autosize": "^8.5.3",
"react-use": "^17.5.1",
"react-virtuoso": "^4.7.13",
"rehype-highlight": "^7.0.0",
"rewire": "^7.0.0",
"styled-components": "^6.1.13",
"typescript": "^4.9.5",
"typescript": "^5.7.3",
"web-vitals": "^2.1.4"
},
"overrides": {
"typescript": "^5.7.3"
},
"scripts": {
"start": "react-scripts start",
"build": "node ./scripts/build-react-no-split.js",

View file

@ -8,9 +8,11 @@ import WelcomeView from "./components/welcome/WelcomeView"
import { ExtensionStateContextProvider, useExtensionState } from "./context/ExtensionStateContext"
import { vscode } from "./utils/vscode"
import McpView from "./components/mcp/McpView"
import { useTranslation } from "react-i18next"
const AppContent = () => {
const { didHydrateState, showWelcome, shouldShowAnnouncement } = useExtensionState()
const { didHydrateState, showWelcome, shouldShowAnnouncement, localeLanguage } = useExtensionState()
const { i18n } = useTranslation()
const [showSettings, setShowSettings] = useState(false)
const [showHistory, setShowHistory] = useState(false)
const [showMcp, setShowMcp] = useState(false)
@ -55,6 +57,12 @@ const AppContent = () => {
}
}, [shouldShowAnnouncement])
useEffect(() => {
if (localeLanguage) {
i18n.changeLanguage(localeLanguage)
}
}, [i18n, localeLanguage])
if (!didHydrateState) {
return null
}

View file

@ -1,17 +1,16 @@
import { VSCodeButton, VSCodeLink } from "@vscode/webview-ui-toolkit/react"
import { memo } from "react"
// import VSCodeButtonLink from "./VSCodeButtonLink"
// import { getOpenRouterAuthUrl } from "./ApiOptions"
// import { vscode } from "../utils/vscode"
import { useTranslation } from "react-i18next"
import { Trans } from "react-i18next"
interface AnnouncementProps {
version: string
hideAnnouncement: () => void
}
/*
You must update the latestAnnouncementId in ClineProvider for new announcements to show to users. This new id will be compared with whats in state for the 'last announcement shown', and if it's different then the announcement will render. As soon as an announcement is shown, the id will be updated in state. This ensures that announcements are not shown more than once, even if the user doesn't close it themselves.
*/
const Announcement = ({ version, hideAnnouncement }: AnnouncementProps) => {
const { t } = useTranslation("translation", { keyPrefix: "announcement", useSuspense: false })
const minorVersion = version.split(".").slice(0, 2).join(".") // 2.0.0 -> 2.0
return (
<div
@ -26,78 +25,39 @@ const Announcement = ({ version, hideAnnouncement }: AnnouncementProps) => {
<VSCodeButton appearance="icon" onClick={hideAnnouncement} style={{ position: "absolute", top: "8px", right: "8px" }}>
<span className="codicon codicon-close"></span>
</VSCodeButton>
<h3 style={{ margin: "0 0 8px" }}>
🎉{" "}New in v{minorVersion}
</h3>
<h3 style={{ margin: "0 0 8px" }}>{t("newInVersion", { version: minorVersion })}</h3>
<ul style={{ margin: "0 0 8px", paddingLeft: "12px" }}>
<li>
<b>Plan/Act mode toggle:</b> Plan mode lets Cline focus on gathering information, asking clarifying questions,
brainstorm ideas, and architect a solution. Switch back to Act mode to let him execute the plan!
<b>{t("checkpointsTitle")}</b> {t("checkpointsDescription")}
<ul style={{ margin: "4px 0", paddingLeft: 22 }}>
<li>
<span
className="codicon codicon-diff-multiple"
style={{
fontSize: "12px",
marginRight: "4px",
}}></span>
<b>{t("compareTitle")}</b> {t("compareDescription")}
</li>
<li>
<span
className="codicon codicon-discard"
style={{
fontSize: "12px",
marginRight: "4px",
}}></span>
<b>{t("restoreTitle")}</b> {t("restoreDescription")}
</li>
</ul>
</li>
<li>
<b>Quick API/model switching</b> with a new popup menu under the chat field
</li>
<li>
<b>VS Code LM API</b> lets you use models from other extensions like GitHub Copilot
</li>
<li>
<b>MCP server improvements:</b> On/off toggle to disable servers when not in use, and Auto-approve option for
individual tools
</li>
<li>
In case you missed it, Cline now supports Checkpoints!{" "}
<VSCodeLink href="https://x.com/sdrzn/status/1876378124126236949" style={{ display: "inline" }}>
See it in action here.
</VSCodeLink>
<b>{t("seeNewChangesTitle")}</b> {t("seeNewChangesDescription")}
</li>
</ul>
{/*<ul style={{ margin: "0 0 8px", paddingLeft: "12px" }}>
<li>
OpenRouter now supports prompt caching! They also have much higher rate limits than other providers,
so I recommend trying them out.
<br />
{!apiConfiguration?.openRouterApiKey && (
<VSCodeButtonLink
href={getOpenRouterAuthUrl(vscodeUriScheme)}
style={{
transform: "scale(0.85)",
transformOrigin: "left center",
margin: "4px -30px 2px 0",
}}>
Get OpenRouter API Key
</VSCodeButtonLink>
)}
{apiConfiguration?.openRouterApiKey && apiConfiguration?.apiProvider !== "openrouter" && (
<VSCodeButton
onClick={() => {
vscode.postMessage({
type: "apiConfiguration",
apiConfiguration: { ...apiConfiguration, apiProvider: "openrouter" },
})
}}
style={{
transform: "scale(0.85)",
transformOrigin: "left center",
margin: "4px -30px 2px 0",
}}>
Switch to OpenRouter
</VSCodeButton>
)}
</li>
<li>
<b>Edit Cline's changes before accepting!</b> When he creates or edits a file, you can modify his
changes directly in the right side of the diff view (+ hover over the 'Revert Block' arrow button in
the center to undo "<code>{"// rest of code here"}</code>" shenanigans)
</li>
<li>
New <code>search_files</code> tool that lets Cline perform regex searches in your project, letting
him refactor code, address TODOs and FIXMEs, remove dead code, and more!
</li>
<li>
When Cline runs commands, you can now type directly in the terminal (+ support for Python
environments)
</li>
</ul>*/}
<p style={{ margin: "8px 0" }}></p>
<VSCodeLink href="https://x.com/sdrzn/status/1876378124126236949" style={{ display: "inline" }}>
{t("seeDemo")}
</VSCodeLink>
<div
style={{
height: "1px",
@ -107,15 +67,13 @@ const Announcement = ({ version, hideAnnouncement }: AnnouncementProps) => {
}}
/>
<p style={{ margin: "0" }}>
Join our{" "}
<VSCodeLink style={{ display: "inline" }} href="https://discord.gg/cline">
discord
</VSCodeLink>{" "}
or{" "}
<VSCodeLink style={{ display: "inline" }} href="https://www.reddit.com/r/cline/">
r/cline
</VSCodeLink>
for more updates!
<Trans
i18nKey="announcement.joinOurCommunities"
components={{
DiscordLink: <VSCodeLink href="https://discord.gg/cline" />,
RedditLink: <VSCodeLink href="https://www.reddit.com/r/cline/" />,
}}
/>
</p>
</div>
)

View file

@ -11,6 +11,7 @@ import {
ClineSayTool,
ExtensionMessage,
} from "../../../../src/shared/ExtensionMessage"
import { useTranslation } from "react-i18next"
import { findLast } from "../../../../src/shared/array"
import { combineApiRequests } from "../../../../src/shared/combineApiRequests"
import { combineCommandSequences } from "../../../../src/shared/combineCommandSequences"
@ -36,6 +37,7 @@ interface ChatViewProps {
export const MAX_IMAGES_PER_MESSAGE = 20 // Anthropic limits to 20 images
const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryView }: ChatViewProps) => {
const { t, i18n, ready } = useTranslation("translation", { keyPrefix: "announcement", useSuspense: false })
const { version, clineMessages: messages, taskHistory, apiConfiguration } = useExtensionState()
//const task = messages.length > 0 ? (messages[0].say === "task" ? messages[0] : undefined) : undefined) : undefined

View file

@ -0,0 +1,35 @@
import { VSCodeDropdown, VSCodeOption } from "@vscode/webview-ui-toolkit/react"
import { memo } from "react"
import { useTranslation } from "react-i18next"
const LanguageOptions = () => {
const { t, i18n } = useTranslation("translation", { keyPrefix: "settingsView", useSuspense: false })
const changeLanguage = (e: any) => {
const language = e.target.value
i18n.changeLanguage(language)
}
return (
<div style={{ display: "flex", flexDirection: "column", gap: 5 }}>
<div className="dropdown-container">
<label htmlFor="language-dropdown">
<span style={{ fontWeight: 500 }}>{t("language")}</span>
</label>
<VSCodeDropdown
id="language-dropdown"
value={i18n.resolvedLanguage}
style={{ width: "100%" }}
onChange={changeLanguage}>
<VSCodeOption value="en">English</VSCodeOption>
<VSCodeOption value="de">Deutsch</VSCodeOption>
<VSCodeOption value="zh-CN">()</VSCodeOption>
<VSCodeOption value="zh-TW">()</VSCodeOption>
<VSCodeOption value="ja"></VSCodeOption>
</VSCodeDropdown>
</div>
</div>
)
}
export default memo(LanguageOptions)

View file

@ -1,9 +1,11 @@
import { VSCodeButton, VSCodeLink, VSCodeTextArea } from "@vscode/webview-ui-toolkit/react"
import { memo, useEffect, useState } from "react"
import { useTranslation } from "react-i18next"
import { useExtensionState } from "../../context/ExtensionStateContext"
import { validateApiConfiguration, validateModelId } from "../../utils/validate"
import { vscode } from "../../utils/vscode"
import ApiOptions from "./ApiOptions"
import LanguageOptions from "./LanguageOptions"
const IS_DEV = false // FIXME: use flags when packaging
@ -12,6 +14,7 @@ type SettingsViewProps = {
}
const SettingsView = ({ onDone }: SettingsViewProps) => {
const { t } = useTranslation("translation", { keyPrefix: "settingsView", useSuspense: false })
const { apiConfiguration, version, customInstructions, setCustomInstructions, openRouterModels } = useExtensionState()
const [apiErrorMessage, setApiErrorMessage] = useState<string | undefined>(undefined)
const [modelIdErrorMessage, setModelIdErrorMessage] = useState<string | undefined>(undefined)
@ -38,18 +41,6 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
setModelIdErrorMessage(undefined)
}, [apiConfiguration])
// validate as soon as the component is mounted
/*
useEffect will use stale values of variables if they are not included in the dependency array. so trying to use useEffect with a dependency array of only one value for example will use any other variables' old values. In most cases you don't want this, and should opt to use react-use hooks.
useEffect(() => {
// uses someVar and anotherVar
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [someVar])
If we only want to run code once on mount we can use react-use's useEffectOnce or useMount
*/
const handleResetState = () => {
vscode.postMessage({ type: "resetState" })
}
@ -75,8 +66,8 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
marginBottom: "17px",
paddingRight: 17,
}}>
<h3 style={{ color: "var(--vscode-foreground)", margin: 0 }}>Settings</h3>
<VSCodeButton onClick={handleSubmit}>Done</VSCodeButton>
<h3 style={{ color: "var(--vscode-foreground)", margin: 0 }}>{t("settings")}</h3>
<VSCodeButton onClick={handleSubmit}>{t("done")}</VSCodeButton>
</div>
<div
style={{
@ -99,9 +90,9 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
value={customInstructions ?? ""}
style={{ width: "100%" }}
rows={4}
placeholder={'e.g. "Run unit tests at the end", "Use TypeScript with async/await", "Speak in Spanish"'}
placeholder={t("customInstructionsPlaceholder")}
onInput={(e: any) => setCustomInstructions(e.target?.value ?? "")}>
<span style={{ fontWeight: "500" }}>Custom Instructions</span>
<span style={{ fontWeight: "500" }}>{t("customInstructions")}</span>
</VSCodeTextArea>
<p
style={{
@ -109,15 +100,18 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
marginTop: "5px",
color: "var(--vscode-descriptionForeground)",
}}>
These instructions are added to the end of the system prompt sent with every request.
{t("customInstructionsDescription")}
</p>
</div>
<div style={{ marginBottom: 5 }}>
<LanguageOptions />
</div>
{IS_DEV && (
<>
<div style={{ marginTop: "10px", marginBottom: "4px" }}>Debug</div>
<div style={{ marginTop: "10px", marginBottom: "4px" }}>{t("debug")}</div>
<VSCodeButton onClick={handleResetState} style={{ marginTop: "5px", width: "auto" }}>
Reset State
{t("resetState")}
</VSCodeButton>
<p
style={{
@ -125,7 +119,7 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
marginTop: "5px",
color: "var(--vscode-descriptionForeground)",
}}>
This will reset all global state and secret storage in the extension.
{t("resetStateDescription")}
</p>
</>
)}
@ -145,7 +139,7 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
margin: 0,
padding: 0,
}}>
If you have any questions or feedback, feel free to open an issue at{" "}
{t("feedback")}{" "}
<VSCodeLink href="https://github.com/cline/cline" style={{ display: "inline" }}>
https://github.com/cline/cline
</VSCodeLink>
@ -156,7 +150,7 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
margin: "10px 0 0 0",
padding: 0,
}}>
v{version}
{t("version")} {version}
</p>
</div>
</div>

View file

@ -34,6 +34,7 @@ export const ExtensionStateContextProvider: React.FC<{
shouldShowAnnouncement: false,
autoApprovalSettings: DEFAULT_AUTO_APPROVAL_SETTINGS,
browserSettings: DEFAULT_BROWSER_SETTINGS,
localeLanguage: "en",
chatSettings: DEFAULT_CHAT_SETTINGS,
})
const [didHydrateState, setDidHydrateState] = useState(false)

22
webview-ui/src/i18n.ts Normal file
View file

@ -0,0 +1,22 @@
import i18n from "i18next"
import { initReactI18next } from "react-i18next"
import translationEN from "./locales/en/translation.json"
import translationDE from "./locales/de/translation.json"
import translationZHCN from "./locales/zh-CN/translation.json"
import translationZHTW from "./locales/zh-TW/translation.json"
import translationJA from "./locales/ja/translation.json"
i18n.use(initReactI18next) // passes i18n down to react-i18next
.init({
fallbackLng: "en",
debug: true,
})
i18n.addResourceBundle("de", "translation", translationDE)
i18n.addResourceBundle("en", "translation", translationEN)
i18n.addResourceBundle("zh-CN", "translation", translationZHCN)
i18n.addResourceBundle("zh-TW", "translation", translationZHTW)
i18n.addResourceBundle("ja", "translation", translationJA)
export default i18n

View file

@ -4,6 +4,7 @@ import "./index.css"
import App from "./App"
import reportWebVitals from "./reportWebVitals"
import "../../node_modules/@vscode/codicons/dist/codicon.css"
import "./i18n"
const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement)
root.render(

View file

@ -0,0 +1,32 @@
{
"announcement": {
"newInVersion": "Neu in Version {{version}}",
"checkpointsTitle": "Checkpunkte",
"checkpointsDescription": "Erstellen Sie Checkpunkte, um den Fortschritt zu speichern.",
"compareTitle": "Vergleichen",
"compareDescription": "Vergleichen Sie Änderungen zwischen Checkpunkten.",
"restoreTitle": "Wiederherstellen",
"restoreDescription": "Stellen Sie frühere Versionen wieder her.",
"seeNewChangesTitle": "Neue Änderungen anzeigen",
"seeNewChangesDescription": "Sehen Sie sich die neuesten Änderungen an.",
"seeDemo": "Demo ansehen",
"joinOurCommunities": "Tritt unserem <DiscordLink>Discord</DiscordLink> oder <RedditLink>Reddit</RedditLink> bei für weitere Updates!"
},
"settingsView": {
"settings": "Einstellungen",
"done": "Fertig",
"language": "Sprache",
"english": "Englisch",
"german": "Deutsch",
"chinese": "Chinesisch",
"japanese": "Japanisch",
"customInstructions": "Benutzerdefinierte Anweisungen",
"customInstructionsPlaceholder": "z.B. \"Führen Sie am Ende Unit-Tests durch\", \"Verwenden Sie TypeScript mit async/await\", \"Sprechen Sie auf Japanisch\"",
"customInstructionsDescription": "Diese Anweisungen werden am Ende des Systemprompts hinzugefügt, der mit jeder Anfrage gesendet wird.",
"debug": "Debuggen",
"resetState": "Zustand zurücksetzen",
"resetStateDescription": "Dies setzt den gesamten globalen Zustand und die geheime Speicherung in der Erweiterung zurück.",
"feedback": "Wenn Sie Fragen oder Feedback haben, können Sie gerne ein Issue eröffnen unter",
"version": "v"
}
}

View file

@ -0,0 +1,32 @@
{
"announcement": {
"newInVersion": "New in version {{version}}",
"checkpointsTitle": "Checkpoints",
"checkpointsDescription": "Create checkpoints to save progress.",
"compareTitle": "Compare",
"compareDescription": "Compare changes between checkpoints.",
"restoreTitle": "Restore",
"restoreDescription": "Restore previous versions.",
"seeNewChangesTitle": "See new changes",
"seeNewChangesDescription": "View the latest changes.",
"seeDemo": "See demo",
"joinOurCommunities": "Join our <DiscordLink>Discord</DiscordLink> or <RedditLink>Reddit</RedditLink> for more updates!"
},
"settingsView": {
"settings": "Settings",
"done": "Done",
"language": "Language",
"english": "English",
"german": "German",
"chinese": "Chinese",
"japanese": "Japanese",
"customInstructions": "Custom Instructions",
"customInstructionsPlaceholder": "e.g. \"Run unit tests at the end\", \"Use TypeScript with async/await\", \"Speak in Japanese\"",
"customInstructionsDescription": "These instructions are added to the end of the system prompt sent with every request.",
"debug": "Debug",
"resetState": "Reset State",
"resetStateDescription": "This will reset all global state and secret storage in the extension.",
"feedback": "If you have any questions or feedback, feel free to open an issue at",
"version": "v"
}
}

View file

@ -0,0 +1,32 @@
{
"announcement": {
"newInVersion": "バージョン{{version}}の新機能",
"checkpointsTitle": "チェックポイント",
"checkpointsDescription": "進捗を保存するためのチェックポイントを作成します。",
"compareTitle": "比較",
"compareDescription": "チェックポイント間の変更を比較します。",
"restoreTitle": "復元",
"restoreDescription": "以前のバージョンを復元します。",
"seeNewChangesTitle": "新しい変更を見る",
"seeNewChangesDescription": "最新の変更を表示します。",
"seeDemo": "デモを見る",
"joinOurCommunities": "最新情報を入手するには、私たちの <DiscordLink>Discord</DiscordLink> または <RedditLink>Reddit</RedditLink> に参加してください!"
},
"settingsView": {
"settings": "設定",
"done": "完了",
"language": "言語",
"english": "英語",
"german": "ドイツ語",
"chinese": "中国語",
"japanese": "日本語",
"customInstructions": "カスタム指示",
"customInstructionsPlaceholder": "例: \"最後に単体テストを実行する\", \"async/awaitを使用してTypeScriptを使用する\", \"日本語で話す\"",
"customInstructionsDescription": "これらの指示は、各リクエストと共に送信されるシステムプロンプトの最後に追加されます。",
"debug": "デバッグ",
"resetState": "状態をリセット",
"resetStateDescription": "これにより、拡張機能のすべてのグローバル状態と秘密のストレージがリセットされます。",
"feedback": "ご質問やフィードバックがある場合は、気軽に問題を開いてください",
"version": "バージョン"
}
}

View file

@ -0,0 +1,33 @@
{
"announcement": {
"newInVersion": "版本 {{version}} 中的新功能",
"checkpointsTitle": "检查点",
"checkpointsDescription": "创建检查点以保存进度。",
"compareTitle": "比较",
"compareDescription": "比较检查点之间的更改。",
"restoreTitle": "恢复",
"restoreDescription": "恢复以前的版本。",
"seeNewChangesTitle": "查看新更改",
"seeNewChangesDescription": "查看最新更改。",
"seeDemo": "查看演示",
"joinOur": "加入我们的",
"joinOurCommunities": "加入我们的 <DiscordLink>Discord</DiscordLink> 或 <RedditLink>Reddit</RedditLink> 以获取更多更新。"
},
"settingsView": {
"settings": "设置",
"done": "完成",
"language": "语言",
"english": "英语",
"german": "德语",
"chinese": "中文",
"japanese": "日语",
"customInstructions": "自定义指令",
"customInstructionsPlaceholder": "例如 \"在结束时运行单元测试\", \"使用 TypeScript 和 async/await\", \"用日语交流\"",
"customInstructionsDescription": "这些指令会添加到每个请求发送的系统提示的末尾。",
"debug": "调试",
"resetState": "重置状态",
"resetStateDescription": "这将重置扩展中的所有全局状态和秘密存储。",
"feedback": "如果您有任何问题或反馈,请随时在以下网址提交问题",
"version": "版本"
}
}

View file

@ -0,0 +1,33 @@
{
"announcement": {
"newInVersion": "版本 {{version}} 中的新功能",
"checkpointsTitle": "檢查點",
"checkpointsDescription": "創建檢查點以保存進度。",
"compareTitle": "比較",
"compareDescription": "比較檢查點之間的更改。",
"restoreTitle": "恢復",
"restoreDescription": "恢復以前的版本。",
"seeNewChangesTitle": "查看新更改",
"seeNewChangesDescription": "查看最新更改。",
"seeDemo": "查看演示",
"joinOur": "加入我們的",
"joinOurCommunities": "加入我們的 <DiscordLink>Discord</DiscordLink> 或 <RedditLink>Reddit</RedditLink> 以獲取更多更新。"
},
"settingsView": {
"settings": "設置",
"done": "完成",
"language": "語言",
"english": "英語",
"german": "德語",
"chinese": "中文",
"japanese": "日語",
"customInstructions": "自定義指令",
"customInstructionsPlaceholder": "例如 \"在結束時運行單元測試\", \"使用 TypeScript 和 async/await\", \"用日語交流\"",
"customInstructionsDescription": "這些指令會添加到每個請求發送的系統提示的末尾。",
"debug": "調試",
"resetState": "重置狀態",
"resetStateDescription": "這將重置擴展中的所有全局狀態和秘密存儲。",
"feedback": "如果您有任何問題或反饋,請隨時在以下網址提交問題",
"version": "版本"
}
}