mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-03-13 18:07:29 +00:00
feat(i18n): localize plugin settings for all languages (#515)
* chore(gitignore): add .worktrees/ to .gitignore
* fix(gitignore): add .worktrees/ to .gitignore
* feat(i18n): localize plugin settings
- Add missing mainTabs.plugins key in Russian locale.
- Add useTranslation to PluginSettingsTab and MobileNav.
- Add pluginSettings translations for en, ru, ja, ko, zh-CN.
- Localize the mobile navigation More button.
* fix: remove Japanese symbols in Rorean translate
* fix: fix Korean typo and localize starter plugin error
* fix(plugins): localize toggle labels and fix translation issues
* refactor(plugins): extract inline onToggle to named handleToggle
* fix(plugins): localize repo input aria-label and "tab" badge
- Replace hardcoded aria-label with t('pluginSettings.installAriaLabel')
- Replace hardcoded "tab" badge text with t('pluginSettings.tab')
- Add missing keys to all settings.json locale files
* fix(plugins): localize "running" status badge
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -134,4 +134,7 @@ tasks/
|
||||
# Translations
|
||||
!src/i18n/locales/en/tasks.json
|
||||
!src/i18n/locales/ja/tasks.json
|
||||
!src/i18n/locales/ru/tasks.json
|
||||
!src/i18n/locales/ru/tasks.json
|
||||
|
||||
# Git worktrees
|
||||
.worktrees/
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useState, useRef, useEffect, type Dispatch, type SetStateAction } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
MessageSquare,
|
||||
Folder,
|
||||
@@ -37,6 +38,7 @@ type MobileNavProps = {
|
||||
};
|
||||
|
||||
export default function MobileNav({ activeTab, setActiveTab, isInputFocused }: MobileNavProps) {
|
||||
const { t } = useTranslation(['common', 'settings']);
|
||||
const { tasksEnabled, isTaskMasterInstalled } = useTasksSettings();
|
||||
const shouldShowTasksTab = Boolean(tasksEnabled && isTaskMasterInstalled);
|
||||
const { plugins } = usePlugins();
|
||||
@@ -126,11 +128,10 @@ export default function MobileNav({ activeTab, setActiveTab, isInputFocused }: M
|
||||
e.preventDefault();
|
||||
setMoreOpen((v) => !v);
|
||||
}}
|
||||
className={`relative flex w-full touch-manipulation flex-col items-center justify-center gap-0.5 rounded-xl px-3 py-2 transition-all duration-200 active:scale-95 ${
|
||||
isPluginActive || moreOpen
|
||||
className={`relative flex w-full touch-manipulation flex-col items-center justify-center gap-0.5 rounded-xl px-3 py-2 transition-all duration-200 active:scale-95 ${isPluginActive || moreOpen
|
||||
? 'text-primary'
|
||||
: 'text-muted-foreground hover:text-foreground'
|
||||
}`}
|
||||
}`}
|
||||
aria-label="More plugins"
|
||||
aria-expanded={moreOpen}
|
||||
>
|
||||
@@ -142,7 +143,7 @@ export default function MobileNav({ activeTab, setActiveTab, isInputFocused }: M
|
||||
strokeWidth={isPluginActive ? 2.4 : 1.8}
|
||||
/>
|
||||
<span className={`relative z-10 text-[10px] font-medium transition-all duration-200 ${isPluginActive || moreOpen ? 'opacity-100' : 'opacity-60'}`}>
|
||||
More
|
||||
{t('settings:pluginSettings.morePlugins')}
|
||||
</span>
|
||||
</button>
|
||||
|
||||
@@ -157,11 +158,10 @@ export default function MobileNav({ activeTab, setActiveTab, isInputFocused }: M
|
||||
<button
|
||||
key={p.name}
|
||||
onClick={() => selectPlugin(p.name)}
|
||||
className={`flex w-full items-center gap-2.5 px-3.5 py-2.5 text-sm transition-colors ${
|
||||
isActive
|
||||
className={`flex w-full items-center gap-2.5 px-3.5 py-2.5 text-sm transition-colors ${isActive
|
||||
? 'bg-primary/8 text-primary'
|
||||
: 'text-foreground hover:bg-muted/60'
|
||||
}`}
|
||||
}`}
|
||||
>
|
||||
<Icon className="h-4 w-4 flex-shrink-0" strokeWidth={isActive ? 2.2 : 1.8} />
|
||||
<span className="truncate">{p.displayName}</span>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Trash2, RefreshCw, GitBranch, Loader2, ServerCrash, ShieldAlert, ExternalLink, BookOpen, Download, BarChart3 } from 'lucide-react';
|
||||
import { usePlugins } from '../../../contexts/PluginsContext';
|
||||
import type { Plugin } from '../../../contexts/PluginsContext';
|
||||
@@ -32,7 +33,7 @@ function ToggleSwitch({ checked, onChange, ariaLabel }: { checked: boolean; onCh
|
||||
}
|
||||
|
||||
/* ─── Server Dot ────────────────────────────────────────────────────────── */
|
||||
function ServerDot({ running }: { running: boolean }) {
|
||||
function ServerDot({ running, t }: { running: boolean; t: any }) {
|
||||
if (!running) return null;
|
||||
return (
|
||||
<span className="relative flex items-center gap-1.5">
|
||||
@@ -41,7 +42,7 @@ function ServerDot({ running }: { running: boolean }) {
|
||||
<span className="relative inline-flex h-1.5 w-1.5 rounded-full bg-emerald-500" />
|
||||
</span>
|
||||
<span className="font-mono text-[10px] uppercase tracking-wide text-emerald-600 dark:text-emerald-400">
|
||||
running
|
||||
{t('pluginSettings.runningStatus')}
|
||||
</span>
|
||||
</span>
|
||||
);
|
||||
@@ -71,6 +72,7 @@ function PluginCard({
|
||||
onCancelUninstall,
|
||||
updateError,
|
||||
}: PluginCardProps) {
|
||||
const { t } = useTranslation('settings');
|
||||
const accentColor = plugin.enabled
|
||||
? 'bg-emerald-500'
|
||||
: 'bg-muted-foreground/20';
|
||||
@@ -108,7 +110,7 @@ function PluginCard({
|
||||
<span className="rounded bg-muted px-1.5 py-0.5 text-[10px] text-muted-foreground">
|
||||
{plugin.slot}
|
||||
</span>
|
||||
<ServerDot running={!!plugin.serverRunning} />
|
||||
<ServerDot running={!!plugin.serverRunning} t={t} />
|
||||
</div>
|
||||
{plugin.description && (
|
||||
<p className="mt-1 text-sm leading-snug text-muted-foreground">
|
||||
@@ -143,8 +145,8 @@ function PluginCard({
|
||||
<button
|
||||
onClick={onUpdate}
|
||||
disabled={updating || !plugin.repoUrl}
|
||||
title={plugin.repoUrl ? 'Pull latest from git' : 'No git remote — update not available'}
|
||||
aria-label={`Update ${plugin.displayName}`}
|
||||
title={plugin.repoUrl ? t('pluginSettings.pullLatest') : t('pluginSettings.noGitRemote')}
|
||||
aria-label={t('pluginSettings.pullLatest')}
|
||||
className="rounded p-1.5 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground disabled:opacity-40"
|
||||
>
|
||||
{updating ? (
|
||||
@@ -156,18 +158,17 @@ function PluginCard({
|
||||
|
||||
<button
|
||||
onClick={onUninstall}
|
||||
title={confirmingUninstall ? 'Click again to confirm' : 'Uninstall plugin'}
|
||||
aria-label={`Uninstall ${plugin.displayName}`}
|
||||
className={`rounded p-1.5 transition-colors ${
|
||||
confirmingUninstall
|
||||
? 'bg-red-50 text-red-500 hover:bg-red-100 dark:bg-red-900/20 dark:hover:bg-red-900/30'
|
||||
: 'text-muted-foreground hover:bg-muted hover:text-red-500'
|
||||
}`}
|
||||
title={confirmingUninstall ? t('pluginSettings.confirmUninstall') : t('pluginSettings.uninstallPlugin')}
|
||||
aria-label={t('pluginSettings.uninstallPlugin')}
|
||||
className={`rounded p-1.5 transition-colors ${confirmingUninstall
|
||||
? 'bg-red-50 text-red-500 hover:bg-red-100 dark:bg-red-900/20 dark:hover:bg-red-900/30'
|
||||
: 'text-muted-foreground hover:bg-muted hover:text-red-500'
|
||||
}`}
|
||||
>
|
||||
<Trash2 className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
|
||||
<ToggleSwitch checked={plugin.enabled} onChange={onToggle} ariaLabel={`${plugin.enabled ? 'Disable' : 'Enable'} ${plugin.displayName}`} />
|
||||
<ToggleSwitch checked={plugin.enabled} onChange={onToggle} ariaLabel={`${plugin.enabled ? t('pluginSettings.disable') : t('pluginSettings.enable')} ${plugin.displayName}`} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -175,20 +176,20 @@ function PluginCard({
|
||||
{confirmingUninstall && (
|
||||
<div className="mt-3 flex items-center justify-between gap-3 rounded border border-red-200 bg-red-50 px-3 py-2 dark:border-red-800/50 dark:bg-red-950/30">
|
||||
<span className="text-sm text-red-600 dark:text-red-400">
|
||||
Remove <span className="font-semibold">{plugin.displayName}</span>? This cannot be undone.
|
||||
{t('pluginSettings.confirmUninstallMessage', { name: plugin.displayName })}
|
||||
</span>
|
||||
<div className="flex gap-1.5">
|
||||
<button
|
||||
onClick={onCancelUninstall}
|
||||
className="rounded border border-border px-2.5 py-1 text-sm text-muted-foreground transition-colors hover:bg-muted hover:text-foreground"
|
||||
>
|
||||
Cancel
|
||||
{t('pluginSettings.cancel')}
|
||||
</button>
|
||||
<button
|
||||
onClick={onUninstall}
|
||||
className="rounded border border-red-300 px-2.5 py-1 text-sm font-medium text-red-600 transition-colors hover:bg-red-100 dark:border-red-700 dark:text-red-400 dark:hover:bg-red-900/30"
|
||||
>
|
||||
Remove
|
||||
{t('pluginSettings.remove')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -208,6 +209,8 @@ function PluginCard({
|
||||
|
||||
/* ─── Starter Plugin Card ───────────────────────────────────────────────── */
|
||||
function StarterPluginCard({ onInstall, installing }: { onInstall: () => void; installing: boolean }) {
|
||||
const { t } = useTranslation('settings');
|
||||
|
||||
return (
|
||||
<div className="relative flex overflow-hidden rounded-lg border border-dashed border-border bg-card transition-all duration-200 hover:border-blue-400 dark:hover:border-blue-500">
|
||||
<div className="w-[3px] flex-shrink-0 bg-blue-500/30" />
|
||||
@@ -220,17 +223,17 @@ function StarterPluginCard({ onInstall, installing }: { onInstall: () => void; i
|
||||
<div className="min-w-0">
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<span className="text-sm font-semibold leading-none text-foreground">
|
||||
Project Stats
|
||||
{t('pluginSettings.starterPlugin.name')}
|
||||
</span>
|
||||
<span className="rounded bg-blue-50 px-1.5 py-0.5 text-[10px] font-medium text-blue-600 dark:bg-blue-950/50 dark:text-blue-400">
|
||||
starter
|
||||
{t('pluginSettings.starterPlugin.badge')}
|
||||
</span>
|
||||
<span className="rounded bg-muted px-1.5 py-0.5 text-[10px] text-muted-foreground">
|
||||
tab
|
||||
{t('pluginSettings.tab')}
|
||||
</span>
|
||||
</div>
|
||||
<p className="mt-1 text-sm leading-snug text-muted-foreground">
|
||||
File counts, lines of code, file-type breakdown, and recent activity for your project.
|
||||
{t('pluginSettings.starterPlugin.description')}
|
||||
</p>
|
||||
<a
|
||||
href={STARTER_PLUGIN_URL}
|
||||
@@ -253,7 +256,7 @@ function StarterPluginCard({ onInstall, installing }: { onInstall: () => void; i
|
||||
) : (
|
||||
<Download className="h-3.5 w-3.5" />
|
||||
)}
|
||||
{installing ? 'Installing…' : 'Install'}
|
||||
{installing ? t('pluginSettings.installing') : t('pluginSettings.starterPlugin.install')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -263,6 +266,7 @@ function StarterPluginCard({ onInstall, installing }: { onInstall: () => void; i
|
||||
|
||||
/* ─── Main Component ────────────────────────────────────────────────────── */
|
||||
export default function PluginSettingsTab() {
|
||||
const { t } = useTranslation('settings');
|
||||
const { plugins, loading, installPlugin, uninstallPlugin, updatePlugin, togglePlugin } =
|
||||
usePlugins();
|
||||
|
||||
@@ -279,7 +283,7 @@ export default function PluginSettingsTab() {
|
||||
setUpdateErrors((prev) => { const next = { ...prev }; delete next[name]; return next; });
|
||||
const result = await updatePlugin(name);
|
||||
if (!result.success) {
|
||||
setUpdateErrors((prev) => ({ ...prev, [name]: result.error || 'Update failed' }));
|
||||
setUpdateErrors((prev) => ({ ...prev, [name]: result.error || t('pluginSettings.updateFailed') }));
|
||||
}
|
||||
setUpdatingPlugins((prev) => { const next = new Set(prev); next.delete(name); return next; });
|
||||
};
|
||||
@@ -292,7 +296,7 @@ export default function PluginSettingsTab() {
|
||||
if (result.success) {
|
||||
setGitUrl('');
|
||||
} else {
|
||||
setInstallError(result.error || 'Installation failed');
|
||||
setInstallError(result.error || t('pluginSettings.installFailed'));
|
||||
}
|
||||
setInstalling(false);
|
||||
};
|
||||
@@ -302,7 +306,7 @@ export default function PluginSettingsTab() {
|
||||
setInstallError(null);
|
||||
const result = await installPlugin(STARTER_PLUGIN_URL);
|
||||
if (!result.success) {
|
||||
setInstallError(result.error || 'Installation failed');
|
||||
setInstallError(result.error || t('pluginSettings.installFailed'));
|
||||
}
|
||||
setInstallingStarter(false);
|
||||
};
|
||||
@@ -316,7 +320,7 @@ export default function PluginSettingsTab() {
|
||||
if (result.success) {
|
||||
setConfirmUninstall(null);
|
||||
} else {
|
||||
setInstallError(result.error || 'Uninstall failed');
|
||||
setInstallError(result.error || t('pluginSettings.uninstallFailed'));
|
||||
setConfirmUninstall(null);
|
||||
}
|
||||
};
|
||||
@@ -328,17 +332,10 @@ export default function PluginSettingsTab() {
|
||||
{/* Header */}
|
||||
<div>
|
||||
<h3 className="mb-1 text-base font-semibold text-foreground">
|
||||
Plugins
|
||||
{t('pluginSettings.title')}
|
||||
</h3>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Extend the interface with custom plugins. Install from{' '}
|
||||
<code className="rounded bg-muted px-1.5 py-0.5 text-xs font-semibold">
|
||||
git
|
||||
</code>{' '}
|
||||
or drop a folder in{' '}
|
||||
<code className="rounded bg-muted px-1.5 py-0.5 text-xs font-semibold">
|
||||
~/.claude-code-ui/plugins/
|
||||
</code>
|
||||
{t('pluginSettings.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -354,8 +351,8 @@ export default function PluginSettingsTab() {
|
||||
setGitUrl(e.target.value);
|
||||
setInstallError(null);
|
||||
}}
|
||||
placeholder="https://github.com/user/my-plugin"
|
||||
aria-label="Plugin git repository URL"
|
||||
placeholder={t('pluginSettings.installPlaceholder')}
|
||||
aria-label={t('pluginSettings.installAriaLabel')}
|
||||
className="flex-1 bg-transparent px-2 py-2.5 text-sm text-foreground placeholder:text-muted-foreground/40 focus:outline-none"
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') void handleInstall();
|
||||
@@ -369,7 +366,7 @@ export default function PluginSettingsTab() {
|
||||
{installing ? (
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
) : (
|
||||
'Install'
|
||||
t('pluginSettings.installButton')
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
@@ -381,7 +378,7 @@ export default function PluginSettingsTab() {
|
||||
<p className="-mt-4 flex items-start gap-1.5 text-xs leading-snug text-muted-foreground/50">
|
||||
<ShieldAlert className="mt-px h-3 w-3 flex-shrink-0" />
|
||||
<span>
|
||||
Only install plugins whose source code you have reviewed or from authors you trust.
|
||||
{t('pluginSettings.securityWarning')}
|
||||
</span>
|
||||
</p>
|
||||
|
||||
@@ -394,26 +391,35 @@ export default function PluginSettingsTab() {
|
||||
{loading ? (
|
||||
<div className="flex items-center justify-center gap-2 py-10 text-sm text-muted-foreground">
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
Scanning plugins…
|
||||
{t('pluginSettings.scanningPlugins')}
|
||||
</div>
|
||||
) : plugins.length === 0 ? (
|
||||
<p className="py-8 text-center text-sm text-muted-foreground">No plugins installed</p>
|
||||
<p className="py-8 text-center text-sm text-muted-foreground">{t('pluginSettings.noPluginsInstalled')}</p>
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
{plugins.map((plugin, index) => (
|
||||
<PluginCard
|
||||
key={plugin.name}
|
||||
plugin={plugin}
|
||||
index={index}
|
||||
onToggle={(enabled) => void togglePlugin(plugin.name, enabled).then(r => { if (!r.success) setInstallError(r.error || 'Toggle failed'); })}
|
||||
onUpdate={() => void handleUpdate(plugin.name)}
|
||||
onUninstall={() => void handleUninstall(plugin.name)}
|
||||
updating={updatingPlugins.has(plugin.name)}
|
||||
confirmingUninstall={confirmUninstall === plugin.name}
|
||||
onCancelUninstall={() => setConfirmUninstall(null)}
|
||||
updateError={updateErrors[plugin.name] ?? null}
|
||||
/>
|
||||
))}
|
||||
{plugins.map((plugin, index) => {
|
||||
const handleToggle = async (enabled: boolean) => {
|
||||
const r = await togglePlugin(plugin.name, enabled);
|
||||
if (!r.success) {
|
||||
setInstallError(r.error || t('pluginSettings.toggleFailed'));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<PluginCard
|
||||
key={plugin.name}
|
||||
plugin={plugin}
|
||||
index={index}
|
||||
onToggle={(enabled) => void handleToggle(enabled)}
|
||||
onUpdate={() => void handleUpdate(plugin.name)}
|
||||
onUninstall={() => void handleUninstall(plugin.name)}
|
||||
updating={updatingPlugins.has(plugin.name)}
|
||||
confirmingUninstall={confirmUninstall === plugin.name}
|
||||
onCancelUninstall={() => setConfirmUninstall(null)}
|
||||
updateError={updateErrors[plugin.name] ?? null}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -422,7 +428,7 @@ export default function PluginSettingsTab() {
|
||||
<div className="flex min-w-0 items-center gap-2">
|
||||
<BookOpen className="h-3.5 w-3.5 flex-shrink-0 text-muted-foreground/40" />
|
||||
<span className="text-xs text-muted-foreground/60">
|
||||
Build your own plugin
|
||||
{t('pluginSettings.buildYourOwn')}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex flex-shrink-0 items-center gap-3">
|
||||
@@ -432,7 +438,7 @@ export default function PluginSettingsTab() {
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-1 text-xs text-muted-foreground/60 transition-colors hover:text-foreground"
|
||||
>
|
||||
Starter <ExternalLink className="h-2.5 w-2.5" />
|
||||
{t('pluginSettings.starter')} <ExternalLink className="h-2.5 w-2.5" />
|
||||
</a>
|
||||
<span className="text-muted-foreground/20">·</span>
|
||||
<a
|
||||
@@ -441,7 +447,7 @@ export default function PluginSettingsTab() {
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-1 text-xs text-muted-foreground/60 transition-colors hover:text-foreground"
|
||||
>
|
||||
Docs <ExternalLink className="h-2.5 w-2.5" />
|
||||
{t('pluginSettings.docs')} <ExternalLink className="h-2.5 w-2.5" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -434,5 +434,41 @@
|
||||
"title": "About Codex MCP",
|
||||
"description": "Codex supports stdio-based MCP servers. You can add servers that extend Codex's capabilities with additional tools and resources."
|
||||
}
|
||||
},
|
||||
"pluginSettings": {
|
||||
"title": "Plugins",
|
||||
"description": "Extend the interface with custom plugins. Install from git or drop a folder in ~/.claude-code-ui/plugins/",
|
||||
"installPlaceholder": "https://github.com/user/my-plugin",
|
||||
"installButton": "Install",
|
||||
"installing": "Installing…",
|
||||
"securityWarning": "Only install plugins whose source code you have reviewed or from authors you trust.",
|
||||
"scanningPlugins": "Scanning plugins…",
|
||||
"noPluginsInstalled": "No plugins installed",
|
||||
"pullLatest": "Pull latest from git",
|
||||
"noGitRemote": "No git remote — update not available",
|
||||
"uninstallPlugin": "Uninstall plugin",
|
||||
"confirmUninstall": "Click again to confirm",
|
||||
"confirmUninstallMessage": "Remove {{name}}? This cannot be undone.",
|
||||
"cancel": "Cancel",
|
||||
"remove": "Remove",
|
||||
"updateFailed": "Update failed",
|
||||
"installFailed": "Installation failed",
|
||||
"uninstallFailed": "Uninstall failed",
|
||||
"toggleFailed": "Toggle failed",
|
||||
"buildYourOwn": "Build your own plugin",
|
||||
"starter": "Starter",
|
||||
"docs": "Docs",
|
||||
"starterPlugin": {
|
||||
"name": "Project Stats",
|
||||
"badge": "starter",
|
||||
"description": "File counts, lines of code, file-type breakdown, and recent activity for your project.",
|
||||
"install": "Install"
|
||||
},
|
||||
"morePlugins": "More",
|
||||
"enable": "Enable",
|
||||
"disable": "Disable",
|
||||
"installAriaLabel": "Plugin git repository URL",
|
||||
"tab": "tab",
|
||||
"runningStatus": "running"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -434,5 +434,41 @@
|
||||
"title": "Codex MCPについて",
|
||||
"description": "Codexはstdioベースのツールサーバーをサポートしています。追加のツールやリソースでCodexの機能を拡張するサーバーを追加できます。"
|
||||
}
|
||||
},
|
||||
"pluginSettings": {
|
||||
"title": "プラグイン",
|
||||
"description": "カスタムプラグインでインターフェースを拡張します。gitからインストールするか、~/.claude-code-ui/plugins/ にフォルダを配置してください。",
|
||||
"installPlaceholder": "https://github.com/user/my-plugin",
|
||||
"installButton": "インストール",
|
||||
"installing": "インストール中…",
|
||||
"securityWarning": "信頼できる作成者のプラグイン、またはソースコードを確認済みのプラグインのみをインストールしてください。",
|
||||
"scanningPlugins": "プラグインをスキャン中…",
|
||||
"noPluginsInstalled": "プラグインがインストールされていません",
|
||||
"pullLatest": "gitから最新を取得",
|
||||
"noGitRemote": "リモートgitリポジトリがありません — アップデート不可",
|
||||
"uninstallPlugin": "プラグインを削除",
|
||||
"confirmUninstall": "クリックして確定",
|
||||
"confirmUninstallMessage": "{{name}} を削除しますか?この操作は取り消せません。",
|
||||
"cancel": "キャンセル",
|
||||
"remove": "削除",
|
||||
"updateFailed": "アップデートに失敗しました",
|
||||
"installFailed": "インストールに失敗しました",
|
||||
"uninstallFailed": "削除に失敗しました",
|
||||
"toggleFailed": "切り替えに失敗しました",
|
||||
"buildYourOwn": "プラグインを自作する",
|
||||
"starter": "スターター",
|
||||
"docs": "ドキュメント",
|
||||
"starterPlugin": {
|
||||
"name": "プロジェクト統計",
|
||||
"badge": "スターター",
|
||||
"description": "プロジェクトのファイル数、コード行数、ファイルタイプの内訳、最近のアクティビティを表示します。",
|
||||
"install": "インストール"
|
||||
},
|
||||
"morePlugins": "詳細",
|
||||
"enable": "有効にする",
|
||||
"disable": "無効にする",
|
||||
"installAriaLabel": "プラグインのgitリポジトリURL",
|
||||
"tab": "タブ",
|
||||
"runningStatus": "実行中"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -434,5 +434,41 @@
|
||||
"title": "Codex MCP 정보",
|
||||
"description": "Codex는 stdio 기반 MCP 서버를 지원합니다. 추가 도구와 리소스로 Codex의 기능을 확장하는 서버를 추가할 수 있습니다."
|
||||
}
|
||||
},
|
||||
"pluginSettings": {
|
||||
"title": "플러그인",
|
||||
"description": "커스텀 플러그인으로 인터페이스를 확장하세요. git에서 설치하거나 ~/.claude-code-ui/plugins/ 폴더에 직접 추가할 수 있습니다.",
|
||||
"installPlaceholder": "https://github.com/user/my-plugin",
|
||||
"installButton": "설치",
|
||||
"installing": "설치 중…",
|
||||
"securityWarning": "소스 코드를 검토했거나 신뢰할 수 있는 작성자의 플러그인만 설치하세요.",
|
||||
"scanningPlugins": "플러그인 스캔 중…",
|
||||
"noPluginsInstalled": "설치된 플러그인이 없습니다",
|
||||
"pullLatest": "git에서 최신 버전 가져오기",
|
||||
"noGitRemote": "git 리모트가 없음 — 업데이트 불가",
|
||||
"uninstallPlugin": "플러그인 삭제",
|
||||
"confirmUninstall": "다시 클릭하여 확인",
|
||||
"confirmUninstallMessage": "{{name}} 플러그인을 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다.",
|
||||
"cancel": "취소",
|
||||
"remove": "삭제",
|
||||
"updateFailed": "업데이트 실패",
|
||||
"installFailed": "설치 실패",
|
||||
"uninstallFailed": "삭제 실패",
|
||||
"toggleFailed": "토글 실패",
|
||||
"buildYourOwn": "나만의 플러그인 만들기",
|
||||
"starter": "스타터",
|
||||
"docs": "문서",
|
||||
"starterPlugin": {
|
||||
"name": "프로젝트 통계",
|
||||
"badge": "스타터",
|
||||
"description": "프로젝트의 파일 수, 코드 라인 수, 파일 유형별 분석 및 최근 활동을 확인합니다.",
|
||||
"install": "설치"
|
||||
},
|
||||
"morePlugins": "더 보기",
|
||||
"enable": "활성화",
|
||||
"disable": "비활성화",
|
||||
"installAriaLabel": "플러그인 git 저장소 URL",
|
||||
"tab": "탭",
|
||||
"runningStatus": "실행 중"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -104,7 +104,8 @@
|
||||
"appearance": "Внешний вид",
|
||||
"git": "Git",
|
||||
"apiTokens": "API и токены",
|
||||
"tasks": "Задачи"
|
||||
"tasks": "Задачи",
|
||||
"plugins": "Плагины"
|
||||
},
|
||||
"appearanceSettings": {
|
||||
"darkMode": {
|
||||
@@ -433,5 +434,41 @@
|
||||
"title": "О Codex MCP",
|
||||
"description": "Codex поддерживает MCP серверы на основе stdio. Вы можете добавлять серверы, которые расширяют возможности Codex дополнительными инструментами и ресурсами."
|
||||
}
|
||||
},
|
||||
"pluginSettings": {
|
||||
"title": "Плагины",
|
||||
"description": "Расширяйте интерфейс с помощью кастомных плагинов. Установите из git или добавьте папку в ~/.claude-code-ui/plugins/",
|
||||
"installPlaceholder": "https://github.com/user/my-plugin",
|
||||
"installButton": "Установить",
|
||||
"installing": "Установка…",
|
||||
"securityWarning": "Устанавливайте только те плагины, исходный код которых вы проверили или от авторов, которым вы доверяете.",
|
||||
"scanningPlugins": "Сканирование плагинов…",
|
||||
"noPluginsInstalled": "Плагины не установлены",
|
||||
"pullLatest": "Получить обновления из git",
|
||||
"noGitRemote": "Нет удаленного git-репозитория — обновление недоступно",
|
||||
"uninstallPlugin": "Удалить плагин",
|
||||
"confirmUninstall": "Нажмите еще раз для подтверждения",
|
||||
"confirmUninstallMessage": "Удалить {{name}}? Это действие нельзя отменить.",
|
||||
"cancel": "Отмена",
|
||||
"remove": "Удалить",
|
||||
"updateFailed": "Ошибка обновления",
|
||||
"installFailed": "Ошибка установки",
|
||||
"uninstallFailed": "Ошибка удаления",
|
||||
"toggleFailed": "Ошибка переключения",
|
||||
"buildYourOwn": "Создайте свой плагин",
|
||||
"starter": "Шаблон",
|
||||
"docs": "Документация",
|
||||
"starterPlugin": {
|
||||
"name": "Статистика проекта",
|
||||
"badge": "шаблон",
|
||||
"description": "Количество файлов, строк кода, разбивка по типам файлов и недавняя активность в вашем проекте.",
|
||||
"install": "Установить"
|
||||
},
|
||||
"morePlugins": "Ещё",
|
||||
"enable": "Включить",
|
||||
"disable": "Выключить",
|
||||
"installAriaLabel": "URL git-репозитория плагина",
|
||||
"tab": "вкладка",
|
||||
"runningStatus": "запущен"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -434,5 +434,41 @@
|
||||
"title": "关于 Codex MCP",
|
||||
"description": "Codex 支持基于 stdio 的 MCP 服务器。您可以添加服务器,通过额外的工具和资源来扩展 Codex 的功能。"
|
||||
}
|
||||
},
|
||||
"pluginSettings": {
|
||||
"title": "插件",
|
||||
"description": "通过自定义插件扩展界面。从 git 安装或直接将文件夹放入 ~/.claude-code-ui/plugins/",
|
||||
"installPlaceholder": "https://github.com/user/my-plugin",
|
||||
"installButton": "安装",
|
||||
"installing": "安装中…",
|
||||
"securityWarning": "仅安装您已审查过源代码或信任作者的插件。",
|
||||
"scanningPlugins": "正在扫描插件…",
|
||||
"noPluginsInstalled": "未安装插件",
|
||||
"pullLatest": "从 git 拉取最新内容",
|
||||
"noGitRemote": "无 git 远程仓库 — 无法更新",
|
||||
"uninstallPlugin": "卸载插件",
|
||||
"confirmUninstall": "再次点击确认",
|
||||
"confirmUninstallMessage": "移除 {{name}}?此操作无法撤销。",
|
||||
"cancel": "取消",
|
||||
"remove": "移除",
|
||||
"updateFailed": "更新失败",
|
||||
"installFailed": "安装失败",
|
||||
"uninstallFailed": "卸载失败",
|
||||
"toggleFailed": "切换失败",
|
||||
"buildYourOwn": "构建您自己的插件",
|
||||
"starter": "入门模板",
|
||||
"docs": "文档",
|
||||
"starterPlugin": {
|
||||
"name": "项目统计",
|
||||
"badge": "入门",
|
||||
"description": "查看项目的文件数、代码行数、文件类型分布以及最近活动。",
|
||||
"install": "安装"
|
||||
},
|
||||
"morePlugins": "更多",
|
||||
"enable": "启用",
|
||||
"disable": "禁用",
|
||||
"installAriaLabel": "插件 git 仓库 URL",
|
||||
"tab": "标签",
|
||||
"runningStatus": "运行中"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user