diff --git a/README.md b/README.md
index 1783a7f..dd5add0 100644
--- a/README.md
+++ b/README.md
@@ -57,7 +57,7 @@ Use all OpenCode features from a familiar sidebar chat UI.
- Settings panel
- Keyboard navigation for inline popups (Tab / Arrow keys)
- Subtask display
-- i18n support (English, Japanese)
+- i18n support (English, Japanese, Simplified Chinese, Korean, Traditional Chinese, Spanish, Brazilian Portuguese, Russian)
### Requirements
@@ -204,7 +204,7 @@ OpenCode の全機能をサイドバーのチャット UI から操作できま
- 設定パネル
- インラインポップアップのキーボードナビゲーション(Tab / 矢印キー)
- サブタスク表示
-- 多言語対応(英語、日本語)
+- 多言語対応(英語、日本語、簡体字中国語、韓国語、繁体字中国語、スペイン語、ブラジルポルトガル語、ロシア語)
### 必要条件
diff --git a/package.nls.es.json b/package.nls.es.json
new file mode 100644
index 0000000..46c38af
--- /dev/null
+++ b/package.nls.es.json
@@ -0,0 +1,4 @@
+{
+ "displayName": "OpenCodeGUI",
+ "description": "[Experimental / No oficial] Una interfaz de chat en la barra lateral de VS Code para OpenCode. Esta extensión se desarrolla principalmente mediante codificación asistida por IA y se proporciona tal cual, sin garantía. No está afiliada ni respaldada por el proyecto OpenCode."
+}
diff --git a/package.nls.ko.json b/package.nls.ko.json
new file mode 100644
index 0000000..d6fc043
--- /dev/null
+++ b/package.nls.ko.json
@@ -0,0 +1,4 @@
+{
+ "displayName": "OpenCodeGUI",
+ "description": "【실험적·비공식】OpenCode용 VS Code 사이드바 채팅 인터페이스. 이 확장 프로그램은 주로 AI 보조 코딩을 통해 개발되었으며 어떠한 보증 없이 있는 그대로 제공됩니다. OpenCode 프로젝트와 제휴 또는 보증 관계가 없습니다."
+}
diff --git a/package.nls.pt-br.json b/package.nls.pt-br.json
new file mode 100644
index 0000000..cdbb03f
--- /dev/null
+++ b/package.nls.pt-br.json
@@ -0,0 +1,4 @@
+{
+ "displayName": "OpenCodeGUI",
+ "description": "[Experimental / Não oficial] Uma interface de chat na barra lateral do VS Code para o OpenCode. Esta extensão é desenvolvida principalmente por meio de codificação assistida por IA e fornecida como está, sem garantia. Não é afiliada nem endossada pelo projeto OpenCode."
+}
diff --git a/package.nls.ru.json b/package.nls.ru.json
new file mode 100644
index 0000000..4d33a80
--- /dev/null
+++ b/package.nls.ru.json
@@ -0,0 +1,4 @@
+{
+ "displayName": "OpenCodeGUI",
+ "description": "[Экспериментальное / Неофициальное] Интерфейс чата в боковой панели VS Code для OpenCode. Это расширение разработано преимущественно с помощью ИИ и предоставляется «как есть» без каких-либо гарантий. Не связано с проектом OpenCode и не одобрено им."
+}
diff --git a/package.nls.zh-cn.json b/package.nls.zh-cn.json
new file mode 100644
index 0000000..0d9120b
--- /dev/null
+++ b/package.nls.zh-cn.json
@@ -0,0 +1,4 @@
+{
+ "displayName": "OpenCodeGUI",
+ "description": "【实验性·非官方】OpenCode 的 VS Code 侧边栏聊天界面。本扩展主要通过 AI 辅助编码开发,按原样提供,不含任何保证。与 OpenCode 项目无关联或背书关系。"
+}
diff --git a/package.nls.zh-tw.json b/package.nls.zh-tw.json
new file mode 100644
index 0000000..965f482
--- /dev/null
+++ b/package.nls.zh-tw.json
@@ -0,0 +1,4 @@
+{
+ "displayName": "OpenCodeGUI",
+ "description": "【實驗性·非官方】OpenCode 的 VS Code 側邊欄聊天介面。本擴充功能主要透過 AI 輔助編碼開發,按原樣提供,不含任何保證。與 OpenCode 專案無關聯或背書關係。"
+}
diff --git a/webview/__tests__/components/organisms/ToolConfigPanel.test.tsx b/webview/__tests__/components/organisms/ToolConfigPanel.test.tsx
index 4e66f98..1e524a6 100644
--- a/webview/__tests__/components/organisms/ToolConfigPanel.test.tsx
+++ b/webview/__tests__/components/organisms/ToolConfigPanel.test.tsx
@@ -25,10 +25,10 @@ describe("ToolConfigPanel", () => {
expect(container.querySelector(".muted.sm")).toBeInTheDocument();
});
- // renders language options
+ // renders language options (auto + 8 locales = 9)
it("言語オプションをレンダリングすること", () => {
const { container } = render();
- expect(container.querySelectorAll("input[name='locale']")).toHaveLength(3);
+ expect(container.querySelectorAll("input[name='locale']")).toHaveLength(9);
});
});
diff --git a/webview/__tests__/hooks/useLocale.test.ts b/webview/__tests__/hooks/useLocale.test.ts
index f7be8f1..95620e4 100644
--- a/webview/__tests__/hooks/useLocale.test.ts
+++ b/webview/__tests__/hooks/useLocale.test.ts
@@ -59,4 +59,109 @@ describe("useLocale", () => {
expect(result.current.resolvedLocale).toBe("ja");
});
});
+
+ // resolveLocale for zh-cn
+ context("setVscodeLanguage で 'zh-cn' を設定した場合", () => {
+ // resolves to zh-cn
+ it("resolvedLocale が 'zh-cn' になること", () => {
+ const { result } = renderHook(() => useLocale());
+ act(() => result.current.setVscodeLanguage("zh-cn"));
+ expect(result.current.resolvedLocale).toBe("zh-cn");
+ });
+ });
+
+ // resolveLocale for ko
+ context("setVscodeLanguage で 'ko' を設定した場合", () => {
+ // resolves to ko
+ it("resolvedLocale が 'ko' になること", () => {
+ const { result } = renderHook(() => useLocale());
+ act(() => result.current.setVscodeLanguage("ko"));
+ expect(result.current.resolvedLocale).toBe("ko");
+ });
+ });
+
+ // resolveLocale for zh-tw
+ context("setVscodeLanguage で 'zh-tw' を設定した場合", () => {
+ // resolves to zh-tw
+ it("resolvedLocale が 'zh-tw' になること", () => {
+ const { result } = renderHook(() => useLocale());
+ act(() => result.current.setVscodeLanguage("zh-tw"));
+ expect(result.current.resolvedLocale).toBe("zh-tw");
+ });
+ });
+
+ // resolveLocale for es
+ context("setVscodeLanguage で 'es' を設定した場合", () => {
+ // resolves to es
+ it("resolvedLocale が 'es' になること", () => {
+ const { result } = renderHook(() => useLocale());
+ act(() => result.current.setVscodeLanguage("es"));
+ expect(result.current.resolvedLocale).toBe("es");
+ });
+ });
+
+ // resolveLocale for pt-br
+ context("setVscodeLanguage で 'pt-br' を設定した場合", () => {
+ // resolves to pt-br
+ it("resolvedLocale が 'pt-br' になること", () => {
+ const { result } = renderHook(() => useLocale());
+ act(() => result.current.setVscodeLanguage("pt-br"));
+ expect(result.current.resolvedLocale).toBe("pt-br");
+ });
+ });
+
+ // resolveLocale for ru
+ context("setVscodeLanguage で 'ru' を設定した場合", () => {
+ // resolves to ru
+ it("resolvedLocale が 'ru' になること", () => {
+ const { result } = renderHook(() => useLocale());
+ act(() => result.current.setVscodeLanguage("ru"));
+ expect(result.current.resolvedLocale).toBe("ru");
+ });
+ });
+
+ // handleLocaleSettingChange with new locales
+ context("handleLocaleSettingChange で直接ロケールを指定した場合", () => {
+ // sets zh-cn directly
+ it("resolvedLocale が 'zh-cn' になること", () => {
+ const { result } = renderHook(() => useLocale());
+ act(() => result.current.handleLocaleSettingChange("zh-cn"));
+ expect(result.current.resolvedLocale).toBe("zh-cn");
+ });
+
+ // sets ko directly
+ it("resolvedLocale が 'ko' になること", () => {
+ const { result } = renderHook(() => useLocale());
+ act(() => result.current.handleLocaleSettingChange("ko"));
+ expect(result.current.resolvedLocale).toBe("ko");
+ });
+
+ // sets zh-tw directly
+ it("resolvedLocale が 'zh-tw' になること", () => {
+ const { result } = renderHook(() => useLocale());
+ act(() => result.current.handleLocaleSettingChange("zh-tw"));
+ expect(result.current.resolvedLocale).toBe("zh-tw");
+ });
+
+ // sets es directly
+ it("resolvedLocale が 'es' になること", () => {
+ const { result } = renderHook(() => useLocale());
+ act(() => result.current.handleLocaleSettingChange("es"));
+ expect(result.current.resolvedLocale).toBe("es");
+ });
+
+ // sets pt-br directly
+ it("resolvedLocale が 'pt-br' になること", () => {
+ const { result } = renderHook(() => useLocale());
+ act(() => result.current.handleLocaleSettingChange("pt-br"));
+ expect(result.current.resolvedLocale).toBe("pt-br");
+ });
+
+ // sets ru directly
+ it("resolvedLocale が 'ru' になること", () => {
+ const { result } = renderHook(() => useLocale());
+ act(() => result.current.handleLocaleSettingChange("ru"));
+ expect(result.current.resolvedLocale).toBe("ru");
+ });
+ });
});
diff --git a/webview/components/organisms/ToolConfigPanel/ToolConfigPanel.tsx b/webview/components/organisms/ToolConfigPanel/ToolConfigPanel.tsx
index 62dbd1b..8a62516 100644
--- a/webview/components/organisms/ToolConfigPanel/ToolConfigPanel.tsx
+++ b/webview/components/organisms/ToolConfigPanel/ToolConfigPanel.tsx
@@ -30,9 +30,18 @@ export function ToolConfigPanel({ paths, onOpenConfigFile, onClose, localeSettin
{t["config.language"]}
- {(["auto", "en", "ja"] as const).map((opt) => {
- const label =
- opt === "auto" ? t["config.langAuto"] : opt === "en" ? t["config.langEn"] : t["config.langJa"];
+ {(["auto", "en", "ja", "zh-cn", "ko", "zh-tw", "es", "pt-br", "ru"] as const).map((opt) => {
+ const labelMap = {
+ auto: t["config.langAuto"],
+ en: t["config.langEn"],
+ ja: t["config.langJa"],
+ "zh-cn": t["config.langZhCn"],
+ ko: t["config.langKo"],
+ "zh-tw": t["config.langZhTw"],
+ es: t["config.langEs"],
+ "pt-br": t["config.langPtBr"],
+ ru: t["config.langRu"],
+ } as const;
return (
);
})}
diff --git a/webview/locales/en.ts b/webview/locales/en.ts
index d6c3f73..4dac4d1 100644
--- a/webview/locales/en.ts
+++ b/webview/locales/en.ts
@@ -105,6 +105,12 @@ export const en = {
"config.langAuto": "Auto (VS Code)",
"config.langEn": "English",
"config.langJa": "日本語",
+ "config.langZhCn": "简体中文",
+ "config.langKo": "한국어",
+ "config.langZhTw": "繁體中文",
+ "config.langEs": "Español",
+ "config.langPtBr": "Português (Brasil)",
+ "config.langRu": "Русский",
// FileChangesHeader
"fileChanges.title": "File Changes",
diff --git a/webview/locales/es.ts b/webview/locales/es.ts
new file mode 100644
index 0000000..cf2cc65
--- /dev/null
+++ b/webview/locales/es.ts
@@ -0,0 +1,139 @@
+import type { en } from "./en";
+
+export const es: typeof en = {
+ // ChatHeader
+ "header.sessions": "Sesiones",
+ "header.title.fallback": "OpenCode",
+ "header.newChat": "Nuevo chat",
+
+ // EmptyState
+ "empty.title": "OpenCode",
+ "empty.description": "Inicia una nueva conversación para comenzar.",
+ "empty.newChat": "Nuevo chat",
+
+ // InputArea
+ "input.addContext": "Añadir contexto",
+ "input.searchFiles": "Buscar archivos...",
+ "input.noFiles": "No se encontraron archivos",
+ "input.remove": "Eliminar",
+ "input.placeholder": "Pregunta a OpenCode... (escribe # para adjuntar archivos)",
+ "input.addFile": (name: string) => `Añadir ${name}`,
+ "input.openTerminal": "Abrir sesión en terminal",
+ "input.shellMode": "Modo Shell",
+ "input.placeholder.shell": "Ingresa un comando de shell...",
+ "input.settings": "Configuración",
+ "input.stop": "Detener",
+ "input.send": "Enviar",
+
+ // MessageItem
+ "message.fileFallback": "archivo",
+ "message.clickToEdit": "Haz clic para editar",
+ "message.cancel": "Cancelar",
+ "message.send": "Enviar",
+ "message.thought": "Pensamiento",
+ "message.thinking": "Pensando…",
+ "message.toggleThought": "Alternar detalles de pensamiento",
+
+ // MessagesArea
+ "checkpoint.revertTitle": "Revertir a este punto",
+ "checkpoint.retryFromHere": "Reintentar desde aquí",
+ "checkpoint.forkFromHere": "Bifurcar desde aquí",
+
+ // Undo/Redo
+ "header.undo": "Deshacer",
+ "header.redo": "Rehacer",
+
+ // PermissionView
+ "permission.allow": "Permitir",
+ "permission.once": "Solo una vez",
+ "permission.deny": "Denegar",
+
+ // SessionList
+ "session.noSessions": "Sin sesiones",
+ "session.untitled": "Sin título",
+ "session.delete": "Eliminar",
+ "session.select": "Seleccionar sesión",
+
+ // Time (relative)
+ "time.now": "ahora",
+ "time.minutes": (n: number) => `${n}min`,
+ "time.hours": (n: number) => `${n}h`,
+ "time.days": (n: number) => `${n}d`,
+
+ // ToolPartView - category labels
+ "tool.read": "Leer",
+ "tool.edit": "Editar",
+ "tool.create": "Crear",
+ "tool.run": "Ejecutar",
+ "tool.search": "Buscar",
+ "tool.tool": "Herramienta",
+ "tool.toggleDetails": "Alternar detalles",
+ "tool.completed": (done: number, total: number) => `${done}/${total} completados`,
+ "tool.moreLines": (n: number) => `… +${n} líneas más`,
+ "tool.addLines": (n: number) => `+${n} líneas`,
+ "tool.todos": (done: number, total: number) => `${done}/${total} tareas`,
+
+ // ModelSelector
+ "model.selectModel": "Seleccionar modelo",
+ "model.notConnected": "No conectado",
+ "model.connectedOnly": "Solo conectados",
+ "model.showAll": "Mostrar todos los proveedores",
+ "model.hideDisconnected": "Ocultar proveedores desconectados",
+
+ // TodoHeader
+ "todo.label": "Tareas",
+ "todo.toggleList": "Alternar lista de tareas",
+
+ // ContextIndicator
+ "context.title": (percent: number) => `Contexto: ${percent}% usado`,
+ "context.windowUsage": "Uso de ventana de contexto",
+ "context.inputTokens": "Tokens de entrada",
+ "context.contextLimit": "Límite de contexto",
+ "context.compressing": "Comprimiendo...",
+ "context.compress": "Comprimir conversación",
+
+ // ShellResultView
+ "shell.title": "Shell",
+
+ // ToolConfigPanel
+ "config.title": "Configuración",
+ "config.projectConfig": "Configuración del proyecto",
+ "config.globalConfig": "Configuración global",
+
+ "config.close": "Cerrar",
+
+ // Language setting
+ "config.language": "Idioma",
+ "config.langAuto": "Automático (VS Code)",
+ "config.langEn": "English",
+ "config.langJa": "日本語",
+ "config.langZhCn": "简体中文",
+ "config.langKo": "한국어",
+ "config.langZhTw": "繁體中文",
+ "config.langEs": "Español",
+ "config.langPtBr": "Português (Brasil)",
+ "config.langRu": "Русский",
+
+ // FileChangesHeader
+ "fileChanges.title": "Cambios de archivos",
+ "fileChanges.noChanges": "Sin cambios de archivos",
+ "fileChanges.openDiff": "Abrir en editor de diferencias",
+ "fileChanges.toggle": "Cambios de archivos",
+
+ // Share
+ "share.share": "Compartir sesión",
+ "share.unshare": "Dejar de compartir",
+ "share.copied": "URL de compartir copiada al portapapeles",
+
+ // ChildSession
+ "childSession.agent": "Agente",
+ "childSession.backToParent": "Volver a la sesión principal",
+
+ // Context menu sections
+ "input.section.files": "Archivos",
+ "input.section.agents": "Agentes",
+ "input.section.shell": "Modo Shell",
+
+ // AgentMention
+ "input.noAgents": "No hay agentes disponibles",
+};
diff --git a/webview/locales/index.ts b/webview/locales/index.ts
index f34e0af..ab79986 100644
--- a/webview/locales/index.ts
+++ b/webview/locales/index.ts
@@ -1,16 +1,37 @@
import { createContext, useContext } from "react";
import { en } from "./en";
+import { es } from "./es";
import { ja } from "./ja";
+import { ko } from "./ko";
+import { ptBr } from "./pt-br";
+import { ru } from "./ru";
+import { zhCn } from "./zh-cn";
+import { zhTw } from "./zh-tw";
-export type SupportedLocale = "en" | "ja";
+export type SupportedLocale = "en" | "ja" | "zh-cn" | "ko" | "zh-tw" | "es" | "pt-br" | "ru";
export type LocaleSetting = "auto" | SupportedLocale;
-const locales: Record = { en, ja };
+const locales: Record = {
+ en,
+ ja,
+ "zh-cn": zhCn,
+ ko,
+ "zh-tw": zhTw,
+ es,
+ "pt-br": ptBr,
+ ru,
+};
export function resolveLocale(setting: LocaleSetting, vscodeLanguage: string): SupportedLocale {
if (setting !== "auto") return setting;
- // vscode.env.language は "ja", "en", "zh-cn" など
+ // vscode.env.language は "ja", "en", "zh-cn", "zh-tw", "ko", "es", "pt-br", "ru" など
if (vscodeLanguage.startsWith("ja")) return "ja";
+ if (vscodeLanguage === "zh-cn") return "zh-cn";
+ if (vscodeLanguage === "zh-tw") return "zh-tw";
+ if (vscodeLanguage.startsWith("ko")) return "ko";
+ if (vscodeLanguage.startsWith("es")) return "es";
+ if (vscodeLanguage === "pt-br") return "pt-br";
+ if (vscodeLanguage.startsWith("ru")) return "ru";
return "en";
}
diff --git a/webview/locales/ja.ts b/webview/locales/ja.ts
index 010816c..77137d7 100644
--- a/webview/locales/ja.ts
+++ b/webview/locales/ja.ts
@@ -107,6 +107,12 @@ export const ja: typeof en = {
"config.langAuto": "自動 (VS Code)",
"config.langEn": "English",
"config.langJa": "日本語",
+ "config.langZhCn": "简体中文",
+ "config.langKo": "한국어",
+ "config.langZhTw": "繁體中文",
+ "config.langEs": "Español",
+ "config.langPtBr": "Português (Brasil)",
+ "config.langRu": "Русский",
// FileChangesHeader
"fileChanges.title": "ファイル変更",
diff --git a/webview/locales/ko.ts b/webview/locales/ko.ts
new file mode 100644
index 0000000..92a1e8e
--- /dev/null
+++ b/webview/locales/ko.ts
@@ -0,0 +1,139 @@
+import type { en } from "./en";
+
+export const ko: typeof en = {
+ // ChatHeader
+ "header.sessions": "세션 목록",
+ "header.title.fallback": "OpenCode",
+ "header.newChat": "새 채팅",
+
+ // EmptyState
+ "empty.title": "OpenCode",
+ "empty.description": "새 대화를 시작하세요.",
+ "empty.newChat": "새 채팅",
+
+ // InputArea
+ "input.addContext": "컨텍스트 추가",
+ "input.searchFiles": "파일 검색...",
+ "input.noFiles": "파일을 찾을 수 없습니다",
+ "input.remove": "제거",
+ "input.placeholder": "OpenCode에 질문하기... (# 으로 파일 첨부)",
+ "input.addFile": (name: string) => `${name} 추가`,
+ "input.openTerminal": "터미널에서 세션 열기",
+ "input.shellMode": "셸 모드",
+ "input.placeholder.shell": "셸 명령어 입력...",
+ "input.settings": "설정",
+ "input.stop": "중지",
+ "input.send": "전송",
+
+ // MessageItem
+ "message.fileFallback": "파일",
+ "message.clickToEdit": "클릭하여 편집",
+ "message.cancel": "취소",
+ "message.send": "전송",
+ "message.thought": "사고",
+ "message.thinking": "사고 중…",
+ "message.toggleThought": "사고 상세 전환",
+
+ // MessagesArea
+ "checkpoint.revertTitle": "이 지점으로 되돌리기",
+ "checkpoint.retryFromHere": "여기서 다시 시도",
+ "checkpoint.forkFromHere": "여기서 분기",
+
+ // Undo/Redo
+ "header.undo": "실행 취소",
+ "header.redo": "다시 실행",
+
+ // PermissionView
+ "permission.allow": "허용",
+ "permission.once": "한 번만",
+ "permission.deny": "거부",
+
+ // SessionList
+ "session.noSessions": "세션 없음",
+ "session.untitled": "제목 없음",
+ "session.delete": "삭제",
+ "session.select": "세션 선택",
+
+ // Time (relative)
+ "time.now": "방금",
+ "time.minutes": (n: number) => `${n}분`,
+ "time.hours": (n: number) => `${n}시간`,
+ "time.days": (n: number) => `${n}일`,
+
+ // ToolPartView - category labels
+ "tool.read": "읽기",
+ "tool.edit": "편집",
+ "tool.create": "생성",
+ "tool.run": "실행",
+ "tool.search": "검색",
+ "tool.tool": "도구",
+ "tool.toggleDetails": "상세 전환",
+ "tool.completed": (done: number, total: number) => `${done}/${total} 완료`,
+ "tool.moreLines": (n: number) => `… +${n}줄 더`,
+ "tool.addLines": (n: number) => `+${n}줄`,
+ "tool.todos": (done: number, total: number) => `${done}/${total} 할 일`,
+
+ // ModelSelector
+ "model.selectModel": "모델 선택",
+ "model.notConnected": "연결 안 됨",
+ "model.connectedOnly": "연결됨만",
+ "model.showAll": "모든 제공자 표시",
+ "model.hideDisconnected": "연결 안 된 제공자 숨기기",
+
+ // TodoHeader
+ "todo.label": "할 일",
+ "todo.toggleList": "할 일 목록 전환",
+
+ // ContextIndicator
+ "context.title": (percent: number) => `컨텍스트: ${percent}% 사용 중`,
+ "context.windowUsage": "컨텍스트 윈도우 사용량",
+ "context.inputTokens": "입력 토큰",
+ "context.contextLimit": "컨텍스트 한도",
+ "context.compressing": "압축 중...",
+ "context.compress": "대화 압축",
+
+ // ShellResultView
+ "shell.title": "셸",
+
+ // ToolConfigPanel
+ "config.title": "설정",
+ "config.projectConfig": "프로젝트 설정",
+ "config.globalConfig": "전역 설정",
+
+ "config.close": "닫기",
+
+ // Language setting
+ "config.language": "언어",
+ "config.langAuto": "자동 (VS Code)",
+ "config.langEn": "English",
+ "config.langJa": "日本語",
+ "config.langZhCn": "简体中文",
+ "config.langKo": "한국어",
+ "config.langZhTw": "繁體中文",
+ "config.langEs": "Español",
+ "config.langPtBr": "Português (Brasil)",
+ "config.langRu": "Русский",
+
+ // FileChangesHeader
+ "fileChanges.title": "파일 변경",
+ "fileChanges.noChanges": "파일 변경 없음",
+ "fileChanges.openDiff": "차이 편집기에서 열기",
+ "fileChanges.toggle": "파일 변경",
+
+ // Share
+ "share.share": "세션 공유",
+ "share.unshare": "공유 해제",
+ "share.copied": "공유 URL이 클립보드에 복사되었습니다",
+
+ // ChildSession
+ "childSession.agent": "에이전트",
+ "childSession.backToParent": "상위 세션으로 돌아가기",
+
+ // Context menu sections
+ "input.section.files": "파일",
+ "input.section.agents": "에이전트",
+ "input.section.shell": "셸 모드",
+
+ // AgentMention
+ "input.noAgents": "사용 가능한 에이전트가 없습니다",
+};
diff --git a/webview/locales/pt-br.ts b/webview/locales/pt-br.ts
new file mode 100644
index 0000000..1c530ad
--- /dev/null
+++ b/webview/locales/pt-br.ts
@@ -0,0 +1,139 @@
+import type { en } from "./en";
+
+export const ptBr: typeof en = {
+ // ChatHeader
+ "header.sessions": "Sessões",
+ "header.title.fallback": "OpenCode",
+ "header.newChat": "Novo chat",
+
+ // EmptyState
+ "empty.title": "OpenCode",
+ "empty.description": "Inicie uma nova conversa para começar.",
+ "empty.newChat": "Novo chat",
+
+ // InputArea
+ "input.addContext": "Adicionar contexto",
+ "input.searchFiles": "Pesquisar arquivos...",
+ "input.noFiles": "Nenhum arquivo encontrado",
+ "input.remove": "Remover",
+ "input.placeholder": "Pergunte ao OpenCode... (digite # para anexar arquivos)",
+ "input.addFile": (name: string) => `Adicionar ${name}`,
+ "input.openTerminal": "Abrir sessão no terminal",
+ "input.shellMode": "Modo Shell",
+ "input.placeholder.shell": "Digite um comando shell...",
+ "input.settings": "Configurações",
+ "input.stop": "Parar",
+ "input.send": "Enviar",
+
+ // MessageItem
+ "message.fileFallback": "arquivo",
+ "message.clickToEdit": "Clique para editar",
+ "message.cancel": "Cancelar",
+ "message.send": "Enviar",
+ "message.thought": "Pensamento",
+ "message.thinking": "Pensando…",
+ "message.toggleThought": "Alternar detalhes do pensamento",
+
+ // MessagesArea
+ "checkpoint.revertTitle": "Reverter para este ponto",
+ "checkpoint.retryFromHere": "Tentar novamente daqui",
+ "checkpoint.forkFromHere": "Ramificar daqui",
+
+ // Undo/Redo
+ "header.undo": "Desfazer",
+ "header.redo": "Refazer",
+
+ // PermissionView
+ "permission.allow": "Permitir",
+ "permission.once": "Apenas uma vez",
+ "permission.deny": "Negar",
+
+ // SessionList
+ "session.noSessions": "Sem sessões",
+ "session.untitled": "Sem título",
+ "session.delete": "Excluir",
+ "session.select": "Selecionar sessão",
+
+ // Time (relative)
+ "time.now": "agora",
+ "time.minutes": (n: number) => `${n}min`,
+ "time.hours": (n: number) => `${n}h`,
+ "time.days": (n: number) => `${n}d`,
+
+ // ToolPartView - category labels
+ "tool.read": "Ler",
+ "tool.edit": "Editar",
+ "tool.create": "Criar",
+ "tool.run": "Executar",
+ "tool.search": "Pesquisar",
+ "tool.tool": "Ferramenta",
+ "tool.toggleDetails": "Alternar detalhes",
+ "tool.completed": (done: number, total: number) => `${done}/${total} concluídos`,
+ "tool.moreLines": (n: number) => `… +${n} linhas`,
+ "tool.addLines": (n: number) => `+${n} linhas`,
+ "tool.todos": (done: number, total: number) => `${done}/${total} tarefas`,
+
+ // ModelSelector
+ "model.selectModel": "Selecionar modelo",
+ "model.notConnected": "Não conectado",
+ "model.connectedOnly": "Apenas conectados",
+ "model.showAll": "Mostrar todos os provedores",
+ "model.hideDisconnected": "Ocultar provedores desconectados",
+
+ // TodoHeader
+ "todo.label": "Tarefas",
+ "todo.toggleList": "Alternar lista de tarefas",
+
+ // ContextIndicator
+ "context.title": (percent: number) => `Contexto: ${percent}% usado`,
+ "context.windowUsage": "Uso da janela de contexto",
+ "context.inputTokens": "Tokens de entrada",
+ "context.contextLimit": "Limite de contexto",
+ "context.compressing": "Comprimindo...",
+ "context.compress": "Comprimir conversa",
+
+ // ShellResultView
+ "shell.title": "Shell",
+
+ // ToolConfigPanel
+ "config.title": "Configurações",
+ "config.projectConfig": "Configuração do projeto",
+ "config.globalConfig": "Configuração global",
+
+ "config.close": "Fechar",
+
+ // Language setting
+ "config.language": "Idioma",
+ "config.langAuto": "Automático (VS Code)",
+ "config.langEn": "English",
+ "config.langJa": "日本語",
+ "config.langZhCn": "简体中文",
+ "config.langKo": "한국어",
+ "config.langZhTw": "繁體中文",
+ "config.langEs": "Español",
+ "config.langPtBr": "Português (Brasil)",
+ "config.langRu": "Русский",
+
+ // FileChangesHeader
+ "fileChanges.title": "Alterações de arquivos",
+ "fileChanges.noChanges": "Sem alterações de arquivos",
+ "fileChanges.openDiff": "Abrir no editor de diferenças",
+ "fileChanges.toggle": "Alterações de arquivos",
+
+ // Share
+ "share.share": "Compartilhar sessão",
+ "share.unshare": "Deixar de compartilhar",
+ "share.copied": "URL de compartilhamento copiada para a área de transferência",
+
+ // ChildSession
+ "childSession.agent": "Agente",
+ "childSession.backToParent": "Voltar à sessão principal",
+
+ // Context menu sections
+ "input.section.files": "Arquivos",
+ "input.section.agents": "Agentes",
+ "input.section.shell": "Modo Shell",
+
+ // AgentMention
+ "input.noAgents": "Nenhum agente disponível",
+};
diff --git a/webview/locales/ru.ts b/webview/locales/ru.ts
new file mode 100644
index 0000000..b9d5689
--- /dev/null
+++ b/webview/locales/ru.ts
@@ -0,0 +1,139 @@
+import type { en } from "./en";
+
+export const ru: typeof en = {
+ // ChatHeader
+ "header.sessions": "Сессии",
+ "header.title.fallback": "OpenCode",
+ "header.newChat": "Новый чат",
+
+ // EmptyState
+ "empty.title": "OpenCode",
+ "empty.description": "Начните новый разговор.",
+ "empty.newChat": "Новый чат",
+
+ // InputArea
+ "input.addContext": "Добавить контекст",
+ "input.searchFiles": "Поиск файлов...",
+ "input.noFiles": "Файлы не найдены",
+ "input.remove": "Удалить",
+ "input.placeholder": "Спросите OpenCode... (введите # для прикрепления файлов)",
+ "input.addFile": (name: string) => `Добавить ${name}`,
+ "input.openTerminal": "Открыть сессию в терминале",
+ "input.shellMode": "Режим Shell",
+ "input.placeholder.shell": "Введите команду оболочки...",
+ "input.settings": "Настройки",
+ "input.stop": "Остановить",
+ "input.send": "Отправить",
+
+ // MessageItem
+ "message.fileFallback": "файл",
+ "message.clickToEdit": "Нажмите для редактирования",
+ "message.cancel": "Отмена",
+ "message.send": "Отправить",
+ "message.thought": "Мысль",
+ "message.thinking": "Думаю…",
+ "message.toggleThought": "Переключить детали мысли",
+
+ // MessagesArea
+ "checkpoint.revertTitle": "Вернуться к этой точке",
+ "checkpoint.retryFromHere": "Повторить отсюда",
+ "checkpoint.forkFromHere": "Ответвить отсюда",
+
+ // Undo/Redo
+ "header.undo": "Отменить",
+ "header.redo": "Повторить",
+
+ // PermissionView
+ "permission.allow": "Разрешить",
+ "permission.once": "Однократно",
+ "permission.deny": "Отклонить",
+
+ // SessionList
+ "session.noSessions": "Нет сессий",
+ "session.untitled": "Без названия",
+ "session.delete": "Удалить",
+ "session.select": "Выбрать сессию",
+
+ // Time (relative)
+ "time.now": "сейчас",
+ "time.minutes": (n: number) => `${n}мин`,
+ "time.hours": (n: number) => `${n}ч`,
+ "time.days": (n: number) => `${n}д`,
+
+ // ToolPartView - category labels
+ "tool.read": "Чтение",
+ "tool.edit": "Редактирование",
+ "tool.create": "Создание",
+ "tool.run": "Запуск",
+ "tool.search": "Поиск",
+ "tool.tool": "Инструмент",
+ "tool.toggleDetails": "Переключить детали",
+ "tool.completed": (done: number, total: number) => `${done}/${total} завершено`,
+ "tool.moreLines": (n: number) => `… ещё ${n} строк`,
+ "tool.addLines": (n: number) => `+${n} строк`,
+ "tool.todos": (done: number, total: number) => `${done}/${total} задач`,
+
+ // ModelSelector
+ "model.selectModel": "Выбрать модель",
+ "model.notConnected": "Не подключено",
+ "model.connectedOnly": "Только подключённые",
+ "model.showAll": "Показать всех провайдеров",
+ "model.hideDisconnected": "Скрыть отключённых провайдеров",
+
+ // TodoHeader
+ "todo.label": "Задачи",
+ "todo.toggleList": "Переключить список задач",
+
+ // ContextIndicator
+ "context.title": (percent: number) => `Контекст: использовано ${percent}%`,
+ "context.windowUsage": "Использование окна контекста",
+ "context.inputTokens": "Входные токены",
+ "context.contextLimit": "Лимит контекста",
+ "context.compressing": "Сжатие...",
+ "context.compress": "Сжать диалог",
+
+ // ShellResultView
+ "shell.title": "Shell",
+
+ // ToolConfigPanel
+ "config.title": "Настройки",
+ "config.projectConfig": "Настройки проекта",
+ "config.globalConfig": "Глобальные настройки",
+
+ "config.close": "Закрыть",
+
+ // Language setting
+ "config.language": "Язык",
+ "config.langAuto": "Автоматически (VS Code)",
+ "config.langEn": "English",
+ "config.langJa": "日本語",
+ "config.langZhCn": "简体中文",
+ "config.langKo": "한국어",
+ "config.langZhTw": "繁體中文",
+ "config.langEs": "Español",
+ "config.langPtBr": "Português (Brasil)",
+ "config.langRu": "Русский",
+
+ // FileChangesHeader
+ "fileChanges.title": "Изменения файлов",
+ "fileChanges.noChanges": "Нет изменений файлов",
+ "fileChanges.openDiff": "Открыть в редакторе различий",
+ "fileChanges.toggle": "Изменения файлов",
+
+ // Share
+ "share.share": "Поделиться сессией",
+ "share.unshare": "Отменить общий доступ",
+ "share.copied": "Ссылка скопирована в буфер обмена",
+
+ // ChildSession
+ "childSession.agent": "Агент",
+ "childSession.backToParent": "Вернуться к родительской сессии",
+
+ // Context menu sections
+ "input.section.files": "Файлы",
+ "input.section.agents": "Агенты",
+ "input.section.shell": "Режим Shell",
+
+ // AgentMention
+ "input.noAgents": "Нет доступных агентов",
+};
diff --git a/webview/locales/zh-cn.ts b/webview/locales/zh-cn.ts
new file mode 100644
index 0000000..9dfb6c0
--- /dev/null
+++ b/webview/locales/zh-cn.ts
@@ -0,0 +1,139 @@
+import type { en } from "./en";
+
+export const zhCn: typeof en = {
+ // ChatHeader
+ "header.sessions": "会话列表",
+ "header.title.fallback": "OpenCode",
+ "header.newChat": "新建聊天",
+
+ // EmptyState
+ "empty.title": "OpenCode",
+ "empty.description": "开始新的对话吧。",
+ "empty.newChat": "新建聊天",
+
+ // InputArea
+ "input.addContext": "添加上下文",
+ "input.searchFiles": "搜索文件...",
+ "input.noFiles": "未找到文件",
+ "input.remove": "移除",
+ "input.placeholder": "向 OpenCode 提问...(输入 # 附加文件)",
+ "input.addFile": (name: string) => `添加 ${name}`,
+ "input.openTerminal": "在终端中打开会话",
+ "input.shellMode": "Shell 模式",
+ "input.placeholder.shell": "输入 Shell 命令...",
+ "input.settings": "设置",
+ "input.stop": "停止",
+ "input.send": "发送",
+
+ // MessageItem
+ "message.fileFallback": "文件",
+ "message.clickToEdit": "点击编辑",
+ "message.cancel": "取消",
+ "message.send": "发送",
+ "message.thought": "思考",
+ "message.thinking": "思考中…",
+ "message.toggleThought": "切换思考详情",
+
+ // MessagesArea
+ "checkpoint.revertTitle": "回退到此处",
+ "checkpoint.retryFromHere": "从此处重试",
+ "checkpoint.forkFromHere": "从此处分支",
+
+ // Undo/Redo
+ "header.undo": "撤销",
+ "header.redo": "重做",
+
+ // PermissionView
+ "permission.allow": "允许",
+ "permission.once": "仅一次",
+ "permission.deny": "拒绝",
+
+ // SessionList
+ "session.noSessions": "暂无会话",
+ "session.untitled": "无标题",
+ "session.delete": "删除",
+ "session.select": "选择会话",
+
+ // Time (relative)
+ "time.now": "刚刚",
+ "time.minutes": (n: number) => `${n}分钟`,
+ "time.hours": (n: number) => `${n}小时`,
+ "time.days": (n: number) => `${n}天`,
+
+ // ToolPartView - category labels
+ "tool.read": "读取",
+ "tool.edit": "编辑",
+ "tool.create": "创建",
+ "tool.run": "运行",
+ "tool.search": "搜索",
+ "tool.tool": "工具",
+ "tool.toggleDetails": "切换详情",
+ "tool.completed": (done: number, total: number) => `${done}/${total} 已完成`,
+ "tool.moreLines": (n: number) => `… 还有 ${n} 行`,
+ "tool.addLines": (n: number) => `+${n} 行`,
+ "tool.todos": (done: number, total: number) => `${done}/${total} 待办`,
+
+ // ModelSelector
+ "model.selectModel": "选择模型",
+ "model.notConnected": "未连接",
+ "model.connectedOnly": "仅已连接",
+ "model.showAll": "显示所有提供者",
+ "model.hideDisconnected": "隐藏未连接的提供者",
+
+ // TodoHeader
+ "todo.label": "待办",
+ "todo.toggleList": "切换待办列表",
+
+ // ContextIndicator
+ "context.title": (percent: number) => `上下文:已使用 ${percent}%`,
+ "context.windowUsage": "上下文窗口使用量",
+ "context.inputTokens": "输入 Token",
+ "context.contextLimit": "上下文上限",
+ "context.compressing": "压缩中...",
+ "context.compress": "压缩对话",
+
+ // ShellResultView
+ "shell.title": "Shell",
+
+ // ToolConfigPanel
+ "config.title": "设置",
+ "config.projectConfig": "项目配置",
+ "config.globalConfig": "全局配置",
+
+ "config.close": "关闭",
+
+ // Language setting
+ "config.language": "语言",
+ "config.langAuto": "自动(VS Code)",
+ "config.langEn": "English",
+ "config.langJa": "日本語",
+ "config.langZhCn": "简体中文",
+ "config.langKo": "한국어",
+ "config.langZhTw": "繁體中文",
+ "config.langEs": "Español",
+ "config.langPtBr": "Português (Brasil)",
+ "config.langRu": "Русский",
+
+ // FileChangesHeader
+ "fileChanges.title": "文件更改",
+ "fileChanges.noChanges": "无文件更改",
+ "fileChanges.openDiff": "在差异编辑器中打开",
+ "fileChanges.toggle": "文件更改",
+
+ // Share
+ "share.share": "共享会话",
+ "share.unshare": "取消共享",
+ "share.copied": "共享链接已复制到剪贴板",
+
+ // ChildSession
+ "childSession.agent": "智能体",
+ "childSession.backToParent": "返回父会话",
+
+ // Context menu sections
+ "input.section.files": "文件",
+ "input.section.agents": "智能体",
+ "input.section.shell": "Shell 模式",
+
+ // AgentMention
+ "input.noAgents": "没有可用的智能体",
+};
diff --git a/webview/locales/zh-tw.ts b/webview/locales/zh-tw.ts
new file mode 100644
index 0000000..c83135f
--- /dev/null
+++ b/webview/locales/zh-tw.ts
@@ -0,0 +1,139 @@
+import type { en } from "./en";
+
+export const zhTw: typeof en = {
+ // ChatHeader
+ "header.sessions": "工作階段列表",
+ "header.title.fallback": "OpenCode",
+ "header.newChat": "新建聊天",
+
+ // EmptyState
+ "empty.title": "OpenCode",
+ "empty.description": "開始新的對話吧。",
+ "empty.newChat": "新建聊天",
+
+ // InputArea
+ "input.addContext": "新增上下文",
+ "input.searchFiles": "搜尋檔案...",
+ "input.noFiles": "找不到檔案",
+ "input.remove": "移除",
+ "input.placeholder": "向 OpenCode 提問...(輸入 # 附加檔案)",
+ "input.addFile": (name: string) => `新增 ${name}`,
+ "input.openTerminal": "在終端機中開啟工作階段",
+ "input.shellMode": "Shell 模式",
+ "input.placeholder.shell": "輸入 Shell 命令...",
+ "input.settings": "設定",
+ "input.stop": "停止",
+ "input.send": "傳送",
+
+ // MessageItem
+ "message.fileFallback": "檔案",
+ "message.clickToEdit": "點擊編輯",
+ "message.cancel": "取消",
+ "message.send": "傳送",
+ "message.thought": "思考",
+ "message.thinking": "思考中…",
+ "message.toggleThought": "切換思考詳情",
+
+ // MessagesArea
+ "checkpoint.revertTitle": "回退到此處",
+ "checkpoint.retryFromHere": "從此處重試",
+ "checkpoint.forkFromHere": "從此處分支",
+
+ // Undo/Redo
+ "header.undo": "復原",
+ "header.redo": "重做",
+
+ // PermissionView
+ "permission.allow": "允許",
+ "permission.once": "僅一次",
+ "permission.deny": "拒絕",
+
+ // SessionList
+ "session.noSessions": "沒有工作階段",
+ "session.untitled": "無標題",
+ "session.delete": "刪除",
+ "session.select": "選擇工作階段",
+
+ // Time (relative)
+ "time.now": "剛剛",
+ "time.minutes": (n: number) => `${n}分鐘`,
+ "time.hours": (n: number) => `${n}小時`,
+ "time.days": (n: number) => `${n}天`,
+
+ // ToolPartView - category labels
+ "tool.read": "讀取",
+ "tool.edit": "編輯",
+ "tool.create": "建立",
+ "tool.run": "執行",
+ "tool.search": "搜尋",
+ "tool.tool": "工具",
+ "tool.toggleDetails": "切換詳情",
+ "tool.completed": (done: number, total: number) => `${done}/${total} 已完成`,
+ "tool.moreLines": (n: number) => `… 還有 ${n} 行`,
+ "tool.addLines": (n: number) => `+${n} 行`,
+ "tool.todos": (done: number, total: number) => `${done}/${total} 待辦`,
+
+ // ModelSelector
+ "model.selectModel": "選擇模型",
+ "model.notConnected": "未連線",
+ "model.connectedOnly": "僅已連線",
+ "model.showAll": "顯示所有提供者",
+ "model.hideDisconnected": "隱藏未連線的提供者",
+
+ // TodoHeader
+ "todo.label": "待辦",
+ "todo.toggleList": "切換待辦列表",
+
+ // ContextIndicator
+ "context.title": (percent: number) => `上下文:已使用 ${percent}%`,
+ "context.windowUsage": "上下文視窗使用量",
+ "context.inputTokens": "輸入 Token",
+ "context.contextLimit": "上下文上限",
+ "context.compressing": "壓縮中...",
+ "context.compress": "壓縮對話",
+
+ // ShellResultView
+ "shell.title": "Shell",
+
+ // ToolConfigPanel
+ "config.title": "設定",
+ "config.projectConfig": "專案設定",
+ "config.globalConfig": "全域設定",
+
+ "config.close": "關閉",
+
+ // Language setting
+ "config.language": "語言",
+ "config.langAuto": "自動(VS Code)",
+ "config.langEn": "English",
+ "config.langJa": "日本語",
+ "config.langZhCn": "简体中文",
+ "config.langKo": "한국어",
+ "config.langZhTw": "繁體中文",
+ "config.langEs": "Español",
+ "config.langPtBr": "Português (Brasil)",
+ "config.langRu": "Русский",
+
+ // FileChangesHeader
+ "fileChanges.title": "檔案變更",
+ "fileChanges.noChanges": "沒有檔案變更",
+ "fileChanges.openDiff": "在差異編輯器中開啟",
+ "fileChanges.toggle": "檔案變更",
+
+ // Share
+ "share.share": "分享工作階段",
+ "share.unshare": "取消分享",
+ "share.copied": "分享連結已複製到剪貼簿",
+
+ // ChildSession
+ "childSession.agent": "智慧代理",
+ "childSession.backToParent": "返回上層工作階段",
+
+ // Context menu sections
+ "input.section.files": "檔案",
+ "input.section.agents": "智慧代理",
+ "input.section.shell": "Shell 模式",
+
+ // AgentMention
+ "input.noAgents": "沒有可用的智慧代理",
+};