@@ -119,9 +122,9 @@ export function ParametersView({ parameters }: { parameters: unknown }) {
|
{p.required ? (
- required
+ {t('dashboard.schema.required')}
) : (
- optional
+ {t('dashboard.schema.optional')}
)}
|
@@ -129,7 +132,7 @@ export function ParametersView({ parameters }: { parameters: unknown }) {
{p.description && {p.description}}
{enumVals && enumVals.length > 0 && (
- enum:
+ {t('dashboard.schema.enum')}
{enumVals.map((v, j) => (
{String(v)}
@@ -139,7 +142,7 @@ export function ParametersView({ parameters }: { parameters: unknown }) {
)}
{p.schema?.default !== undefined && (
- default: {JSON.stringify(p.schema.default)}
+ {t('dashboard.schema.default')} {JSON.stringify(p.schema.default)}
)}
@@ -157,6 +160,7 @@ export function ParametersView({ parameters }: { parameters: unknown }) {
/* ===== Schema Properties Tree ===== */
function SchemaProperties({ schema, depth = 0 }: { schema: SchemaObj; depth?: number }) {
+ const { t } = useI18n();
const properties = schema.properties;
const requiredSet = new Set(schema.required || []);
@@ -189,10 +193,10 @@ function SchemaProperties({ schema, depth = 0 }: { schema: SchemaObj; depth?: nu
({prop.format})
)}
{requiredSet.has(name) && (
- required
+ {t('dashboard.schema.required')}
)}
{prop.nullable && (
- nullable
+ {t('dashboard.schema.nullable')}
)}
{prop.description && (
{prop.description}
@@ -200,7 +204,7 @@ function SchemaProperties({ schema, depth = 0 }: { schema: SchemaObj; depth?: nu
{prop.enum && prop.enum.length > 0 && (
- enum:
+ {t('dashboard.schema.enum')}
{prop.enum.map((v, j) => (
{String(v)}
@@ -210,7 +214,7 @@ function SchemaProperties({ schema, depth = 0 }: { schema: SchemaObj; depth?: nu
)}
{prop.default !== undefined && (
- default: {JSON.stringify(prop.default)}
+ {t('dashboard.schema.default')} {JSON.stringify(prop.default)}
)}
{hasChildren && }
@@ -225,6 +229,7 @@ function SchemaProperties({ schema, depth = 0 }: { schema: SchemaObj; depth?: nu
/* ===== Request Body ===== */
export function RequestBodyView({ requestBody }: { requestBody: unknown }) {
+ const { t } = useI18n();
if (!requestBody || typeof requestBody !== 'object') return null;
const body = requestBody as {
required?: boolean;
@@ -238,8 +243,8 @@ export function RequestBodyView({ requestBody }: { requestBody: unknown }) {
return (
- Request Body
- {body.required && required}
+ {t('dashboard.schema.requestBody')}
+ {body.required && {t('dashboard.schema.required')}}
@@ -255,8 +260,8 @@ export function RequestBodyView({ requestBody }: { requestBody: unknown }) {
return (
- Request Body
- {body.required && required}
+ {t('dashboard.schema.requestBody')}
+ {body.required && {t('dashboard.schema.required')}}
{body.description && (
{body.description}
@@ -277,7 +282,7 @@ export function RequestBodyView({ requestBody }: { requestBody: unknown }) {
)
) : (
- No schema
+ {t('dashboard.schema.noSchema')}
)}
@@ -303,13 +308,14 @@ function StatusBadge({ code }: { code: string }) {
}
export function ResponsesView({ responses }: { responses: unknown }) {
+ const { t } = useI18n();
if (!responses || typeof responses !== 'object') return null;
const entries = Object.entries(responses as Record);
if (entries.length === 0) return null;
return (
- Responses
+ {t('dashboard.schema.responses')}
{entries.map(([code, resp]) => {
const response = resp as {
@@ -349,7 +355,7 @@ export function ResponsesView({ responses }: { responses: unknown }) {
) : schema.type === 'array' && schema.items?.properties ? (
- of objects:
+ {t('dashboard.schema.ofObjects')}
diff --git a/packages/web/src/components/SettingsDialog.tsx b/packages/web/src/components/SettingsDialog.tsx
index f03d3d4..64187bd 100644
--- a/packages/web/src/components/SettingsDialog.tsx
+++ b/packages/web/src/components/SettingsDialog.tsx
@@ -1,6 +1,7 @@
import { useState, useRef, useEffect } from 'react';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { useAuth } from '../lib/auth';
+import { useI18n } from '../lib/i18n';
import { apiFetch } from '../lib/api';
import ConfirmDialog from './ConfirmDialog';
@@ -8,6 +9,7 @@ type ApiKeyStatus = { hasKey: boolean; prefix: string | null };
export default function SettingsDialog({ open, onClose }: { open: boolean; onClose: () => void }) {
const { user, updateUser } = useAuth();
+ const { t } = useI18n();
const dialogRef = useRef (null);
const queryClient = useQueryClient();
@@ -29,8 +31,8 @@ export default function SettingsDialog({ open, onClose }: { open: boolean; onClo
queryFn: () => apiFetch('/auth/api-key/status'),
enabled: open,
});
- const [freshKey, setFreshKey] = useState(null); // just generated/rotated
- const [revealedKey, setRevealedKey] = useState(null); // revealed via password
+ const [freshKey, setFreshKey] = useState(null);
+ const [revealedKey, setRevealedKey] = useState(null);
const [keyLoading, setKeyLoading] = useState(false);
const [keyError, setKeyError] = useState('');
const [keyCopied, setKeyCopied] = useState(false);
@@ -74,7 +76,7 @@ export default function SettingsDialog({ open, onClose }: { open: boolean; onClo
method: 'PUT', body: JSON.stringify({ name }),
});
updateUser({ name: data.name });
- setProfileMsg({ type: 'success', text: 'Profile updated' });
+ setProfileMsg({ type: 'success', text: t('dashboard.settings.profileUpdated') });
setTimeout(() => setProfileMsg(null), 3000);
} catch (err) {
setProfileMsg({ type: 'error', text: err instanceof Error ? err.message : 'Failed to update profile' });
@@ -85,7 +87,7 @@ export default function SettingsDialog({ open, onClose }: { open: boolean; onClo
const handlePasswordChange = async () => {
if (newPassword !== confirmPassword) {
- setPasswordMsg({ type: 'error', text: 'Passwords do not match' });
+ setPasswordMsg({ type: 'error', text: t('dashboard.settings.passwordMismatch') });
return;
}
setPasswordLoading(true);
@@ -94,7 +96,7 @@ export default function SettingsDialog({ open, onClose }: { open: boolean; onClo
await apiFetch('/auth/change-password', {
method: 'POST', body: JSON.stringify({ currentPassword, newPassword }),
});
- setPasswordMsg({ type: 'success', text: 'Password changed successfully' });
+ setPasswordMsg({ type: 'success', text: t('dashboard.settings.passwordChanged') });
setCurrentPassword('');
setNewPassword('');
setConfirmPassword('');
@@ -181,7 +183,7 @@ export default function SettingsDialog({ open, onClose }: { open: boolean; onClo
className="max-w-[560px] w-[90vw] rounded-2xl bg-bg-elevated border border-border-default shadow-lg p-0 backdrop:bg-overlay backdrop:backdrop-blur-sm"
>
- Settings
+ {t('dashboard.settings.title')}
)}
@@ -353,20 +359,20 @@ export default function SettingsDialog({ open, onClose }: { open: boolean; onClo
{/* Password */}
- Change Password
- Update your password to keep your account secure.
+ {t('dashboard.settings.changePasswordTitle')}
+ {t('dashboard.settings.changePasswordDesc')}
-
- setCurrentPassword(e.target.value)} className="input-base" placeholder="Enter current password" />
+
+ setCurrentPassword(e.target.value)} className="input-base" placeholder={t('dashboard.settings.enterCurrentPassword')} />
-
- setNewPassword(e.target.value)} className="input-base" placeholder="At least 8 characters" minLength={8} />
+
+ setNewPassword(e.target.value)} className="input-base" placeholder={t('dashboard.settings.atLeast8Chars')} minLength={8} />
-
- setConfirmPassword(e.target.value)} className="input-base" placeholder="Confirm new password" />
+
+ setConfirmPassword(e.target.value)} className="input-base" placeholder={t('dashboard.settings.confirmNewPassword')} />
{passwordMsg && (
@@ -381,7 +387,7 @@ export default function SettingsDialog({ open, onClose }: { open: boolean; onClo
disabled={passwordLoading || !currentPassword || !newPassword || newPassword.length < 8}
className="btn-primary"
>
- {passwordLoading ? 'Changing...' : 'Change Password'}
+ {passwordLoading ? t('dashboard.settings.changingPassword') : t('dashboard.settings.changePassword')}
@@ -392,9 +398,9 @@ export default function SettingsDialog({ open, onClose }: { open: boolean; onClo
open={showRotateConfirm}
onCancel={() => setShowRotateConfirm(false)}
onConfirm={handleRotateKey}
- title="Rotate API Key"
- description="The current API key will be invalidated immediately. All MCP clients using the old key will stop working. A new key will be generated."
- confirmText="Rotate Key"
+ title={t('dashboard.settings.rotateTitle')}
+ description={t('dashboard.settings.rotateDesc')}
+ confirmText={t('dashboard.settings.rotateConfirm')}
variant="warning"
/>
>
diff --git a/packages/web/src/components/ThemeToggle.tsx b/packages/web/src/components/ThemeToggle.tsx
index 4a1380a..c1468cf 100644
--- a/packages/web/src/components/ThemeToggle.tsx
+++ b/packages/web/src/components/ThemeToggle.tsx
@@ -1,4 +1,5 @@
import { useTheme } from '../lib/theme';
+import { useI18n } from '../lib/i18n';
const icons = {
light: (
@@ -18,26 +19,26 @@ const icons = {
),
};
-const labels = { light: '浅色', dark: '深色', system: '跟随系统' } as const;
const order: Array<'light' | 'dark' | 'system'> = ['light', 'dark', 'system'];
export default function ThemeToggle() {
const { theme, setTheme } = useTheme();
+ const { t } = useI18n();
return (
- {order.map((t) => (
+ {order.map((key) => (
))}
diff --git a/packages/web/src/lib/i18n.tsx b/packages/web/src/lib/i18n.tsx
index 4164cda..57a000f 100644
--- a/packages/web/src/lib/i18n.tsx
+++ b/packages/web/src/lib/i18n.tsx
@@ -1,301 +1,18 @@
import { createContext, useContext, useState, useCallback, type ReactNode } from 'react';
+import en from './i18n/en';
+import zh from './i18n/zh';
export type Locale = 'en' | 'zh';
type Translations = Record ;
type AllTranslations = Record;
-const translations: AllTranslations = {
- en: {
- // Nav
- 'nav.features': 'Features',
- 'nav.tools': 'Tools',
- 'nav.testimonials': 'Testimonials',
- 'nav.pricing': 'Pricing',
- 'nav.faq': 'FAQ',
- 'nav.signIn': 'Sign In',
- 'nav.getStarted': 'Get Started',
- 'nav.dashboard': 'Dashboard',
-
- // Hero
- 'hero.badge': 'MCP-Powered API Intelligence',
- 'hero.title': 'API Docs for LLMs,',
- 'hero.titleHighlight': 'Done Right',
- 'hero.subtitle': 'Let AI agents query your OpenAPI documentation with surgical precision. Multi-level retrieval serves exactly the tokens needed — not the entire spec.',
- 'hero.cta': 'Start Free',
- 'hero.ctaSecondary': 'View Documentation',
- 'hero.terminal.comment': '# Connect your AI tool to any API documentation',
- 'hero.terminal.cmd1': 'get_project_overview',
- 'hero.terminal.res1': '{ "name": "Stripe API", "modules": 12, "endpoints": 247 }',
- 'hero.terminal.cmd2': 'list_endpoints moduleId="payments"',
- 'hero.terminal.res2': '[ "POST /charges", "GET /charges/:id", "POST /refunds" ... ]',
- 'hero.terminal.cmd3': 'get_endpoint_detail endpointId="create-charge"',
- 'hero.terminal.res3': '{ params: [...], requestBody: {...}, responses: {...} } // ~800 tokens',
-
- // Features
- 'features.label': 'Features',
- 'features.title': 'Intelligent API Retrieval',
- 'features.subtitle': 'Five specialized MCP tools designed for minimal token usage per call',
- 'features.progressive.title': 'Progressive Drill-Down',
- 'features.progressive.desc': 'Navigate from project overview to module list to endpoint detail — retrieve only what you need.',
- 'features.token.title': 'Token Efficient',
- 'features.token.desc': '~200-2,000 tokens per call vs 10,000+ for dumping the full OpenAPI spec into context.',
- 'features.spec.title': 'Full Spec Support',
- 'features.spec.desc': 'Import OpenAPI 3.x and Swagger 2.0 docs. All $refs are dereferenced automatically.',
- 'features.import.title': 'One-Click Import',
- 'features.import.desc': 'Paste a URL or upload JSON/YAML — your API docs are parsed and indexed instantly.',
- 'features.projects.title': 'Multi-Project',
- 'features.projects.desc': 'Organize APIs into isolated projects, each with its own MCP endpoint and API key.',
- 'features.security.title': 'Secure by Default',
- 'features.security.desc': 'Project-level API keys with bcrypt hashing. JWT auth for the dashboard. Zero shared secrets.',
-
- // Tools
- 'tools.label': 'Compatibility',
- 'tools.title': 'Works with Your Favorite AI Tools',
- 'tools.subtitle': 'AgentFox speaks MCP — the universal protocol supported by leading AI coding assistants',
- 'tools.claude.name': 'Claude Code',
- 'tools.claude.desc': 'Anthropic CLI',
- 'tools.codex.name': 'Codex',
- 'tools.codex.desc': 'OpenAI CLI',
- 'tools.cursor.name': 'Cursor',
- 'tools.cursor.desc': 'AI Code Editor',
- 'tools.copilot.name': 'GitHub Copilot',
- 'tools.copilot.desc': 'GitHub AI Pair',
- 'tools.gemini.name': 'Gemini CLI',
- 'tools.gemini.desc': 'Google AI CLI',
- 'tools.antigravity.name': 'Antigravity',
- 'tools.antigravity.desc': 'AI Dev Platform',
- 'tools.openclaw.name': 'OpenClaw',
- 'tools.openclaw.desc': 'AI Dev Platform',
-
- // Testimonials
- 'testimonials.label': 'Testimonials',
- 'testimonials.title': 'Loved by Developers',
- 'testimonials.1.quote': 'AgentFox cut our API integration time in half. Instead of copy-pasting docs, Claude just queries what it needs through MCP.',
- 'testimonials.1.name': 'Sarah Chen',
- 'testimonials.1.role': 'Staff Engineer at Vercel',
- 'testimonials.2.quote': 'The token savings are real — our Cursor workflow went from burning 15K tokens per API call to under 1K. Game changer for complex integrations.',
- 'testimonials.2.name': 'Marcus Rivera',
- 'testimonials.2.role': 'CTO at Stackblitz',
- 'testimonials.3.quote': 'We onboarded 50+ internal APIs to AgentFox in a week. Now every team\'s AI assistant can discover and use any service endpoint.',
- 'testimonials.3.name': 'Yuki Tanaka',
- 'testimonials.3.role': 'Platform Lead at Shopify',
-
- // Pricing
- 'pricing.label': 'Pricing',
- 'pricing.title': 'Simple, Transparent Pricing',
- 'pricing.subtitle': 'Start free, scale as you grow',
- 'pricing.free.name': 'Free',
- 'pricing.free.price': '$0',
- 'pricing.free.period': '/month',
- 'pricing.free.desc': 'Perfect for trying out MCP-powered API docs',
- 'pricing.free.f1': '1 project',
- 'pricing.free.f2': '100 MCP queries/day',
- 'pricing.free.f3': 'OpenAPI 3.x & Swagger 2.0',
- 'pricing.free.f4': 'Community support',
- 'pricing.free.cta': 'Get Started',
- 'pricing.pro.name': 'Pro',
- 'pricing.pro.price': '$29',
- 'pricing.pro.period': '/month',
- 'pricing.pro.badge': 'Most Popular',
- 'pricing.pro.desc': 'For teams shipping with AI-assisted development',
- 'pricing.pro.f1': 'Unlimited projects',
- 'pricing.pro.f2': 'Unlimited MCP queries',
- 'pricing.pro.f3': 'Priority import queue',
- 'pricing.pro.f4': 'Team collaboration',
- 'pricing.pro.f5': 'Priority support',
- 'pricing.pro.cta': 'Start Free Trial',
- 'pricing.enterprise.name': 'Enterprise',
- 'pricing.enterprise.price': 'Custom',
- 'pricing.enterprise.period': '',
- 'pricing.enterprise.desc': 'For organizations with advanced requirements',
- 'pricing.enterprise.f1': 'Self-hosted deployment',
- 'pricing.enterprise.f2': 'SSO / SAML',
- 'pricing.enterprise.f3': 'SLA guarantee',
- 'pricing.enterprise.f4': 'Dedicated support',
- 'pricing.enterprise.f5': 'Custom integrations',
- 'pricing.enterprise.cta': 'Contact Sales',
-
- // FAQ
- 'faq.label': 'FAQ',
- 'faq.title': 'Frequently Asked Questions',
- 'faq.1.q': 'What is MCP and how does AgentFox use it?',
- 'faq.1.a': 'MCP (Model Context Protocol) is an open standard that lets AI assistants connect to external tools and data sources. AgentFox exposes your API documentation through MCP tools, so AI coding assistants like Claude Code, Cursor, and Copilot can query endpoint details on demand — without dumping the entire spec into their context window.',
- 'faq.2.q': 'Which OpenAPI formats are supported?',
- 'faq.2.a': 'AgentFox supports both OpenAPI 3.x and Swagger 2.0 specifications. You can import documents in JSON or YAML format, or provide a URL to fetch them directly. All $ref references are automatically dereferenced during import.',
- 'faq.3.q': 'How much does it reduce token usage?',
- 'faq.3.a': 'Each MCP tool call returns ~200-2,000 tokens of focused information, compared to 10,000+ tokens for dumping a full API spec. For a typical integration task, this means 80-95% reduction in token consumption.',
- 'faq.4.q': 'Is my API documentation secure?',
- 'faq.4.a': 'Yes. Each project has its own API key (bcrypt hashed, never stored in plain text). The MCP endpoint requires authentication for every request. User dashboard access uses JWT with automatic token rotation.',
- 'faq.5.q': 'Which AI tools are compatible?',
- 'faq.5.a': 'Any tool that supports the MCP protocol can connect to AgentFox. This includes Claude Code, OpenAI Codex CLI, OpenClaw, Gemini CLI, Cursor, GitHub Copilot (via MCP plugins), Antigravity, and more. If your tool supports MCP, it works with AgentFox.',
- 'faq.6.q': 'Can I self-host AgentFox?',
- 'faq.6.a': 'Yes! AgentFox is designed for both cloud and self-hosted deployment. The Enterprise plan includes full self-hosted support with Docker Compose, along with SSO integration and dedicated support.',
-
- // Footer
- 'footer.product': 'Product',
- 'footer.features': 'Features',
- 'footer.pricing': 'Pricing',
- 'footer.docs': 'Documentation',
- 'footer.changelog': 'Changelog',
- 'footer.resources': 'Resources',
- 'footer.github': 'GitHub',
- 'footer.community': 'Community',
- 'footer.blog': 'Blog',
- 'footer.legal': 'Legal',
- 'footer.privacy': 'Privacy',
- 'footer.terms': 'Terms',
- 'footer.copyright': '© 2026 AgentFox. All rights reserved.',
- 'footer.tagline': 'MCP-powered API documentation for AI agents.',
- },
- zh: {
- // Nav
- 'nav.features': '特性',
- 'nav.tools': '工具',
- 'nav.testimonials': '用户评价',
- 'nav.pricing': '定价',
- 'nav.faq': '常见问题',
- 'nav.signIn': '登录',
- 'nav.getStarted': '免费开始',
- 'nav.dashboard': '控制台',
-
- // Hero
- 'hero.badge': 'MCP 驱动的 API 智能服务',
- 'hero.title': '为 LLM 而生的',
- 'hero.titleHighlight': 'API 文档',
- 'hero.subtitle': '让 AI 代理以精准的方式查询你的 OpenAPI 文档。多级检索只提供所需的 token,而非整个规范。',
- 'hero.cta': '免费开始',
- 'hero.ctaSecondary': '查看文档',
- 'hero.terminal.comment': '# 将你的 AI 工具连接到任何 API 文档',
- 'hero.terminal.cmd1': 'get_project_overview',
- 'hero.terminal.res1': '{ "name": "Stripe API", "modules": 12, "endpoints": 247 }',
- 'hero.terminal.cmd2': 'list_endpoints moduleId="payments"',
- 'hero.terminal.res2': '[ "POST /charges", "GET /charges/:id", "POST /refunds" ... ]',
- 'hero.terminal.cmd3': 'get_endpoint_detail endpointId="create-charge"',
- 'hero.terminal.res3': '{ params: [...], requestBody: {...}, responses: {...} } // ~800 tokens',
-
- // Features
- 'features.label': '核心特性',
- 'features.title': '智能 API 检索',
- 'features.subtitle': '五个专用 MCP 工具,每次调用最小化 token 消耗',
- 'features.progressive.title': '渐进式下钻',
- 'features.progressive.desc': '从项目概览到模块列表再到端点详情,按需精确检索。',
- 'features.token.title': 'Token 高效',
- 'features.token.desc': '每次调用 ~200-2,000 tokens,对比全量 OpenAPI 规范的 10,000+ tokens。',
- 'features.spec.title': '全规范支持',
- 'features.spec.desc': '导入 OpenAPI 3.x 和 Swagger 2.0 文档,所有 $ref 自动解引用。',
- 'features.import.title': '一键导入',
- 'features.import.desc': '粘贴 URL 或上传 JSON/YAML 文件,API 文档即时解析并索引。',
- 'features.projects.title': '多项目管理',
- 'features.projects.desc': '将 API 组织到独立项目中,每个项目拥有专属 MCP 端点和 API Key。',
- 'features.security.title': '安全可靠',
- 'features.security.desc': '项目级 API Key(bcrypt 哈希加密),JWT 双令牌认证,零共享密钥。',
-
- // Tools
- 'tools.label': '兼容性',
- 'tools.title': '兼容你常用的 AI 工具',
- 'tools.subtitle': 'AgentFox 使用 MCP 协议 — 主流 AI 编程助手均已支持的通用协议',
- 'tools.claude.name': 'Claude Code',
- 'tools.claude.desc': 'Anthropic CLI',
- 'tools.codex.name': 'Codex',
- 'tools.codex.desc': 'OpenAI CLI',
- 'tools.cursor.name': 'Cursor',
- 'tools.cursor.desc': 'AI 代码编辑器',
- 'tools.copilot.name': 'GitHub Copilot',
- 'tools.copilot.desc': 'GitHub AI 助手',
- 'tools.gemini.name': 'Gemini CLI',
- 'tools.gemini.desc': 'Google AI CLI',
- 'tools.antigravity.name': 'Antigravity',
- 'tools.antigravity.desc': 'AI 开发平台',
- 'tools.openclaw.name': 'OpenClaw',
- 'tools.openclaw.desc': 'AI 开发平台',
-
- // Testimonials
- 'testimonials.label': '用户评价',
- 'testimonials.title': '深受开发者喜爱',
- 'testimonials.1.quote': 'AgentFox 将我们的 API 集成时间缩短了一半。Claude 通过 MCP 直接查询所需内容,不再需要复制粘贴文档。',
- 'testimonials.1.name': 'Sarah Chen',
- 'testimonials.1.role': 'Vercel 高级工程师',
- 'testimonials.2.quote': 'Token 节省是实实在在的 — 我们 Cursor 工作流从每次 API 调用消耗 15K tokens 降到了不到 1K。复杂集成的真正利器。',
- 'testimonials.2.name': 'Marcus Rivera',
- 'testimonials.2.role': 'Stackblitz CTO',
- 'testimonials.3.quote': '我们在一周内将 50+ 内部 API 接入了 AgentFox。现在每个团队的 AI 助手都能发现和使用任何服务端点。',
- 'testimonials.3.name': 'Yuki Tanaka',
- 'testimonials.3.role': 'Shopify 平台负责人',
-
- // Pricing
- 'pricing.label': '定价方案',
- 'pricing.title': '简洁透明的定价',
- 'pricing.subtitle': '免费起步,按需扩展',
- 'pricing.free.name': '免费版',
- 'pricing.free.price': '¥0',
- 'pricing.free.period': '/月',
- 'pricing.free.desc': '体验 MCP 驱动的 API 文档服务',
- 'pricing.free.f1': '1 个项目',
- 'pricing.free.f2': '每日 100 次 MCP 查询',
- 'pricing.free.f3': 'OpenAPI 3.x & Swagger 2.0',
- 'pricing.free.f4': '社区支持',
- 'pricing.free.cta': '免费开始',
- 'pricing.pro.name': '专业版',
- 'pricing.pro.price': '¥199',
- 'pricing.pro.period': '/月',
- 'pricing.pro.badge': '最受欢迎',
- 'pricing.pro.desc': '为 AI 辅助开发团队打造',
- 'pricing.pro.f1': '无限项目',
- 'pricing.pro.f2': '无限 MCP 查询',
- 'pricing.pro.f3': '优先导入队列',
- 'pricing.pro.f4': '团队协作',
- 'pricing.pro.f5': '优先支持',
- 'pricing.pro.cta': '开始免费试用',
- 'pricing.enterprise.name': '企业版',
- 'pricing.enterprise.price': '联系我们',
- 'pricing.enterprise.period': '',
- 'pricing.enterprise.desc': '满足企业级高级需求',
- 'pricing.enterprise.f1': '私有化部署',
- 'pricing.enterprise.f2': 'SSO / SAML',
- 'pricing.enterprise.f3': 'SLA 保障',
- 'pricing.enterprise.f4': '专属支持',
- 'pricing.enterprise.f5': '定制集成',
- 'pricing.enterprise.cta': '联系销售',
-
- // FAQ
- 'faq.label': '常见问题',
- 'faq.title': '常见问题解答',
- 'faq.1.q': '什么是 MCP?AgentFox 如何使用它?',
- 'faq.1.a': 'MCP(Model Context Protocol)是一个开放标准,让 AI 助手能够连接外部工具和数据源。AgentFox 通过 MCP 工具暴露你的 API 文档,让 Claude Code、Cursor、Copilot 等 AI 编程助手可以按需查询端点详情,而无需将整个规范放入上下文窗口。',
- 'faq.2.q': '支持哪些 OpenAPI 格式?',
- 'faq.2.a': 'AgentFox 支持 OpenAPI 3.x 和 Swagger 2.0 规范。你可以导入 JSON 或 YAML 格式的文档,也可以提供 URL 直接获取。导入时所有 $ref 引用会自动解引用。',
- 'faq.3.q': '能减少多少 Token 消耗?',
- 'faq.3.a': '每次 MCP 工具调用返回 ~200-2,000 tokens 的精准信息,相比全量 API 规范的 10,000+ tokens。对于典型的集成任务,这意味着 80-95% 的 token 消耗降低。',
- 'faq.4.q': '我的 API 文档安全吗?',
- 'faq.4.a': '是的。每个项目拥有独立的 API Key(bcrypt 哈希加密,从不以明文存储)。MCP 端点每次请求都需要认证。用户仪表盘使用 JWT 并自动轮换 token。',
- 'faq.5.q': '兼容哪些 AI 工具?',
- 'faq.5.a': '任何支持 MCP 协议的工具都可以连接 AgentFox,包括 Claude Code、OpenAI Codex CLI、OpenClaw、Gemini CLI、Cursor、GitHub Copilot(通过 MCP 插件)、Antigravity 等。如果你的工具支持 MCP,就能与 AgentFox 配合使用。',
- 'faq.6.q': '可以私有化部署吗?',
- 'faq.6.a': '可以!AgentFox 支持云端和私有化部署。企业版包含完整的 Docker Compose 私有化部署支持,以及 SSO 集成和专属技术支持。',
-
- // Footer
- 'footer.product': '产品',
- 'footer.features': '特性',
- 'footer.pricing': '定价',
- 'footer.docs': '文档',
- 'footer.changelog': '更新日志',
- 'footer.resources': '资源',
- 'footer.github': 'GitHub',
- 'footer.community': '社区',
- 'footer.blog': '博客',
- 'footer.legal': '法律',
- 'footer.privacy': '隐私政策',
- 'footer.terms': '服务条款',
- 'footer.copyright': '© 2026 AgentFox. 保留所有权利。',
- 'footer.tagline': '为 AI 代理打造的 MCP 驱动 API 文档服务。',
- },
-};
+const translations: AllTranslations = { en, zh };
type I18nContextType = {
locale: Locale;
setLocale: (l: Locale) => void;
- t: (key: string) => string;
+ t: (key: string, params?: Record) => string;
};
const I18nContext = createContext(null);
@@ -314,8 +31,14 @@ export function I18nProvider({ children }: { children: ReactNode }) {
localStorage.setItem('agent-fox-locale', l);
}, []);
- const t = useCallback((key: string): string => {
- return translations[locale][key] ?? key;
+ const t = useCallback((key: string, params?: Record): string => {
+ let text = translations[locale][key] ?? key;
+ if (params) {
+ Object.entries(params).forEach(([k, v]) => {
+ text = text.replaceAll(`{${k}}`, String(v));
+ });
+ }
+ return text;
}, [locale]);
return (
diff --git a/packages/web/src/lib/i18n/en.ts b/packages/web/src/lib/i18n/en.ts
new file mode 100644
index 0000000..5fd1e96
--- /dev/null
+++ b/packages/web/src/lib/i18n/en.ts
@@ -0,0 +1,361 @@
+const en: Record = {
+ // ===== Landing Page =====
+
+ // Nav
+ 'nav.features': 'Features',
+ 'nav.tools': 'Tools',
+ 'nav.testimonials': 'Testimonials',
+ 'nav.pricing': 'Pricing',
+ 'nav.faq': 'FAQ',
+ 'nav.signIn': 'Sign In',
+ 'nav.getStarted': 'Get Started',
+ 'nav.dashboard': 'Dashboard',
+
+ // Hero
+ 'hero.badge': 'MCP-Powered API Intelligence',
+ 'hero.title': 'API Docs for LLMs,',
+ 'hero.titleHighlight': 'Done Right',
+ 'hero.subtitle': 'Let AI agents query your OpenAPI documentation with surgical precision. Multi-level retrieval serves exactly the tokens needed — not the entire spec.',
+ 'hero.cta': 'Start Free',
+ 'hero.ctaSecondary': 'View Documentation',
+ 'hero.terminal.comment': '# Connect your AI tool to any API documentation',
+ 'hero.terminal.cmd1': 'get_project_overview',
+ 'hero.terminal.res1': '{ "name": "Stripe API", "modules": 12, "endpoints": 247 }',
+ 'hero.terminal.cmd2': 'list_endpoints moduleId="payments"',
+ 'hero.terminal.res2': '[ "POST /charges", "GET /charges/:id", "POST /refunds" ... ]',
+ 'hero.terminal.cmd3': 'get_endpoint_detail endpointId="create-charge"',
+ 'hero.terminal.res3': '{ params: [...], requestBody: {...}, responses: {...} } // ~800 tokens',
+
+ // Features
+ 'features.label': 'Features',
+ 'features.title': 'Intelligent API Retrieval',
+ 'features.subtitle': 'Five specialized MCP tools designed for minimal token usage per call',
+ 'features.progressive.title': 'Progressive Drill-Down',
+ 'features.progressive.desc': 'Navigate from project overview to module list to endpoint detail — retrieve only what you need.',
+ 'features.token.title': 'Token Efficient',
+ 'features.token.desc': '~200-2,000 tokens per call vs 10,000+ for dumping the full OpenAPI spec into context.',
+ 'features.spec.title': 'Full Spec Support',
+ 'features.spec.desc': 'Import OpenAPI 3.x and Swagger 2.0 docs. All $refs are dereferenced automatically.',
+ 'features.import.title': 'One-Click Import',
+ 'features.import.desc': 'Paste a URL or upload JSON/YAML — your API docs are parsed and indexed instantly.',
+ 'features.projects.title': 'Multi-Project',
+ 'features.projects.desc': 'Organize APIs into isolated projects, each with its own MCP endpoint and API key.',
+ 'features.security.title': 'Secure by Default',
+ 'features.security.desc': 'Project-level API keys with bcrypt hashing. JWT auth for the dashboard. Zero shared secrets.',
+
+ // Tools
+ 'tools.label': 'Compatibility',
+ 'tools.title': 'Works with Your Favorite AI Tools',
+ 'tools.subtitle': 'AgentFox speaks MCP — the universal protocol supported by leading AI coding assistants',
+ 'tools.claude.name': 'Claude Code',
+ 'tools.claude.desc': 'Anthropic CLI',
+ 'tools.codex.name': 'Codex',
+ 'tools.codex.desc': 'OpenAI CLI',
+ 'tools.cursor.name': 'Cursor',
+ 'tools.cursor.desc': 'AI Code Editor',
+ 'tools.copilot.name': 'GitHub Copilot',
+ 'tools.copilot.desc': 'GitHub AI Pair',
+ 'tools.gemini.name': 'Gemini CLI',
+ 'tools.gemini.desc': 'Google AI CLI',
+ 'tools.antigravity.name': 'Antigravity',
+ 'tools.antigravity.desc': 'AI Dev Platform',
+ 'tools.openclaw.name': 'OpenClaw',
+ 'tools.openclaw.desc': 'AI Dev Platform',
+
+ // Testimonials
+ 'testimonials.label': 'Testimonials',
+ 'testimonials.title': 'Loved by Developers',
+ 'testimonials.1.quote': 'AgentFox cut our API integration time in half. Instead of copy-pasting docs, Claude just queries what it needs through MCP.',
+ 'testimonials.1.name': 'Sarah Chen',
+ 'testimonials.1.role': 'Staff Engineer at Vercel',
+ 'testimonials.2.quote': 'The token savings are real — our Cursor workflow went from burning 15K tokens per API call to under 1K. Game changer for complex integrations.',
+ 'testimonials.2.name': 'Marcus Rivera',
+ 'testimonials.2.role': 'CTO at Stackblitz',
+ 'testimonials.3.quote': 'We onboarded 50+ internal APIs to AgentFox in a week. Now every team\'s AI assistant can discover and use any service endpoint.',
+ 'testimonials.3.name': 'Yuki Tanaka',
+ 'testimonials.3.role': 'Platform Lead at Shopify',
+
+ // Pricing
+ 'pricing.label': 'Pricing',
+ 'pricing.title': 'Simple, Transparent Pricing',
+ 'pricing.subtitle': 'Start free, scale as you grow',
+ 'pricing.free.name': 'Free',
+ 'pricing.free.price': '$0',
+ 'pricing.free.period': '/month',
+ 'pricing.free.desc': 'Perfect for trying out MCP-powered API docs',
+ 'pricing.free.f1': '1 project',
+ 'pricing.free.f2': '100 MCP queries/day',
+ 'pricing.free.f3': 'OpenAPI 3.x & Swagger 2.0',
+ 'pricing.free.f4': 'Community support',
+ 'pricing.free.cta': 'Get Started',
+ 'pricing.pro.name': 'Pro',
+ 'pricing.pro.price': '$29',
+ 'pricing.pro.period': '/month',
+ 'pricing.pro.badge': 'Most Popular',
+ 'pricing.pro.desc': 'For teams shipping with AI-assisted development',
+ 'pricing.pro.f1': 'Unlimited projects',
+ 'pricing.pro.f2': 'Unlimited MCP queries',
+ 'pricing.pro.f3': 'Priority import queue',
+ 'pricing.pro.f4': 'Team collaboration',
+ 'pricing.pro.f5': 'Priority support',
+ 'pricing.pro.cta': 'Start Free Trial',
+ 'pricing.enterprise.name': 'Enterprise',
+ 'pricing.enterprise.price': 'Custom',
+ 'pricing.enterprise.period': '',
+ 'pricing.enterprise.desc': 'For organizations with advanced requirements',
+ 'pricing.enterprise.f1': 'Self-hosted deployment',
+ 'pricing.enterprise.f2': 'SSO / SAML',
+ 'pricing.enterprise.f3': 'SLA guarantee',
+ 'pricing.enterprise.f4': 'Dedicated support',
+ 'pricing.enterprise.f5': 'Custom integrations',
+ 'pricing.enterprise.cta': 'Contact Sales',
+
+ // FAQ
+ 'faq.label': 'FAQ',
+ 'faq.title': 'Frequently Asked Questions',
+ 'faq.1.q': 'What is MCP and how does AgentFox use it?',
+ 'faq.1.a': 'MCP (Model Context Protocol) is an open standard that lets AI assistants connect to external tools and data sources. AgentFox exposes your API documentation through MCP tools, so AI coding assistants like Claude Code, Cursor, and Copilot can query endpoint details on demand — without dumping the entire spec into their context window.',
+ 'faq.2.q': 'Which OpenAPI formats are supported?',
+ 'faq.2.a': 'AgentFox supports both OpenAPI 3.x and Swagger 2.0 specifications. You can import documents in JSON or YAML format, or provide a URL to fetch them directly. All $ref references are automatically dereferenced during import.',
+ 'faq.3.q': 'How much does it reduce token usage?',
+ 'faq.3.a': 'Each MCP tool call returns ~200-2,000 tokens of focused information, compared to 10,000+ tokens for dumping a full API spec. For a typical integration task, this means 80-95% reduction in token consumption.',
+ 'faq.4.q': 'Is my API documentation secure?',
+ 'faq.4.a': 'Yes. Each project has its own API key (bcrypt hashed, never stored in plain text). The MCP endpoint requires authentication for every request. User dashboard access uses JWT with automatic token rotation.',
+ 'faq.5.q': 'Which AI tools are compatible?',
+ 'faq.5.a': 'Any tool that supports the MCP protocol can connect to AgentFox. This includes Claude Code, OpenAI Codex CLI, OpenClaw, Gemini CLI, Cursor, GitHub Copilot (via MCP plugins), Antigravity, and more. If your tool supports MCP, it works with AgentFox.',
+ 'faq.6.q': 'Can I self-host AgentFox?',
+ 'faq.6.a': 'Yes! AgentFox is designed for both cloud and self-hosted deployment. The Enterprise plan includes full self-hosted support with Docker Compose, along with SSO integration and dedicated support.',
+
+ // Footer
+ 'footer.product': 'Product',
+ 'footer.features': 'Features',
+ 'footer.pricing': 'Pricing',
+ 'footer.docs': 'Documentation',
+ 'footer.changelog': 'Changelog',
+ 'footer.resources': 'Resources',
+ 'footer.github': 'GitHub',
+ 'footer.community': 'Community',
+ 'footer.blog': 'Blog',
+ 'footer.legal': 'Legal',
+ 'footer.privacy': 'Privacy',
+ 'footer.terms': 'Terms',
+ 'footer.copyright': '© 2026 AgentFox. All rights reserved.',
+ 'footer.tagline': 'MCP-powered API documentation for AI agents.',
+
+ // ===== Common =====
+ 'common.cancel': 'Cancel',
+ 'common.confirm': 'Confirm',
+ 'common.delete': 'Delete',
+ 'common.save': 'Save',
+ 'common.back': 'Back',
+ 'common.done': 'Done',
+ 'common.copy': 'Copy',
+ 'common.copied': 'Copied',
+ 'common.continue': 'Continue',
+ 'common.import': 'Import',
+ 'common.importing': 'Importing...',
+ 'common.signOut': 'Sign Out',
+ 'common.signOutConfirm': 'Are you sure you want to sign out?',
+ 'common.settings': 'Settings',
+ 'common.modules': 'Modules',
+ 'common.endpoints': 'Endpoints',
+ 'common.total': 'total',
+ 'common.add': 'Add',
+ 'common.fromUrl': 'From URL',
+ 'common.uploadFile': 'Upload File',
+ 'common.dropFile': 'Drop your OpenAPI file here',
+ 'common.jsonOrYaml': 'JSON or YAML',
+
+ // ===== Theme =====
+ 'theme.light': 'Light',
+ 'theme.dark': 'Dark',
+ 'theme.system': 'System',
+
+ // ===== Auth =====
+ // Login
+ 'auth.login.title': 'Sign in to AgentFox',
+ 'auth.login.subtitle': 'API documentation for LLMs',
+ 'auth.login.email': 'Email',
+ 'auth.login.password': 'Password',
+ 'auth.login.submit': 'Sign In',
+ 'auth.login.submitting': 'Signing in...',
+ 'auth.login.noAccount': 'Don\'t have an account?',
+ 'auth.login.signUp': 'Sign Up',
+ 'auth.login.emailRequired': 'Email is required',
+ 'auth.login.emailInvalid': 'Please enter a valid email address',
+ 'auth.login.passwordRequired': 'Password is required',
+ 'auth.login.passwordPlaceholder': 'Enter your password',
+
+ // Register
+ 'auth.register.title': 'Create your account',
+ 'auth.register.subtitle': 'Get started with AgentFox',
+ 'auth.register.name': 'Name',
+ 'auth.register.email': 'Email',
+ 'auth.register.password': 'Password',
+ 'auth.register.submit': 'Create Account',
+ 'auth.register.submitting': 'Creating account...',
+ 'auth.register.hasAccount': 'Already have an account?',
+ 'auth.register.signIn': 'Sign In',
+ 'auth.register.nameRequired': 'Name is required',
+ 'auth.register.emailRequired': 'Email is required',
+ 'auth.register.emailInvalid': 'Please enter a valid email address',
+ 'auth.register.passwordRequired': 'Password is required',
+ 'auth.register.passwordMin': 'Password must be at least 8 characters',
+ 'auth.register.namePlaceholder': 'Your name',
+ 'auth.register.passwordPlaceholder': 'At least 8 characters',
+
+ // ===== Dashboard Layout =====
+ 'dashboard.layout.projects': 'Projects',
+ 'dashboard.layout.allProjects': 'All Projects',
+ 'dashboard.layout.onboardingTitle': 'Welcome! Generate an API key to start using MCP services.',
+ 'dashboard.layout.onboardingDesc': 'You\'ll need an API key to connect your LLM client to your projects.',
+ 'dashboard.layout.generateApiKey': 'Generate API Key',
+
+ // ===== Dashboard Projects =====
+ 'dashboard.projects.title': 'Projects',
+ 'dashboard.projects.importBtn': 'Import API Doc',
+ 'dashboard.projects.emptyTitle': 'No projects yet',
+ 'dashboard.projects.emptyDesc': 'Import an OpenAPI document to get started with MCP-powered API documentation.',
+ 'dashboard.projects.importFirst': 'Import Your First API',
+ 'dashboard.projects.deleteTitle': 'Delete project',
+ 'dashboard.projects.deleteDesc': 'Are you sure you want to delete "{name}"? This will permanently remove all modules, endpoints, and MCP configuration.',
+ 'dashboard.projects.deleteBtn': 'Delete project',
+
+ // ===== Project Detail =====
+ 'dashboard.projectDetail.breadcrumbProjects': 'Projects',
+ 'dashboard.projectDetail.notFound': 'Project not found',
+ 'dashboard.projectDetail.backToProjects': 'Back to projects',
+ 'dashboard.projectDetail.tabMcp': 'MCP',
+ 'dashboard.projectDetail.tabDocs': 'Documentation',
+ 'dashboard.projectDetail.tabModules': 'Modules',
+ 'dashboard.projectDetail.tabSettings': 'Settings',
+
+ // ===== Import Dialog =====
+ 'dashboard.import.title': 'Import OpenAPI Document',
+ 'dashboard.import.desc': 'Import a Swagger 2.0 or OpenAPI 3.x document to create a new project.',
+ 'dashboard.import.successTitle': 'Import Successful',
+ 'dashboard.import.goToProject': 'Go to Project',
+
+ // ===== Reimport Dialog =====
+ 'dashboard.reimport.title': 'Re-import API Document',
+ 'dashboard.reimport.subtitle': 'This action will replace all existing data.',
+ 'dashboard.reimport.warningTitle': 'The following data will be permanently deleted:',
+ 'dashboard.reimport.warningModules': '{count} module(s)',
+ 'dashboard.reimport.warningEndpoints': '{count} endpoint(s)',
+ 'dashboard.reimport.warningNote': 'New modules and endpoints will be created from the imported document. The API key will remain unchanged.',
+ 'dashboard.reimport.importTitle': 'Import New Document',
+ 'dashboard.reimport.importDesc': 'Provide a Swagger 2.0 or OpenAPI 3.x document.',
+ 'dashboard.reimport.submit': 'Re-import',
+ 'dashboard.reimport.successTitle': 'Re-import Successful',
+ 'dashboard.reimport.successDesc': 'API documentation has been updated.',
+
+ // ===== MCP Integration =====
+ 'dashboard.mcp.urlTitle': 'MCP Service URL',
+ 'dashboard.mcp.urlDesc': 'Connect your LLM client to this endpoint.',
+ 'dashboard.mcp.configTitle': 'Configuration for Claude Code / Cursor',
+ 'dashboard.mcp.configDesc': 'Add this to your MCP client configuration.',
+ 'dashboard.mcp.keyGenerated': 'API key generated. Copy it from',
+ 'dashboard.mcp.keyReplace': 'and replace',
+ 'dashboard.mcp.keyAbove': 'above.',
+ 'dashboard.mcp.noKeyWarning': 'You need to generate an API key before using MCP.',
+ 'dashboard.mcp.openSettings': 'Open Settings',
+ 'dashboard.mcp.toolsTitle': 'Available MCP Tools',
+ 'dashboard.mcp.toolsDesc': '5 tools for progressive drill-down, designed for minimal token usage.',
+ 'dashboard.mcp.tool1Desc': 'Get project name, version, base URL, and module summary. Call this first.',
+ 'dashboard.mcp.tool2Desc': 'List all modules with descriptions and endpoint counts.',
+ 'dashboard.mcp.tool3Desc': 'List endpoints in a module. Provide moduleId.',
+ 'dashboard.mcp.tool4Desc': 'Get full endpoint details: parameters, request body, responses.',
+ 'dashboard.mcp.tool5Desc': 'Search by keyword across all endpoints. Optional moduleId filter.',
+
+ // ===== Project Settings =====
+ 'dashboard.projectSettings.generalTitle': 'General',
+ 'dashboard.projectSettings.generalDesc': 'Update your project name and description.',
+ 'dashboard.projectSettings.projectName': 'Project Name',
+ 'dashboard.projectSettings.description': 'Description',
+ 'dashboard.projectSettings.saveChanges': 'Save Changes',
+ 'dashboard.projectSettings.saved': 'Saved',
+ 'dashboard.projectSettings.reimportTitle': 'Re-import API Document',
+ 'dashboard.projectSettings.reimportDesc': 'Replace the current API documentation with a new OpenAPI document. This will clear all existing modules ({modules}) and endpoints ({endpoints}), then recreate them from the new document.',
+ 'dashboard.projectSettings.reimportBtn': 'Re-import Document',
+ 'dashboard.projectSettings.dangerZone': 'Danger Zone',
+ 'dashboard.projectSettings.dangerDesc': 'Permanently delete this project and all its data. This action cannot be undone.',
+ 'dashboard.projectSettings.deleteProject': 'Delete Project',
+ 'dashboard.projectSettings.deleteTitle': 'Delete project',
+ 'dashboard.projectSettings.deleteDesc': 'Permanently delete "{name}"? All modules, endpoints, and MCP configuration will be removed.',
+
+ // ===== Module Management =====
+ 'dashboard.modules.addTitle': 'Add Manual Module',
+ 'dashboard.modules.placeholder': 'Module name',
+ 'dashboard.modules.allModules': 'All Modules',
+ 'dashboard.modules.emptyTitle': 'No modules yet',
+ 'dashboard.modules.emptyDesc': 'Modules are automatically created when you import an API document. You can also add manual modules above.',
+ 'dashboard.modules.deleteTitle': 'Delete module',
+ 'dashboard.modules.deleteDesc': 'Delete "{name}"? This will also remove its {count} endpoints.',
+ 'dashboard.modules.deleteBtn': 'Delete module',
+
+ // ===== Doc Preview =====
+ 'dashboard.docs.modules': 'Modules',
+ 'dashboard.docs.noModules': 'No modules',
+ 'dashboard.docs.allEndpoints': 'All endpoints',
+ 'dashboard.docs.noEndpoints': 'No endpoints',
+ 'dashboard.docs.noEndpointsModule': 'This module has no endpoints.',
+ 'dashboard.docs.noEndpointsProject': 'No endpoints in this project yet. Import an API document to get started.',
+ 'dashboard.docs.deprecated': 'deprecated',
+ 'dashboard.docs.operationId': 'Operation ID',
+
+ // ===== Schema View =====
+ 'dashboard.schema.parameters': 'Parameters',
+ 'dashboard.schema.name': 'Name',
+ 'dashboard.schema.in': 'In',
+ 'dashboard.schema.type': 'Type',
+ 'dashboard.schema.required': 'required',
+ 'dashboard.schema.optional': 'optional',
+ 'dashboard.schema.descriptionCol': 'Description',
+ 'dashboard.schema.requestBody': 'Request Body',
+ 'dashboard.schema.responses': 'Responses',
+ 'dashboard.schema.noSchema': 'No schema',
+ 'dashboard.schema.ofObjects': 'of objects:',
+ 'dashboard.schema.enum': 'enum:',
+ 'dashboard.schema.default': 'default:',
+ 'dashboard.schema.nullable': 'nullable',
+
+ // ===== Settings Dialog =====
+ 'dashboard.settings.title': 'Settings',
+ 'dashboard.settings.profileTitle': 'Profile',
+ 'dashboard.settings.profileDesc': 'Manage your personal information.',
+ 'dashboard.settings.displayName': 'Display Name',
+ 'dashboard.settings.saveProfile': 'Save Profile',
+ 'dashboard.settings.saving': 'Saving...',
+ 'dashboard.settings.profileUpdated': 'Profile updated',
+ 'dashboard.settings.apiKeyTitle': 'API Key',
+ 'dashboard.settings.apiKeyDesc': 'Used to authenticate all MCP requests across your projects.',
+ 'dashboard.settings.keySaveWarning': 'Save this key now — you won\'t be able to see it again.',
+ 'dashboard.settings.copyToClipboard': 'Copy to Clipboard',
+ 'dashboard.settings.keySaved': 'I\'ve saved it, continue',
+ 'dashboard.settings.noKey': 'No API key generated yet. Generate one to use MCP services.',
+ 'dashboard.settings.generateKey': 'Generate API Key',
+ 'dashboard.settings.generating': 'Generating...',
+ 'dashboard.settings.rotateKey': 'Rotate API Key',
+ 'dashboard.settings.rotateTitle': 'Rotate API Key',
+ 'dashboard.settings.rotateDesc': 'The current API key will be invalidated immediately. All MCP clients using the old key will stop working. A new key will be generated.',
+ 'dashboard.settings.rotateConfirm': 'Rotate Key',
+ 'dashboard.settings.passwordPrompt': 'Enter your password to {action} the API key.',
+ 'dashboard.settings.passwordPromptCopy': 'copy',
+ 'dashboard.settings.passwordPromptReveal': 'reveal',
+ 'dashboard.settings.currentPassword': 'Current password',
+ 'dashboard.settings.verifying': 'Verifying...',
+ 'dashboard.settings.changePasswordTitle': 'Change Password',
+ 'dashboard.settings.changePasswordDesc': 'Update your password to keep your account secure.',
+ 'dashboard.settings.currentPasswordLabel': 'Current Password',
+ 'dashboard.settings.newPasswordLabel': 'New Password',
+ 'dashboard.settings.confirmPasswordLabel': 'Confirm New Password',
+ 'dashboard.settings.changePassword': 'Change Password',
+ 'dashboard.settings.changingPassword': 'Changing...',
+ 'dashboard.settings.passwordMismatch': 'Passwords do not match',
+ 'dashboard.settings.passwordChanged': 'Password changed successfully',
+ 'dashboard.settings.enterCurrentPassword': 'Enter current password',
+ 'dashboard.settings.atLeast8Chars': 'At least 8 characters',
+ 'dashboard.settings.confirmNewPassword': 'Confirm new password',
+};
+
+export default en;
diff --git a/packages/web/src/lib/i18n/zh.ts b/packages/web/src/lib/i18n/zh.ts
new file mode 100644
index 0000000..b25faeb
--- /dev/null
+++ b/packages/web/src/lib/i18n/zh.ts
@@ -0,0 +1,361 @@
+const zh: Record = {
+ // ===== Landing Page =====
+
+ // Nav
+ 'nav.features': '特性',
+ 'nav.tools': '工具',
+ 'nav.testimonials': '用户评价',
+ 'nav.pricing': '定价',
+ 'nav.faq': '常见问题',
+ 'nav.signIn': '登录',
+ 'nav.getStarted': '免费开始',
+ 'nav.dashboard': '控制台',
+
+ // Hero
+ 'hero.badge': 'MCP 驱动的 API 智能服务',
+ 'hero.title': '为 LLM 而生的',
+ 'hero.titleHighlight': 'API 文档',
+ 'hero.subtitle': '让 AI 代理以精准的方式查询你的 OpenAPI 文档。多级检索只提供所需的 token,而非整个规范。',
+ 'hero.cta': '免费开始',
+ 'hero.ctaSecondary': '查看文档',
+ 'hero.terminal.comment': '# 将你的 AI 工具连接到任何 API 文档',
+ 'hero.terminal.cmd1': 'get_project_overview',
+ 'hero.terminal.res1': '{ "name": "Stripe API", "modules": 12, "endpoints": 247 }',
+ 'hero.terminal.cmd2': 'list_endpoints moduleId="payments"',
+ 'hero.terminal.res2': '[ "POST /charges", "GET /charges/:id", "POST /refunds" ... ]',
+ 'hero.terminal.cmd3': 'get_endpoint_detail endpointId="create-charge"',
+ 'hero.terminal.res3': '{ params: [...], requestBody: {...}, responses: {...} } // ~800 tokens',
+
+ // Features
+ 'features.label': '核心特性',
+ 'features.title': '智能 API 检索',
+ 'features.subtitle': '五个专用 MCP 工具,每次调用最小化 token 消耗',
+ 'features.progressive.title': '渐进式下钻',
+ 'features.progressive.desc': '从项目概览到模块列表再到端点详情,按需精确检索。',
+ 'features.token.title': 'Token 高效',
+ 'features.token.desc': '每次调用 ~200-2,000 tokens,对比全量 OpenAPI 规范的 10,000+ tokens。',
+ 'features.spec.title': '全规范支持',
+ 'features.spec.desc': '导入 OpenAPI 3.x 和 Swagger 2.0 文档,所有 $ref 自动解引用。',
+ 'features.import.title': '一键导入',
+ 'features.import.desc': '粘贴 URL 或上传 JSON/YAML 文件,API 文档即时解析并索引。',
+ 'features.projects.title': '多项目管理',
+ 'features.projects.desc': '将 API 组织到独立项目中,每个项目拥有专属 MCP 端点和 API Key。',
+ 'features.security.title': '安全可靠',
+ 'features.security.desc': '项目级 API Key(bcrypt 哈希加密),JWT 双令牌认证,零共享密钥。',
+
+ // Tools
+ 'tools.label': '兼容性',
+ 'tools.title': '兼容你常用的 AI 工具',
+ 'tools.subtitle': 'AgentFox 使用 MCP 协议 — 主流 AI 编程助手均已支持的通用协议',
+ 'tools.claude.name': 'Claude Code',
+ 'tools.claude.desc': 'Anthropic CLI',
+ 'tools.codex.name': 'Codex',
+ 'tools.codex.desc': 'OpenAI CLI',
+ 'tools.cursor.name': 'Cursor',
+ 'tools.cursor.desc': 'AI 代码编辑器',
+ 'tools.copilot.name': 'GitHub Copilot',
+ 'tools.copilot.desc': 'GitHub AI 助手',
+ 'tools.gemini.name': 'Gemini CLI',
+ 'tools.gemini.desc': 'Google AI CLI',
+ 'tools.antigravity.name': 'Antigravity',
+ 'tools.antigravity.desc': 'AI 开发平台',
+ 'tools.openclaw.name': 'OpenClaw',
+ 'tools.openclaw.desc': 'AI 开发平台',
+
+ // Testimonials
+ 'testimonials.label': '用户评价',
+ 'testimonials.title': '深受开发者喜爱',
+ 'testimonials.1.quote': 'AgentFox 将我们的 API 集成时间缩短了一半。Claude 通过 MCP 直接查询所需内容,不再需要复制粘贴文档。',
+ 'testimonials.1.name': 'Sarah Chen',
+ 'testimonials.1.role': 'Vercel 高级工程师',
+ 'testimonials.2.quote': 'Token 节省是实实在在的 — 我们 Cursor 工作流从每次 API 调用消耗 15K tokens 降到了不到 1K。复杂集成的真正利器。',
+ 'testimonials.2.name': 'Marcus Rivera',
+ 'testimonials.2.role': 'Stackblitz CTO',
+ 'testimonials.3.quote': '我们在一周内将 50+ 内部 API 接入了 AgentFox。现在每个团队的 AI 助手都能发现和使用任何服务端点。',
+ 'testimonials.3.name': 'Yuki Tanaka',
+ 'testimonials.3.role': 'Shopify 平台负责人',
+
+ // Pricing
+ 'pricing.label': '定价方案',
+ 'pricing.title': '简洁透明的定价',
+ 'pricing.subtitle': '免费起步,按需扩展',
+ 'pricing.free.name': '免费版',
+ 'pricing.free.price': '¥0',
+ 'pricing.free.period': '/月',
+ 'pricing.free.desc': '体验 MCP 驱动的 API 文档服务',
+ 'pricing.free.f1': '1 个项目',
+ 'pricing.free.f2': '每日 100 次 MCP 查询',
+ 'pricing.free.f3': 'OpenAPI 3.x & Swagger 2.0',
+ 'pricing.free.f4': '社区支持',
+ 'pricing.free.cta': '免费开始',
+ 'pricing.pro.name': '专业版',
+ 'pricing.pro.price': '¥199',
+ 'pricing.pro.period': '/月',
+ 'pricing.pro.badge': '最受欢迎',
+ 'pricing.pro.desc': '为 AI 辅助开发团队打造',
+ 'pricing.pro.f1': '无限项目',
+ 'pricing.pro.f2': '无限 MCP 查询',
+ 'pricing.pro.f3': '优先导入队列',
+ 'pricing.pro.f4': '团队协作',
+ 'pricing.pro.f5': '优先支持',
+ 'pricing.pro.cta': '开始免费试用',
+ 'pricing.enterprise.name': '企业版',
+ 'pricing.enterprise.price': '联系我们',
+ 'pricing.enterprise.period': '',
+ 'pricing.enterprise.desc': '满足企业级高级需求',
+ 'pricing.enterprise.f1': '私有化部署',
+ 'pricing.enterprise.f2': 'SSO / SAML',
+ 'pricing.enterprise.f3': 'SLA 保障',
+ 'pricing.enterprise.f4': '专属支持',
+ 'pricing.enterprise.f5': '定制集成',
+ 'pricing.enterprise.cta': '联系销售',
+
+ // FAQ
+ 'faq.label': '常见问题',
+ 'faq.title': '常见问题解答',
+ 'faq.1.q': '什么是 MCP?AgentFox 如何使用它?',
+ 'faq.1.a': 'MCP(Model Context Protocol)是一个开放标准,让 AI 助手能够连接外部工具和数据源。AgentFox 通过 MCP 工具暴露你的 API 文档,让 Claude Code、Cursor、Copilot 等 AI 编程助手可以按需查询端点详情,而无需将整个规范放入上下文窗口。',
+ 'faq.2.q': '支持哪些 OpenAPI 格式?',
+ 'faq.2.a': 'AgentFox 支持 OpenAPI 3.x 和 Swagger 2.0 规范。你可以导入 JSON 或 YAML 格式的文档,也可以提供 URL 直接获取。导入时所有 $ref 引用会自动解引用。',
+ 'faq.3.q': '能减少多少 Token 消耗?',
+ 'faq.3.a': '每次 MCP 工具调用返回 ~200-2,000 tokens 的精准信息,相比全量 API 规范的 10,000+ tokens。对于典型的集成任务,这意味着 80-95% 的 token 消耗降低。',
+ 'faq.4.q': '我的 API 文档安全吗?',
+ 'faq.4.a': '是的。每个项目拥有独立的 API Key(bcrypt 哈希加密,从不以明文存储)。MCP 端点每次请求都需要认证。用户仪表盘使用 JWT 并自动轮换 token。',
+ 'faq.5.q': '兼容哪些 AI 工具?',
+ 'faq.5.a': '任何支持 MCP 协议的工具都可以连接 AgentFox,包括 Claude Code、OpenAI Codex CLI、OpenClaw、Gemini CLI、Cursor、GitHub Copilot(通过 MCP 插件)、Antigravity 等。如果你的工具支持 MCP,就能与 AgentFox 配合使用。',
+ 'faq.6.q': '可以私有化部署吗?',
+ 'faq.6.a': '可以!AgentFox 支持云端和私有化部署。企业版包含完整的 Docker Compose 私有化部署支持,以及 SSO 集成和专属技术支持。',
+
+ // Footer
+ 'footer.product': '产品',
+ 'footer.features': '特性',
+ 'footer.pricing': '定价',
+ 'footer.docs': '文档',
+ 'footer.changelog': '更新日志',
+ 'footer.resources': '资源',
+ 'footer.github': 'GitHub',
+ 'footer.community': '社区',
+ 'footer.blog': '博客',
+ 'footer.legal': '法律',
+ 'footer.privacy': '隐私政策',
+ 'footer.terms': '服务条款',
+ 'footer.copyright': '© 2026 AgentFox. 保留所有权利。',
+ 'footer.tagline': '为 AI 代理打造的 MCP 驱动 API 文档服务。',
+
+ // ===== Common =====
+ 'common.cancel': '取消',
+ 'common.confirm': '确认',
+ 'common.delete': '删除',
+ 'common.save': '保存',
+ 'common.back': '返回',
+ 'common.done': '完成',
+ 'common.copy': '复制',
+ 'common.copied': '已复制',
+ 'common.continue': '继续',
+ 'common.import': '导入',
+ 'common.importing': '导入中...',
+ 'common.signOut': '退出登录',
+ 'common.signOutConfirm': '确定要退出登录吗?',
+ 'common.settings': '设置',
+ 'common.modules': '模块',
+ 'common.endpoints': '端点',
+ 'common.total': '总计',
+ 'common.add': '添加',
+ 'common.fromUrl': '从 URL',
+ 'common.uploadFile': '上传文件',
+ 'common.dropFile': '将 OpenAPI 文件拖放到这里',
+ 'common.jsonOrYaml': 'JSON 或 YAML',
+
+ // ===== Theme =====
+ 'theme.light': '浅色',
+ 'theme.dark': '深色',
+ 'theme.system': '跟随系统',
+
+ // ===== Auth =====
+ // Login
+ 'auth.login.title': '登录 AgentFox',
+ 'auth.login.subtitle': '为 LLM 打造的 API 文档服务',
+ 'auth.login.email': '邮箱',
+ 'auth.login.password': '密码',
+ 'auth.login.submit': '登录',
+ 'auth.login.submitting': '登录中...',
+ 'auth.login.noAccount': '还没有账号?',
+ 'auth.login.signUp': '注册',
+ 'auth.login.emailRequired': '请输入邮箱',
+ 'auth.login.emailInvalid': '请输入有效的邮箱地址',
+ 'auth.login.passwordRequired': '请输入密码',
+ 'auth.login.passwordPlaceholder': '输入你的密码',
+
+ // Register
+ 'auth.register.title': '创建账号',
+ 'auth.register.subtitle': '开始使用 AgentFox',
+ 'auth.register.name': '姓名',
+ 'auth.register.email': '邮箱',
+ 'auth.register.password': '密码',
+ 'auth.register.submit': '创建账号',
+ 'auth.register.submitting': '创建中...',
+ 'auth.register.hasAccount': '已有账号?',
+ 'auth.register.signIn': '登录',
+ 'auth.register.nameRequired': '请输入姓名',
+ 'auth.register.emailRequired': '请输入邮箱',
+ 'auth.register.emailInvalid': '请输入有效的邮箱地址',
+ 'auth.register.passwordRequired': '请输入密码',
+ 'auth.register.passwordMin': '密码至少需要 8 个字符',
+ 'auth.register.namePlaceholder': '你的姓名',
+ 'auth.register.passwordPlaceholder': '至少 8 个字符',
+
+ // ===== Dashboard Layout =====
+ 'dashboard.layout.projects': '项目',
+ 'dashboard.layout.allProjects': '所有项目',
+ 'dashboard.layout.onboardingTitle': '欢迎!生成 API Key 以开始使用 MCP 服务。',
+ 'dashboard.layout.onboardingDesc': '你需要一个 API Key 来将 LLM 客户端连接到你的项目。',
+ 'dashboard.layout.generateApiKey': '生成 API Key',
+
+ // ===== Dashboard Projects =====
+ 'dashboard.projects.title': '项目',
+ 'dashboard.projects.importBtn': '导入 API 文档',
+ 'dashboard.projects.emptyTitle': '暂无项目',
+ 'dashboard.projects.emptyDesc': '导入 OpenAPI 文档以开始使用 MCP 驱动的 API 文档服务。',
+ 'dashboard.projects.importFirst': '导入你的第一个 API',
+ 'dashboard.projects.deleteTitle': '删除项目',
+ 'dashboard.projects.deleteDesc': '确定要删除"{name}"吗?这将永久删除所有模块、端点和 MCP 配置。',
+ 'dashboard.projects.deleteBtn': '删除项目',
+
+ // ===== Project Detail =====
+ 'dashboard.projectDetail.breadcrumbProjects': '项目',
+ 'dashboard.projectDetail.notFound': '项目未找到',
+ 'dashboard.projectDetail.backToProjects': '返回项目列表',
+ 'dashboard.projectDetail.tabMcp': 'MCP',
+ 'dashboard.projectDetail.tabDocs': '文档',
+ 'dashboard.projectDetail.tabModules': '模块',
+ 'dashboard.projectDetail.tabSettings': '设置',
+
+ // ===== Import Dialog =====
+ 'dashboard.import.title': '导入 OpenAPI 文档',
+ 'dashboard.import.desc': '导入 Swagger 2.0 或 OpenAPI 3.x 文档以创建新项目。',
+ 'dashboard.import.successTitle': '导入成功',
+ 'dashboard.import.goToProject': '前往项目',
+
+ // ===== Reimport Dialog =====
+ 'dashboard.reimport.title': '重新导入 API 文档',
+ 'dashboard.reimport.subtitle': '此操作将替换所有现有数据。',
+ 'dashboard.reimport.warningTitle': '以下数据将被永久删除:',
+ 'dashboard.reimport.warningModules': '{count} 个模块',
+ 'dashboard.reimport.warningEndpoints': '{count} 个端点',
+ 'dashboard.reimport.warningNote': '将根据导入的文档创建新的模块和端点。API Key 将保持不变。',
+ 'dashboard.reimport.importTitle': '导入新文档',
+ 'dashboard.reimport.importDesc': '提供 Swagger 2.0 或 OpenAPI 3.x 文档。',
+ 'dashboard.reimport.submit': '重新导入',
+ 'dashboard.reimport.successTitle': '重新导入成功',
+ 'dashboard.reimport.successDesc': 'API 文档已更新。',
+
+ // ===== MCP Integration =====
+ 'dashboard.mcp.urlTitle': 'MCP 服务 URL',
+ 'dashboard.mcp.urlDesc': '将你的 LLM 客户端连接到此端点。',
+ 'dashboard.mcp.configTitle': 'Claude Code / Cursor 配置',
+ 'dashboard.mcp.configDesc': '将此内容添加到你的 MCP 客户端配置中。',
+ 'dashboard.mcp.keyGenerated': 'API Key 已生成。从',
+ 'dashboard.mcp.keyReplace': '复制并替换上方的',
+ 'dashboard.mcp.keyAbove': '。',
+ 'dashboard.mcp.noKeyWarning': '使用 MCP 前需要先生成 API Key。',
+ 'dashboard.mcp.openSettings': '打开设置',
+ 'dashboard.mcp.toolsTitle': '可用 MCP 工具',
+ 'dashboard.mcp.toolsDesc': '5 个渐进式下钻工具,为最小 token 消耗而设计。',
+ 'dashboard.mcp.tool1Desc': '获取项目名称、版本、基础 URL 和模块摘要。首先调用此工具。',
+ 'dashboard.mcp.tool2Desc': '列出所有模块及其描述和端点数量。',
+ 'dashboard.mcp.tool3Desc': '列出模块中的端点。需提供 moduleId。',
+ 'dashboard.mcp.tool4Desc': '获取完整端点详情:参数、请求体、响应。',
+ 'dashboard.mcp.tool5Desc': '按关键词搜索所有端点。可选 moduleId 过滤。',
+
+ // ===== Project Settings =====
+ 'dashboard.projectSettings.generalTitle': '基本信息',
+ 'dashboard.projectSettings.generalDesc': '更新项目名称和描述。',
+ 'dashboard.projectSettings.projectName': '项目名称',
+ 'dashboard.projectSettings.description': '描述',
+ 'dashboard.projectSettings.saveChanges': '保存更改',
+ 'dashboard.projectSettings.saved': '已保存',
+ 'dashboard.projectSettings.reimportTitle': '重新导入 API 文档',
+ 'dashboard.projectSettings.reimportDesc': '使用新的 OpenAPI 文档替换当前 API 文档。这将清除所有现有模块({modules})和端点({endpoints}),然后根据新文档重新创建。',
+ 'dashboard.projectSettings.reimportBtn': '重新导入文档',
+ 'dashboard.projectSettings.dangerZone': '危险区域',
+ 'dashboard.projectSettings.dangerDesc': '永久删除此项目及其所有数据。此操作不可撤销。',
+ 'dashboard.projectSettings.deleteProject': '删除项目',
+ 'dashboard.projectSettings.deleteTitle': '删除项目',
+ 'dashboard.projectSettings.deleteDesc': '永久删除"{name}"?所有模块、端点和 MCP 配置将被移除。',
+
+ // ===== Module Management =====
+ 'dashboard.modules.addTitle': '添加手动模块',
+ 'dashboard.modules.placeholder': '模块名称',
+ 'dashboard.modules.allModules': '所有模块',
+ 'dashboard.modules.emptyTitle': '暂无模块',
+ 'dashboard.modules.emptyDesc': '导入 API 文档时会自动创建模块。你也可以在上方手动添加模块。',
+ 'dashboard.modules.deleteTitle': '删除模块',
+ 'dashboard.modules.deleteDesc': '删除"{name}"?这将同时删除其 {count} 个端点。',
+ 'dashboard.modules.deleteBtn': '删除模块',
+
+ // ===== Doc Preview =====
+ 'dashboard.docs.modules': '模块',
+ 'dashboard.docs.noModules': '暂无模块',
+ 'dashboard.docs.allEndpoints': '所有端点',
+ 'dashboard.docs.noEndpoints': '暂无端点',
+ 'dashboard.docs.noEndpointsModule': '此模块暂无端点。',
+ 'dashboard.docs.noEndpointsProject': '此项目暂无端点。导入 API 文档以开始使用。',
+ 'dashboard.docs.deprecated': '已弃用',
+ 'dashboard.docs.operationId': '操作 ID',
+
+ // ===== Schema View =====
+ 'dashboard.schema.parameters': '参数',
+ 'dashboard.schema.name': '名称',
+ 'dashboard.schema.in': '位置',
+ 'dashboard.schema.type': '类型',
+ 'dashboard.schema.required': '必填',
+ 'dashboard.schema.optional': '可选',
+ 'dashboard.schema.descriptionCol': '说明',
+ 'dashboard.schema.requestBody': '请求体',
+ 'dashboard.schema.responses': '响应',
+ 'dashboard.schema.noSchema': '无 Schema',
+ 'dashboard.schema.ofObjects': '对象数组:',
+ 'dashboard.schema.enum': '枚举:',
+ 'dashboard.schema.default': '默认值:',
+ 'dashboard.schema.nullable': '可空',
+
+ // ===== Settings Dialog =====
+ 'dashboard.settings.title': '设置',
+ 'dashboard.settings.profileTitle': '个人资料',
+ 'dashboard.settings.profileDesc': '管理你的个人信息。',
+ 'dashboard.settings.displayName': '显示名称',
+ 'dashboard.settings.saveProfile': '保存资料',
+ 'dashboard.settings.saving': '保存中...',
+ 'dashboard.settings.profileUpdated': '资料已更新',
+ 'dashboard.settings.apiKeyTitle': 'API Key',
+ 'dashboard.settings.apiKeyDesc': '用于验证所有项目的 MCP 请求。',
+ 'dashboard.settings.keySaveWarning': '请立即保存此密钥 — 之后将无法再次查看。',
+ 'dashboard.settings.copyToClipboard': '复制到剪贴板',
+ 'dashboard.settings.keySaved': '我已保存,继续',
+ 'dashboard.settings.noKey': '尚未生成 API Key。生成一个以使用 MCP 服务。',
+ 'dashboard.settings.generateKey': '生成 API Key',
+ 'dashboard.settings.generating': '生成中...',
+ 'dashboard.settings.rotateKey': '轮换 API Key',
+ 'dashboard.settings.rotateTitle': '轮换 API Key',
+ 'dashboard.settings.rotateDesc': '当前 API Key 将立即失效。所有使用旧密钥的 MCP 客户端将停止工作。将生成新的密钥。',
+ 'dashboard.settings.rotateConfirm': '轮换密钥',
+ 'dashboard.settings.passwordPrompt': '输入密码以{action} API Key。',
+ 'dashboard.settings.passwordPromptCopy': '复制',
+ 'dashboard.settings.passwordPromptReveal': '查看',
+ 'dashboard.settings.currentPassword': '当前密码',
+ 'dashboard.settings.verifying': '验证中...',
+ 'dashboard.settings.changePasswordTitle': '修改密码',
+ 'dashboard.settings.changePasswordDesc': '更新密码以保护账号安全。',
+ 'dashboard.settings.currentPasswordLabel': '当前密码',
+ 'dashboard.settings.newPasswordLabel': '新密码',
+ 'dashboard.settings.confirmPasswordLabel': '确认新密码',
+ 'dashboard.settings.changePassword': '修改密码',
+ 'dashboard.settings.changingPassword': '修改中...',
+ 'dashboard.settings.passwordMismatch': '两次输入的密码不一致',
+ 'dashboard.settings.passwordChanged': '密码修改成功',
+ 'dashboard.settings.enterCurrentPassword': '输入当前密码',
+ 'dashboard.settings.atLeast8Chars': '至少 8 个字符',
+ 'dashboard.settings.confirmNewPassword': '确认新密码',
+};
+
+export default zh;
diff --git a/packages/web/src/pages/ImportDialog.tsx b/packages/web/src/pages/ImportDialog.tsx
index 28ca58c..8b34f2e 100644
--- a/packages/web/src/pages/ImportDialog.tsx
+++ b/packages/web/src/pages/ImportDialog.tsx
@@ -2,6 +2,7 @@ import { useState, useRef } from 'react';
import { useNavigate } from 'react-router-dom';
import { useQueryClient } from '@tanstack/react-query';
import { apiFetch } from '../lib/api';
+import { useI18n } from '../lib/i18n';
import Modal from '../components/Modal';
type ImportResult = {
@@ -21,6 +22,7 @@ export default function ImportDialog({ onClose }: { onClose: () => void }) {
const fileInputRef = useRef(null);
const navigate = useNavigate();
const queryClient = useQueryClient();
+ const { t } = useI18n();
const handleFile = (file: File) => {
setFileName(file.name);
@@ -63,14 +65,14 @@ export default function ImportDialog({ onClose }: { onClose: () => void }) {
{!result ? (
- Import OpenAPI Document
- Import a Swagger 2.0 or OpenAPI 3.x document to create a new project.
+ {t('dashboard.import.title')}
+ {t('dashboard.import.desc')}
{/* Mode toggle */}
-
-
+
+
{mode === 'url' ? (
@@ -93,8 +95,8 @@ export default function ImportDialog({ onClose }: { onClose: () => void }) {
{fileName}
) : (
<>
- Drop your OpenAPI file here
- JSON or YAML
+ {t('common.dropFile')}
+ {t('common.jsonOrYaml')}
>
)}
@@ -108,11 +110,11 @@ export default function ImportDialog({ onClose }: { onClose: () => void }) {
)}
-
+
@@ -123,7 +125,7 @@ export default function ImportDialog({ onClose }: { onClose: () => void }) {
- Import Successful
+ {t('dashboard.import.successTitle')}
{result.project.name}
@@ -131,16 +133,16 @@ export default function ImportDialog({ onClose }: { onClose: () => void }) {
{result.stats.modules}
- Modules
+ {t('common.modules')}
{result.stats.endpoints}
- Endpoints
+ {t('common.endpoints')}
-
+
)}
diff --git a/packages/web/src/pages/Layout.tsx b/packages/web/src/pages/Layout.tsx
index ad7a10b..44167d1 100644
--- a/packages/web/src/pages/Layout.tsx
+++ b/packages/web/src/pages/Layout.tsx
@@ -2,8 +2,10 @@ import { useState, useRef, useEffect } from 'react';
import { Navigate, Outlet, NavLink, Link, useLocation, useParams, useOutletContext } from 'react-router-dom';
import { useQuery } from '@tanstack/react-query';
import { useAuth } from '../lib/auth';
+import { useI18n } from '../lib/i18n';
import { apiFetch } from '../lib/api';
import ThemeToggle from '../components/ThemeToggle';
+import LanguageToggle from '../components/LanguageToggle';
import SettingsDialog from '../components/SettingsDialog';
import ConfirmDialog from '../components/ConfirmDialog';
@@ -19,6 +21,7 @@ function UserDropdown({ user, logout, onOpenSettings }: { user: { name: string;
const [open, setOpen] = useState(false);
const [confirmLogout, setConfirmLogout] = useState(false);
const ref = useRef(null);
+ const { t } = useI18n();
const initials = user.name.split(' ').map(w => w[0]).join('').toUpperCase().slice(0, 2);
useEffect(() => {
@@ -73,7 +76,7 @@ function UserDropdown({ user, logout, onOpenSettings }: { user: { name: string;
- Settings
+ {t('common.settings')}
@@ -92,9 +95,9 @@ function UserDropdown({ user, logout, onOpenSettings }: { user: { name: string;
open={confirmLogout}
onConfirm={() => { setConfirmLogout(false); logout(); }}
onCancel={() => setConfirmLogout(false)}
- title="Sign Out"
- description="Are you sure you want to sign out?"
- confirmText="Sign Out"
+ title={t('common.signOut')}
+ description={t('common.signOutConfirm')}
+ confirmText={t('common.signOut')}
variant="warning"
/>
@@ -105,6 +108,7 @@ function ProjectSidebar() {
const location = useLocation();
const params = useParams();
const activeProjectId = params.id;
+ const { t } = useI18n();
const { data: projects, isLoading } = useQuery({
queryKey: ['projects'],
@@ -117,7 +121,7 @@ function ProjectSidebar() {
|