From da2be8045ff3e636e3a790c839502edd6601024f Mon Sep 17 00:00:00 2001 From: pomelo-nwu Date: Mon, 17 Nov 2025 20:23:07 +0800 Subject: [PATCH] feat: add i18n utils --- package.json | 1 + packages/cli/src/config/settingsSchema.ts | 17 + packages/cli/src/core/auth.ts | 5 +- packages/cli/src/core/initializer.ts | 8 + packages/cli/src/i18n/index.ts | 232 ++++++++++ packages/cli/src/i18n/locales/en.js | 513 ++++++++++++++++++++++ packages/cli/src/i18n/locales/zh.js | 479 ++++++++++++++++++++ scripts/copy_files.js | 12 +- 8 files changed, 1264 insertions(+), 3 deletions(-) create mode 100644 packages/cli/src/i18n/index.ts create mode 100644 packages/cli/src/i18n/locales/en.js create mode 100644 packages/cli/src/i18n/locales/zh.js diff --git a/package.json b/package.json index a8b69061..a8d3de52 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "lint:all": "node scripts/lint.js", "format": "prettier --experimental-cli --write .", "typecheck": "npm run typecheck --workspaces --if-present", + "check-i18n": "npm run check-i18n --workspace=packages/cli", "preflight": "npm run clean && npm ci && npm run format && npm run lint:ci && npm run build && npm run typecheck && npm run test:ci", "prepare": "husky && npm run bundle", "prepare:package": "node scripts/prepare-package.js", diff --git a/packages/cli/src/config/settingsSchema.ts b/packages/cli/src/config/settingsSchema.ts index 70037dfd..e1495f21 100644 --- a/packages/cli/src/config/settingsSchema.ts +++ b/packages/cli/src/config/settingsSchema.ts @@ -186,6 +186,23 @@ const SETTINGS_SCHEMA = { description: 'Enable debug logging of keystrokes to the console.', showInDialog: true, }, + language: { + type: 'enum', + label: 'Language', + category: 'General', + requiresRestart: false, + default: 'auto', + description: + 'The language for the user interface. Use "auto" to detect from system settings. ' + + 'You can also use custom language codes (e.g., "es", "fr") by placing JS language files ' + + 'in ~/.qwen/locales/ (e.g., ~/.qwen/locales/es.js).', + showInDialog: true, + options: [ + { value: 'auto', label: 'Auto (detect from system)' }, + { value: 'en', label: 'English' }, + { value: 'zh', label: '中文 (Chinese)' }, + ], + }, }, }, output: { diff --git a/packages/cli/src/core/auth.ts b/packages/cli/src/core/auth.ts index 2284a112..eb2cbd09 100644 --- a/packages/cli/src/core/auth.ts +++ b/packages/cli/src/core/auth.ts @@ -9,6 +9,7 @@ import { type Config, getErrorMessage, } from '@qwen-code/qwen-code-core'; +import { t } from '../i18n/index.js'; /** * Handles the initial authentication flow. @@ -29,7 +30,9 @@ export async function performInitialAuth( // The console.log is intentionally left out here. // We can add a dedicated startup message later if needed. } catch (e) { - return `Failed to login. Message: ${getErrorMessage(e)}`; + return t('Failed to login. Message: {{message}}', { + message: getErrorMessage(e), + }); } return null; diff --git a/packages/cli/src/core/initializer.ts b/packages/cli/src/core/initializer.ts index 039b9277..8b2e685e 100644 --- a/packages/cli/src/core/initializer.ts +++ b/packages/cli/src/core/initializer.ts @@ -14,6 +14,7 @@ import { import { type LoadedSettings } from '../config/settings.js'; import { performInitialAuth } from './auth.js'; import { validateTheme } from './theme.js'; +import { initializeI18n } from '../i18n/index.js'; export interface InitializationResult { authError: string | null; @@ -33,6 +34,13 @@ export async function initializeApp( config: Config, settings: LoadedSettings, ): Promise { + // Initialize i18n system + const languageSetting = + settings.merged.general?.language || + process.env['QWEN_CODE_LANG'] || + 'auto'; + await initializeI18n(languageSetting); + const authError = await performInitialAuth( config, settings.merged.security?.auth?.selectedType, diff --git a/packages/cli/src/i18n/index.ts b/packages/cli/src/i18n/index.ts new file mode 100644 index 00000000..2cad8dec --- /dev/null +++ b/packages/cli/src/i18n/index.ts @@ -0,0 +1,232 @@ +/** + * @license + * Copyright 2025 Qwen + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as fs from 'node:fs'; +import * as path from 'node:path'; +import { fileURLToPath, pathToFileURL } from 'node:url'; +import { homedir } from 'node:os'; + +export type SupportedLanguage = 'en' | 'zh' | string; // Allow custom language codes + +// State +let currentLanguage: SupportedLanguage = 'en'; +let translations: Record = {}; + +// Cache +type TranslationDict = Record; +const translationCache: Record = {}; +const loadingPromises: Record> = {}; + +// Path helpers +const getBuiltinLocalesDir = (): string => { + const __filename = fileURLToPath(import.meta.url); + return path.join(path.dirname(__filename), 'locales'); +}; + +const getUserLocalesDir = (): string => + path.join(homedir(), '.qwen', 'locales'); + +/** + * Get the path to the user's custom locales directory. + * Users can place custom language packs (e.g., es.js, fr.js) in this directory. + * @returns The path to ~/.qwen/locales + */ +export function getUserLocalesDirectory(): string { + return getUserLocalesDir(); +} + +const getLocalePath = ( + lang: SupportedLanguage, + useUserDir: boolean = false, +): string => { + const baseDir = useUserDir ? getUserLocalesDir() : getBuiltinLocalesDir(); + return path.join(baseDir, `${lang}.js`); +}; + +// Language detection +export function detectSystemLanguage(): SupportedLanguage { + const envLang = process.env['QWEN_CODE_LANG'] || process.env['LANG']; + if (envLang?.startsWith('zh')) return 'zh'; + if (envLang?.startsWith('en')) return 'en'; + + try { + const locale = Intl.DateTimeFormat().resolvedOptions().locale; + if (locale.startsWith('zh')) return 'zh'; + } catch { + // Fallback to default + } + + return 'en'; +} + +// Translation loading +async function loadTranslationsAsync( + lang: SupportedLanguage, +): Promise { + if (translationCache[lang]) { + return translationCache[lang]; + } + + const existingPromise = loadingPromises[lang]; + if (existingPromise) { + return existingPromise; + } + + const loadPromise = (async () => { + // Try user directory first (for custom language packs), then builtin directory + const searchDirs = [ + { dir: getUserLocalesDir(), isUser: true }, + { dir: getBuiltinLocalesDir(), isUser: false }, + ]; + + for (const { dir, isUser } of searchDirs) { + // Ensure directory exists + if (!fs.existsSync(dir)) { + continue; + } + + const jsPath = getLocalePath(lang, isUser); + if (!fs.existsSync(jsPath)) { + continue; + } + + try { + // Convert file path to file:// URL for cross-platform compatibility + const fileUrl = pathToFileURL(jsPath).href; + try { + const module = await import(fileUrl); + const result = module.default || module; + if ( + result && + typeof result === 'object' && + Object.keys(result).length > 0 + ) { + translationCache[lang] = result; + return result; + } else { + throw new Error('Module loaded but result is empty or invalid'); + } + } catch { + // For builtin locales, try alternative import method (relative path) + if (!isUser) { + try { + const module = await import(`./locales/${lang}.js`); + const result = module.default || module; + if ( + result && + typeof result === 'object' && + Object.keys(result).length > 0 + ) { + translationCache[lang] = result; + return result; + } + } catch { + // Continue to next directory + } + } + // If import failed, continue to next directory + continue; + } + } catch (error) { + // Log warning but continue to next directory + if (isUser) { + console.warn( + `Failed to load translations from user directory for ${lang}:`, + error, + ); + } else { + console.warn(`Failed to load JS translations for ${lang}:`, error); + if (error instanceof Error) { + console.warn(`Error details: ${error.message}`); + console.warn(`Stack: ${error.stack}`); + } + } + // Continue to next directory + continue; + } + } + + // Return empty object if both directories fail + // Cache it to avoid repeated failed attempts + translationCache[lang] = {}; + return {}; + })(); + + loadingPromises[lang] = loadPromise; + + // Clean up promise after completion to allow retry on next call if needed + loadPromise.finally(() => { + delete loadingPromises[lang]; + }); + + return loadPromise; +} + +function loadTranslations(lang: SupportedLanguage): TranslationDict { + // Only return from cache (JS files require async loading) + return translationCache[lang] || {}; +} + +// String interpolation +function interpolate( + template: string, + params?: Record, +): string { + if (!params) return template; + return template.replace( + /\{\{(\w+)\}\}/g, + (match, key) => params[key] ?? match, + ); +} + +// Language setting helpers +function resolveLanguage(lang: SupportedLanguage | 'auto'): SupportedLanguage { + return lang === 'auto' ? detectSystemLanguage() : lang; +} + +// Public API +export function setLanguage(lang: SupportedLanguage | 'auto'): void { + const resolvedLang = resolveLanguage(lang); + currentLanguage = resolvedLang; + + // Try to load translations synchronously (from cache only) + const loaded = loadTranslations(resolvedLang); + translations = loaded; + + // Warn if translations are empty and JS file exists (requires async loading) + if (Object.keys(loaded).length === 0) { + const userJsPath = getLocalePath(resolvedLang, true); + const builtinJsPath = getLocalePath(resolvedLang, false); + if (fs.existsSync(userJsPath) || fs.existsSync(builtinJsPath)) { + console.warn( + `Language file for ${resolvedLang} requires async loading. ` + + `Use setLanguageAsync() instead, or call initializeI18n() first.`, + ); + } + } +} + +export async function setLanguageAsync( + lang: SupportedLanguage | 'auto', +): Promise { + currentLanguage = resolveLanguage(lang); + translations = await loadTranslationsAsync(currentLanguage); +} + +export function getCurrentLanguage(): SupportedLanguage { + return currentLanguage; +} + +export function t(key: string, params?: Record): string { + const translation = translations[key] ?? key; + return interpolate(translation, params); +} + +export async function initializeI18n( + lang?: SupportedLanguage | 'auto', +): Promise { + await setLanguageAsync(lang ?? 'auto'); +} diff --git a/packages/cli/src/i18n/locales/en.js b/packages/cli/src/i18n/locales/en.js new file mode 100644 index 00000000..75a90f02 --- /dev/null +++ b/packages/cli/src/i18n/locales/en.js @@ -0,0 +1,513 @@ +/** + * @license + * Copyright 2025 Qwen + * SPDX-License-Identifier: Apache-2.0 + */ + +// English translations for Qwen Code CLI +// The key serves as both the translation key and the default English text + +export default { + // ============================================================================ + // Help / UI Components + // ============================================================================ + 'Basics:': 'Basics:', + 'Add context': 'Add context', + 'Use {{symbol}} to specify files for context (e.g., {{example}}) to target specific files or folders.': + 'Use {{symbol}} to specify files for context (e.g., {{example}}) to target specific files or folders.', + '@': '@', + '@src/myFile.ts': '@src/myFile.ts', + 'Shell mode': 'Shell mode', + 'Execute shell commands via {{symbol}} (e.g., {{example1}}) or use natural language (e.g., {{example2}}).': + 'Execute shell commands via {{symbol}} (e.g., {{example1}}) or use natural language (e.g., {{example2}}).', + '!': '!', + '!npm run start': '!npm run start', + 'start server': 'start server', + 'Commands:': 'Commands:', + 'shell command': 'shell command', + 'Model Context Protocol command (from external servers)': + 'Model Context Protocol command (from external servers)', + 'Keyboard Shortcuts:': 'Keyboard Shortcuts:', + 'Jump through words in the input': 'Jump through words in the input', + 'Close dialogs, cancel requests, or quit application': + 'Close dialogs, cancel requests, or quit application', + 'New line': 'New line', + 'New line (Alt+Enter works for certain linux distros)': + 'New line (Alt+Enter works for certain linux distros)', + 'Clear the screen': 'Clear the screen', + 'Open input in external editor': 'Open input in external editor', + 'Send message': 'Send message', + 'Initializing...': 'Initializing...', + 'Connecting to MCP servers... ({{connected}}/{{total}})': + 'Connecting to MCP servers... ({{connected}}/{{total}})', + 'Type your message or @path/to/file': 'Type your message or @path/to/file', + "Press 'i' for INSERT mode and 'Esc' for NORMAL mode.": + "Press 'i' for INSERT mode and 'Esc' for NORMAL mode.", + 'Cancel operation / Clear input (double press)': + 'Cancel operation / Clear input (double press)', + 'Cycle approval modes': 'Cycle approval modes', + 'Cycle through your prompt history': 'Cycle through your prompt history', + 'For a full list of shortcuts, see {{docPath}}': + 'For a full list of shortcuts, see {{docPath}}', + 'docs/keyboard-shortcuts.md': 'docs/keyboard-shortcuts.md', + 'for help on Qwen Code': 'for help on Qwen Code', + 'show version info': 'show version info', + 'submit a bug report': 'submit a bug report', + + // ============================================================================ + // Commands - General + // ============================================================================ + 'Analyzes the project and creates a tailored QWEN.md file.': + 'Analyzes the project and creates a tailored QWEN.md file.', + 'list available Qwen Code tools. Usage: /tools [desc]': + 'list available Qwen Code tools. Usage: /tools [desc]', + 'View or change the approval mode for tool usage': + 'View or change the approval mode for tool usage', + 'View or change the language setting': 'View or change the language setting', + 'change the theme': 'change the theme', + 'clear the screen and conversation history': + 'clear the screen and conversation history', + 'Compresses the context by replacing it with a summary.': + 'Compresses the context by replacing it with a summary.', + 'open full Qwen Code documentation in your browser': + 'open full Qwen Code documentation in your browser', + 'Configuration not available.': 'Configuration not available.', + 'change the auth method': 'change the auth method', + 'Show quit confirmation dialog': 'Show quit confirmation dialog', + 'Copy the last result or code snippet to clipboard': + 'Copy the last result or code snippet to clipboard', + 'Manage subagents for specialized task delegation.': + 'Manage subagents for specialized task delegation.', + 'Manage existing subagents (view, edit, delete).': + 'Manage existing subagents (view, edit, delete).', + 'Create a new subagent with guided setup.': + 'Create a new subagent with guided setup.', + 'View and edit Qwen Code settings': 'View and edit Qwen Code settings', + 'toggle vim mode on/off': 'toggle vim mode on/off', + 'check session stats. Usage: /stats [model|tools]': + 'check session stats. Usage: /stats [model|tools]', + 'Show model-specific usage statistics.': + 'Show model-specific usage statistics.', + 'Show tool-specific usage statistics.': + 'Show tool-specific usage statistics.', + 'exit the cli': 'exit the cli', + 'list configured MCP servers and tools, or authenticate with OAuth-enabled servers': + 'list configured MCP servers and tools, or authenticate with OAuth-enabled servers', + 'Manage workspace directories': 'Manage workspace directories', + 'Add directories to the workspace. Use comma to separate multiple paths': + 'Add directories to the workspace. Use comma to separate multiple paths', + 'Show all directories in the workspace': + 'Show all directories in the workspace', + 'set external editor preference': 'set external editor preference', + 'Manage extensions': 'Manage extensions', + 'List active extensions': 'List active extensions', + 'Update extensions. Usage: update |--all': + 'Update extensions. Usage: update |--all', + 'manage IDE integration': 'manage IDE integration', + 'check status of IDE integration': 'check status of IDE integration', + 'install required IDE companion for {{ideName}}': + 'install required IDE companion for {{ideName}}', + 'enable IDE integration': 'enable IDE integration', + 'disable IDE integration': 'disable IDE integration', + 'Set up GitHub Actions': 'Set up GitHub Actions', + 'Configure terminal keybindings for multiline input (VS Code, Cursor, Windsurf)': + 'Configure terminal keybindings for multiline input (VS Code, Cursor, Windsurf)', + + // ============================================================================ + // Commands - Language + // ============================================================================ + 'Invalid language. Available: en-US, zh-CN': + 'Invalid language. Available: en-US, zh-CN', + 'Language subcommands do not accept additional arguments.': + 'Language subcommands do not accept additional arguments.', + 'Current UI language: {{lang}}': 'Current UI language: {{lang}}', + 'Current LLM output language: {{lang}}': + 'Current LLM output language: {{lang}}', + 'LLM output language not set': 'LLM output language not set', + 'Set UI language': 'Set UI language', + 'Set LLM output language': 'Set LLM output language', + 'Usage: /lang ui [zh-CN|en-US]': 'Usage: /lang ui [zh-CN|en-US]', + 'Usage: /lang output ': 'Usage: /lang output ', + 'Example: /lang output 中文': 'Example: /lang output 中文', + 'Example: /lang output English': 'Example: /lang output English', + 'Example: /lang output 日本語': 'Example: /lang output 日本語', + 'UI language changed to {{lang}}': 'UI language changed to {{lang}}', + 'LLM output language rule file generated at {{path}}': + 'LLM output language rule file generated at {{path}}', + 'Failed to generate LLM output language rule file: {{error}}': + 'Failed to generate LLM output language rule file: {{error}}', + 'Invalid command. Available subcommands:': + 'Invalid command. Available subcommands:', + 'Available subcommands:': 'Available subcommands:', + 'To request additional UI language packs, please open an issue on GitHub.': + 'To request additional UI language packs, please open an issue on GitHub.', + 'Available options:': 'Available options:', + ' - zh-CN: Simplified Chinese': ' - zh-CN: Simplified Chinese', + ' - en-US: English': ' - en-US: English', + 'Set UI language to Simplified Chinese (zh-CN)': + 'Set UI language to Simplified Chinese (zh-CN)', + 'Set UI language to English (en-US)': 'Set UI language to English (en-US)', + + // ============================================================================ + // Commands - Approval Mode + // ============================================================================ + 'Current approval mode: {{mode}}': 'Current approval mode: {{mode}}', + 'Available approval modes:': 'Available approval modes:', + 'Approval mode changed to: {{mode}}': 'Approval mode changed to: {{mode}}', + 'Approval mode changed to: {{mode}} (saved to {{scope}} settings{{location}})': + 'Approval mode changed to: {{mode}} (saved to {{scope}} settings{{location}})', + 'Usage: /approval-mode [--session|--user|--project]': + 'Usage: /approval-mode [--session|--user|--project]', + 'Invalid approval mode: {{mode}}': 'Invalid approval mode: {{mode}}', + 'Multiple scope flags provided': 'Multiple scope flags provided', + 'Invalid arguments provided': 'Invalid arguments provided', + 'Missing approval mode': 'Missing approval mode', + 'Scope subcommands do not accept additional arguments.': + 'Scope subcommands do not accept additional arguments.', + 'Plan mode - Analyze only, do not modify files or execute commands': + 'Plan mode - Analyze only, do not modify files or execute commands', + 'Default mode - Require approval for file edits or shell commands': + 'Default mode - Require approval for file edits or shell commands', + 'Auto-edit mode - Automatically approve file edits': + 'Auto-edit mode - Automatically approve file edits', + 'YOLO mode - Automatically approve all tools': + 'YOLO mode - Automatically approve all tools', + '{{mode}} mode': '{{mode}} mode', + 'Settings service is not available; unable to persist the approval mode.': + 'Settings service is not available; unable to persist the approval mode.', + 'Failed to save approval mode: {{error}}': + 'Failed to save approval mode: {{error}}', + 'Failed to change approval mode: {{error}}': + 'Failed to change approval mode: {{error}}', + 'Apply to current session only (temporary)': + 'Apply to current session only (temporary)', + 'Persist for this project/workspace': 'Persist for this project/workspace', + 'Persist for this user on this machine': + 'Persist for this user on this machine', + + // ============================================================================ + // Commands - Memory + // ============================================================================ + 'Commands for interacting with memory.': + 'Commands for interacting with memory.', + 'Show the current memory contents.': 'Show the current memory contents.', + 'Show project-level memory contents.': 'Show project-level memory contents.', + 'Show global memory contents.': 'Show global memory contents.', + 'Add content to project-level memory.': + 'Add content to project-level memory.', + 'Add content to global memory.': 'Add content to global memory.', + 'Refresh the memory from the source.': 'Refresh the memory from the source.', + 'Usage: /memory add --project ': + 'Usage: /memory add --project ', + 'Usage: /memory add --global ': + 'Usage: /memory add --global ', + 'Attempting to save to project memory: "{{text}}"': + 'Attempting to save to project memory: "{{text}}"', + 'Attempting to save to global memory: "{{text}}"': + 'Attempting to save to global memory: "{{text}}"', + 'Current memory content from {{count}} file(s):': + 'Current memory content from {{count}} file(s):', + 'Memory is currently empty.': 'Memory is currently empty.', + 'Project memory file not found or is currently empty.': + 'Project memory file not found or is currently empty.', + 'Global memory file not found or is currently empty.': + 'Global memory file not found or is currently empty.', + 'Global memory is currently empty.': 'Global memory is currently empty.', + 'Global memory content:\n\n---\n{{content}}\n---': + 'Global memory content:\n\n---\n{{content}}\n---', + 'Project memory content from {{path}}:\n\n---\n{{content}}\n---': + 'Project memory content from {{path}}:\n\n---\n{{content}}\n---', + 'Project memory is currently empty.': 'Project memory is currently empty.', + 'Refreshing memory from source files...': + 'Refreshing memory from source files...', + + // ============================================================================ + // Commands - MCP + // ============================================================================ + 'Authenticate with an OAuth-enabled MCP server': + 'Authenticate with an OAuth-enabled MCP server', + 'List configured MCP servers and tools': + 'List configured MCP servers and tools', + 'Restarts MCP servers.': 'Restarts MCP servers.', + 'Config not loaded.': 'Config not loaded.', + 'Could not retrieve tool registry.': 'Could not retrieve tool registry.', + 'No MCP servers configured with OAuth authentication.': + 'No MCP servers configured with OAuth authentication.', + 'MCP servers with OAuth authentication:': + 'MCP servers with OAuth authentication:', + 'Use /mcp auth to authenticate.': + 'Use /mcp auth to authenticate.', + "MCP server '{{name}}' not found.": "MCP server '{{name}}' not found.", + "Successfully authenticated and refreshed tools for '{{name}}'.": + "Successfully authenticated and refreshed tools for '{{name}}'.", + "Failed to authenticate with MCP server '{{name}}': {{error}}": + "Failed to authenticate with MCP server '{{name}}': {{error}}", + "Re-discovering tools from '{{name}}'...": + "Re-discovering tools from '{{name}}'...", + + // ============================================================================ + // Commands - Chat + // ============================================================================ + 'Manage conversation history.': 'Manage conversation history.', + 'List saved conversation checkpoints': 'List saved conversation checkpoints', + 'No saved conversation checkpoints found.': + 'No saved conversation checkpoints found.', + 'List of saved conversations:': 'List of saved conversations:', + 'Note: Newest last, oldest first': 'Note: Newest last, oldest first', + 'Save the current conversation as a checkpoint. Usage: /chat save ': + 'Save the current conversation as a checkpoint. Usage: /chat save ', + 'Missing tag. Usage: /chat save ': + 'Missing tag. Usage: /chat save ', + 'Delete a conversation checkpoint. Usage: /chat delete ': + 'Delete a conversation checkpoint. Usage: /chat delete ', + 'Missing tag. Usage: /chat delete ': + 'Missing tag. Usage: /chat delete ', + "Conversation checkpoint '{{tag}}' has been deleted.": + "Conversation checkpoint '{{tag}}' has been deleted.", + "Error: No checkpoint found with tag '{{tag}}'.": + "Error: No checkpoint found with tag '{{tag}}'.", + 'Resume a conversation from a checkpoint. Usage: /chat resume ': + 'Resume a conversation from a checkpoint. Usage: /chat resume ', + 'Missing tag. Usage: /chat resume ': + 'Missing tag. Usage: /chat resume ', + 'No saved checkpoint found with tag: {{tag}}.': + 'No saved checkpoint found with tag: {{tag}}.', + 'A checkpoint with the tag {{tag}} already exists. Do you want to overwrite it?': + 'A checkpoint with the tag {{tag}} already exists. Do you want to overwrite it?', + 'No chat client available to save conversation.': + 'No chat client available to save conversation.', + 'Conversation checkpoint saved with tag: {{tag}}.': + 'Conversation checkpoint saved with tag: {{tag}}.', + 'No conversation found to save.': 'No conversation found to save.', + 'No chat client available to share conversation.': + 'No chat client available to share conversation.', + 'Invalid file format. Only .md and .json are supported.': + 'Invalid file format. Only .md and .json are supported.', + 'Error sharing conversation: {{error}}': + 'Error sharing conversation: {{error}}', + 'Conversation shared to {{filePath}}': 'Conversation shared to {{filePath}}', + 'No conversation found to share.': 'No conversation found to share.', + + // ============================================================================ + // Commands - Summary + // ============================================================================ + 'Generate a project summary and save it to .qwen/PROJECT_SUMMARY.md': + 'Generate a project summary and save it to .qwen/PROJECT_SUMMARY.md', + 'No chat client available to generate summary.': + 'No chat client available to generate summary.', + 'Already generating summary, wait for previous request to complete': + 'Already generating summary, wait for previous request to complete', + 'No conversation found to summarize.': 'No conversation found to summarize.', + 'Failed to generate project context summary: {{error}}': + 'Failed to generate project context summary: {{error}}', + + // ============================================================================ + // Commands - Model + // ============================================================================ + 'Switch the model for this session': 'Switch the model for this session', + 'Content generator configuration not available.': + 'Content generator configuration not available.', + 'Authentication type not available.': 'Authentication type not available.', + 'No models available for the current authentication type ({{authType}}).': + 'No models available for the current authentication type ({{authType}}).', + + // ============================================================================ + // Commands - Clear + // ============================================================================ + 'Clearing terminal and resetting chat.': + 'Clearing terminal and resetting chat.', + 'Clearing terminal.': 'Clearing terminal.', + + // ============================================================================ + // Commands - Compress + // ============================================================================ + 'Already compressing, wait for previous request to complete': + 'Already compressing, wait for previous request to complete', + 'Failed to compress chat history.': 'Failed to compress chat history.', + 'Failed to compress chat history: {{error}}': + 'Failed to compress chat history: {{error}}', + + // ============================================================================ + // Commands - Docs + // ============================================================================ + 'Please open the following URL in your browser to view the documentation:\n{{url}}': + 'Please open the following URL in your browser to view the documentation:\n{{url}}', + 'Opening documentation in your browser: {{url}}': + 'Opening documentation in your browser: {{url}}', + + // ============================================================================ + // Dialogs - Tool Confirmation + // ============================================================================ + 'Do you want to proceed?': 'Do you want to proceed?', + 'Yes, allow once': 'Yes, allow once', + 'Allow always': 'Allow always', + No: 'No', + 'No (esc)': 'No (esc)', + 'Yes, allow always for this session': 'Yes, allow always for this session', + + // ============================================================================ + // Dialogs - Shell Confirmation + // ============================================================================ + 'Shell Command Execution': 'Shell Command Execution', + 'A custom command wants to run the following shell commands:': + 'A custom command wants to run the following shell commands:', + + // ============================================================================ + // Dialogs - Quit Confirmation + // ============================================================================ + 'What would you like to do before exiting?': + 'What would you like to do before exiting?', + 'Quit immediately (/quit)': 'Quit immediately (/quit)', + 'Generate summary and quit (/summary)': + 'Generate summary and quit (/summary)', + 'Save conversation and quit (/chat save)': + 'Save conversation and quit (/chat save)', + 'Cancel (stay in application)': 'Cancel (stay in application)', + + // ============================================================================ + // Dialogs - Pro Quota + // ============================================================================ + 'Pro quota limit reached for {{model}}.': + 'Pro quota limit reached for {{model}}.', + 'Change auth (executes the /auth command)': + 'Change auth (executes the /auth command)', + 'Continue with {{model}}': 'Continue with {{model}}', + + // ============================================================================ + // Dialogs - Welcome Back + // ============================================================================ + 'Current Plan:': 'Current Plan:', + 'Progress: {{done}}/{{total}} tasks completed': + 'Progress: {{done}}/{{total}} tasks completed', + ', {{inProgress}} in progress': ', {{inProgress}} in progress', + 'Pending Tasks:': 'Pending Tasks:', + 'What would you like to do?': 'What would you like to do?', + 'Choose how to proceed with your session:': + 'Choose how to proceed with your session:', + 'Start new chat session': 'Start new chat session', + 'Continue previous conversation': 'Continue previous conversation', + '👋 Welcome back! (Last updated: {{timeAgo}})': + '👋 Welcome back! (Last updated: {{timeAgo}})', + '🎯 Overall Goal:': '🎯 Overall Goal:', + + // ============================================================================ + // Dialogs - Auth + // ============================================================================ + 'Get started': 'Get started', + 'How would you like to authenticate for this project?': + 'How would you like to authenticate for this project?', + 'OpenAI API key is required to use OpenAI authentication.': + 'OpenAI API key is required to use OpenAI authentication.', + 'You must select an auth method to proceed. Press Ctrl+C again to exit.': + 'You must select an auth method to proceed. Press Ctrl+C again to exit.', + '(Use Enter to Set Auth)': '(Use Enter to Set Auth)', + 'Terms of Services and Privacy Notice for Qwen Code': + 'Terms of Services and Privacy Notice for Qwen Code', + 'Qwen OAuth': 'Qwen OAuth', + OpenAI: 'OpenAI', + 'Failed to login. Message: {{message}}': + 'Failed to login. Message: {{message}}', + 'Authentication is enforced to be {{enforcedType}}, but you are currently using {{currentType}}.': + 'Authentication is enforced to be {{enforcedType}}, but you are currently using {{currentType}}.', + 'Qwen OAuth authentication timed out. Please try again.': + 'Qwen OAuth authentication timed out. Please try again.', + 'Qwen OAuth authentication cancelled.': + 'Qwen OAuth authentication cancelled.', + 'Qwen OAuth Authentication': 'Qwen OAuth Authentication', + 'Please visit this URL to authorize:': 'Please visit this URL to authorize:', + 'Or scan the QR code below:': 'Or scan the QR code below:', + 'Waiting for authorization': 'Waiting for authorization', + 'Time remaining:': 'Time remaining:', + '(Press ESC or CTRL+C to cancel)': '(Press ESC or CTRL+C to cancel)', + 'Qwen OAuth Authentication Timeout': 'Qwen OAuth Authentication Timeout', + 'OAuth token expired (over {{seconds}} seconds). Please select authentication method again.': + 'OAuth token expired (over {{seconds}} seconds). Please select authentication method again.', + 'Press any key to return to authentication type selection.': + 'Press any key to return to authentication type selection.', + 'Waiting for Qwen OAuth authentication...': + 'Waiting for Qwen OAuth authentication...', + + // ============================================================================ + // Dialogs - Permissions + // ============================================================================ + 'Manage folder trust settings': 'Manage folder trust settings', + + // ============================================================================ + // Status Bar + // ============================================================================ + 'Using:': 'Using:', + '{{count}} open file': '{{count}} open file', + '{{count}} open files': '{{count}} open files', + '(ctrl+g to view)': '(ctrl+g to view)', + '{{count}} {{name}} file': '{{count}} {{name}} file', + '{{count}} {{name}} files': '{{count}} {{name}} files', + '{{count}} MCP server': '{{count}} MCP server', + '{{count}} MCP servers': '{{count}} MCP servers', + '{{count}} Blocked': '{{count}} Blocked', + '(ctrl+t to view)': '(ctrl+t to view)', + '(ctrl+t to toggle)': '(ctrl+t to toggle)', + 'Press Ctrl+C again to exit.': 'Press Ctrl+C again to exit.', + 'Press Ctrl+D again to exit.': 'Press Ctrl+D again to exit.', + 'Press Esc again to clear.': 'Press Esc again to clear.', + + // ============================================================================ + // MCP Status + // ============================================================================ + 'No MCP servers configured.': 'No MCP servers configured.', + 'Please view MCP documentation in your browser:': + 'Please view MCP documentation in your browser:', + 'or use the cli /docs command': 'or use the cli /docs command', + '⏳ MCP servers are starting up ({{count}} initializing)...': + '⏳ MCP servers are starting up ({{count}} initializing)...', + 'Note: First startup may take longer. Tool availability will update automatically.': + 'Note: First startup may take longer. Tool availability will update automatically.', + 'Configured MCP servers:': 'Configured MCP servers:', + Ready: 'Ready', + 'Starting... (first startup may take longer)': + 'Starting... (first startup may take longer)', + Disconnected: 'Disconnected', + '{{count}} tool': '{{count}} tool', + '{{count}} tools': '{{count}} tools', + '{{count}} prompt': '{{count}} prompt', + '{{count}} prompts': '{{count}} prompts', + '(from {{extensionName}})': '(from {{extensionName}})', + + // ============================================================================ + // Startup Tips + // ============================================================================ + 'Tips for getting started:': 'Tips for getting started:', + '1. Ask questions, edit files, or run commands.': + '1. Ask questions, edit files, or run commands.', + '2. Be specific for the best results.': + '2. Be specific for the best results.', + 'files to customize your interactions with Qwen Code.': + 'files to customize your interactions with Qwen Code.', + 'for more information.': 'for more information.', + + // ============================================================================ + // Exit Screen / Stats + // ============================================================================ + 'Agent powering down. Goodbye!': 'Agent powering down. Goodbye!', + 'Interaction Summary': 'Interaction Summary', + 'Session ID:': 'Session ID:', + 'Tool Calls:': 'Tool Calls:', + 'Success Rate:': 'Success Rate:', + 'User Agreement:': 'User Agreement:', + reviewed: 'reviewed', + 'Code Changes:': 'Code Changes:', + Performance: 'Performance', + 'Wall Time:': 'Wall Time:', + 'Agent Active:': 'Agent Active:', + 'API Time:': 'API Time:', + 'Tool Time:': 'Tool Time:', + 'Session Stats': 'Session Stats', + 'Model Usage': 'Model Usage', + Reqs: 'Reqs', + 'Input Tokens': 'Input Tokens', + 'Output Tokens': 'Output Tokens', + 'Savings Highlight:': 'Savings Highlight:', + 'of input tokens were served from the cache, reducing costs.': + 'of input tokens were served from the cache, reducing costs.', + 'Tip: For a full token breakdown, run `/stats model`.': + 'Tip: For a full token breakdown, run `/stats model`.', +}; diff --git a/packages/cli/src/i18n/locales/zh.js b/packages/cli/src/i18n/locales/zh.js new file mode 100644 index 00000000..2c976ef2 --- /dev/null +++ b/packages/cli/src/i18n/locales/zh.js @@ -0,0 +1,479 @@ +/** + * @license + * Copyright 2025 Qwen + * SPDX-License-Identifier: Apache-2.0 + */ + +// Chinese translations for Qwen Code CLI + +export default { + // ============================================================================ + // Help / UI Components + // ============================================================================ + 'Basics:': '基础功能:', + 'Add context': '添加上下文', + 'Use {{symbol}} to specify files for context (e.g., {{example}}) to target specific files or folders.': + '使用 {{symbol}} 指定文件作为上下文(例如,{{example}}),用于定位特定文件或文件夹', + '@': '@', + '@src/myFile.ts': '@src/myFile.ts', + 'Shell mode': 'Shell 模式', + 'Execute shell commands via {{symbol}} (e.g., {{example1}}) or use natural language (e.g., {{example2}}).': + '通过 {{symbol}} 执行 shell 命令(例如,{{example1}})或使用自然语言(例如,{{example2}})', + '!': '!', + '!npm run start': '!npm run start', + 'start server': 'start server', + 'Commands:': '命令:', + 'shell command': 'shell 命令', + 'Model Context Protocol command (from external servers)': + '模型上下文协议命令(来自外部服务器)', + 'Keyboard Shortcuts:': '键盘快捷键:', + 'Jump through words in the input': '在输入中按单词跳转', + 'Close dialogs, cancel requests, or quit application': + '关闭对话框、取消请求或退出应用程序', + 'New line': '换行', + 'New line (Alt+Enter works for certain linux distros)': + '换行(某些 Linux 发行版支持 Alt+Enter)', + 'Clear the screen': '清屏', + 'Open input in external editor': '在外部编辑器中打开输入', + 'Send message': '发送消息', + 'Initializing...': '正在初始化...', + 'Connecting to MCP servers... ({{connected}}/{{total}})': + '正在连接到 MCP 服务器... ({{connected}}/{{total}})', + 'Type your message or @path/to/file': '输入您的消息或 @ 文件路径', + "Press 'i' for INSERT mode and 'Esc' for NORMAL mode.": + "按 'i' 进入插入模式,按 'Esc' 进入普通模式", + 'Cancel operation / Clear input (double press)': + '取消操作 / 清空输入(双击)', + 'Cycle approval modes': '循环切换审批模式', + 'Cycle through your prompt history': '循环浏览提示历史', + 'For a full list of shortcuts, see {{docPath}}': + '完整快捷键列表,请参阅 {{docPath}}', + 'docs/keyboard-shortcuts.md': 'docs/keyboard-shortcuts.md', + 'for help on Qwen Code': '获取 Qwen Code 帮助', + 'show version info': '显示版本信息', + 'submit a bug report': '提交错误报告', + + // ============================================================================ + // Commands - General + // ============================================================================ + 'Analyzes the project and creates a tailored QWEN.md file.': + '分析项目并创建定制的 QWEN.md 文件', + 'list available Qwen Code tools. Usage: /tools [desc]': + '列出可用的 Qwen Code 工具。用法:/tools [desc]', + 'View or change the approval mode for tool usage': + '查看或更改工具使用的审批模式', + 'View or change the language setting': '查看或更改语言设置', + 'change the theme': '更改主题', + 'clear the screen and conversation history': '清屏并清除对话历史', + 'Compresses the context by replacing it with a summary.': + '通过用摘要替换来压缩上下文', + 'open full Qwen Code documentation in your browser': + '在浏览器中打开完整的 Qwen Code 文档', + 'Configuration not available.': '配置不可用', + 'change the auth method': '更改认证方法', + 'Show quit confirmation dialog': '显示退出确认对话框', + 'Copy the last result or code snippet to clipboard': + '将最后的结果或代码片段复制到剪贴板', + 'Manage subagents for specialized task delegation.': + '管理用于专门任务委派的子代理', + 'Manage existing subagents (view, edit, delete).': + '管理现有子代理(查看、编辑、删除)', + 'Create a new subagent with guided setup.': '通过引导式设置创建新的子代理', + 'View and edit Qwen Code settings': '查看和编辑 Qwen Code 设置', + 'toggle vim mode on/off': '切换 vim 模式开关', + 'check session stats. Usage: /stats [model|tools]': + '检查会话统计信息。用法:/stats [model|tools]', + 'Show model-specific usage statistics.': '显示模型相关的使用统计信息', + 'Show tool-specific usage statistics.': '显示工具相关的使用统计信息', + 'exit the cli': '退出命令行界面', + 'list configured MCP servers and tools, or authenticate with OAuth-enabled servers': + '列出已配置的 MCP 服务器和工具,或使用支持 OAuth 的服务器进行身份验证', + 'Manage workspace directories': '管理工作区目录', + 'Add directories to the workspace. Use comma to separate multiple paths': + '将目录添加到工作区。使用逗号分隔多个路径', + 'Show all directories in the workspace': '显示工作区中的所有目录', + 'set external editor preference': '设置外部编辑器首选项', + 'Manage extensions': '管理扩展', + 'List active extensions': '列出活动扩展', + 'Update extensions. Usage: update |--all': + '更新扩展。用法:update |--all', + 'manage IDE integration': '管理 IDE 集成', + 'check status of IDE integration': '检查 IDE 集成状态', + 'install required IDE companion for {{ideName}}': + '安装 {{ideName}} 所需的 IDE 配套工具', + 'enable IDE integration': '启用 IDE 集成', + 'disable IDE integration': '禁用 IDE 集成', + 'Set up GitHub Actions': '设置 GitHub Actions', + 'Configure terminal keybindings for multiline input (VS Code, Cursor, Windsurf)': + '配置终端按键绑定以支持多行输入(VS Code、Cursor、Windsurf)', + + // ============================================================================ + // Commands - Language + // ============================================================================ + 'Invalid language. Available: en-US, zh-CN': + '无效的语言。可用选项:en-US, zh-CN', + 'Language subcommands do not accept additional arguments.': + '语言子命令不接受额外参数', + 'Current UI language: {{lang}}': '当前 UI 语言:{{lang}}', + 'Current LLM output language: {{lang}}': '当前 LLM 输出语言:{{lang}}', + 'LLM output language not set': '未设置 LLM 输出语言', + 'Set UI language': '设置 UI 语言', + 'Set LLM output language': '设置 LLM 输出语言', + 'Usage: /lang ui [zh-CN|en-US]': '用法:/lang ui [zh-CN|en-US]', + 'Usage: /lang output ': '用法:/lang output <语言>', + 'Example: /lang output 中文': '示例:/lang output 中文', + 'Example: /lang output English': '示例:/lang output English', + 'Example: /lang output 日本語': '示例:/lang output 日本語', + 'UI language changed to {{lang}}': 'UI 语言已更改为 {{lang}}', + 'LLM output language rule file generated at {{path}}': + 'LLM 输出语言规则文件已生成于 {{path}}', + 'Failed to generate LLM output language rule file: {{error}}': + '生成 LLM 输出语言规则文件失败:{{error}}', + 'Invalid command. Available subcommands:': '无效的命令。可用的子命令:', + 'Available subcommands:': '可用的子命令:', + 'To request additional UI language packs, please open an issue on GitHub.': + '如需请求其他 UI 语言包,请在 GitHub 上提交 issue', + 'Available options:': '可用选项:', + ' - zh-CN: Simplified Chinese': ' - zh-CN: 简体中文', + ' - en-US: English': ' - en-US: English', + 'Set UI language to Simplified Chinese (zh-CN)': + '将 UI 语言设置为简体中文 (zh-CN)', + 'Set UI language to English (en-US)': '将 UI 语言设置为英语 (en-US)', + + // ============================================================================ + // Commands - Approval Mode + // ============================================================================ + 'Current approval mode: {{mode}}': '当前审批模式:{{mode}}', + 'Available approval modes:': '可用的审批模式:', + 'Approval mode changed to: {{mode}}': '审批模式已更改为:{{mode}}', + 'Approval mode changed to: {{mode}} (saved to {{scope}} settings{{location}})': + '审批模式已更改为:{{mode}}(已保存到{{scope}}设置{{location}})', + 'Usage: /approval-mode [--session|--user|--project]': + '用法:/approval-mode [--session|--user|--project]', + 'Invalid approval mode: {{mode}}': '无效的审批模式:{{mode}}', + 'Multiple scope flags provided': '提供了多个作用域标志', + 'Invalid arguments provided': '提供了无效的参数', + 'Missing approval mode': '缺少审批模式', + 'Scope subcommands do not accept additional arguments.': + '作用域子命令不接受额外参数', + 'Plan mode - Analyze only, do not modify files or execute commands': + '计划模式 - 仅分析,不修改文件或执行命令', + 'Default mode - Require approval for file edits or shell commands': + '默认模式 - 需要批准文件编辑或 shell 命令', + 'Auto-edit mode - Automatically approve file edits': + '自动编辑模式 - 自动批准文件编辑', + 'YOLO mode - Automatically approve all tools': 'YOLO 模式 - 自动批准所有工具', + '{{mode}} mode': '{{mode}} 模式', + 'Settings service is not available; unable to persist the approval mode.': + '设置服务不可用;无法持久化审批模式。', + 'Failed to save approval mode: {{error}}': '保存审批模式失败:{{error}}', + 'Failed to change approval mode: {{error}}': '更改审批模式失败:{{error}}', + 'Apply to current session only (temporary)': '仅应用于当前会话(临时)', + 'Persist for this project/workspace': '持久化到此项目/工作区', + 'Persist for this user on this machine': '持久化到此机器上的此用户', + + // ============================================================================ + // Commands - Memory + // ============================================================================ + 'Commands for interacting with memory.': '用于与记忆交互的命令', + 'Show the current memory contents.': '显示当前记忆内容', + 'Show project-level memory contents.': '显示项目级记忆内容', + 'Show global memory contents.': '显示全局记忆内容', + 'Add content to project-level memory.': '添加内容到项目级记忆', + 'Add content to global memory.': '添加内容到全局记忆', + 'Refresh the memory from the source.': '从源刷新记忆', + 'Usage: /memory add --project ': + '用法:/memory add --project <要记住的文本>', + 'Usage: /memory add --global ': + '用法:/memory add --global <要记住的文本>', + 'Attempting to save to project memory: "{{text}}"': + '正在尝试保存到项目记忆:"{{text}}"', + 'Attempting to save to global memory: "{{text}}"': + '正在尝试保存到全局记忆:"{{text}}"', + 'Current memory content from {{count}} file(s):': + '来自 {{count}} 个文件的当前记忆内容:', + 'Memory is currently empty.': '记忆当前为空', + 'Project memory file not found or is currently empty.': + '项目记忆文件未找到或当前为空', + 'Global memory file not found or is currently empty.': + '全局记忆文件未找到或当前为空', + 'Global memory is currently empty.': '全局记忆当前为空', + 'Global memory content:\n\n---\n{{content}}\n---': + '全局记忆内容:\n\n---\n{{content}}\n---', + 'Project memory content from {{path}}:\n\n---\n{{content}}\n---': + '项目记忆内容来自 {{path}}:\n\n---\n{{content}}\n---', + 'Project memory is currently empty.': '项目记忆当前为空', + 'Refreshing memory from source files...': '正在从源文件刷新记忆...', + + // ============================================================================ + // Commands - MCP + // ============================================================================ + 'Authenticate with an OAuth-enabled MCP server': + '使用支持 OAuth 的 MCP 服务器进行认证', + 'List configured MCP servers and tools': '列出已配置的 MCP 服务器和工具', + 'Restarts MCP servers.': '重启 MCP 服务器', + 'Config not loaded.': '配置未加载', + 'Could not retrieve tool registry.': '无法检索工具注册表', + 'No MCP servers configured with OAuth authentication.': + '未配置支持 OAuth 认证的 MCP 服务器', + 'MCP servers with OAuth authentication:': '支持 OAuth 认证的 MCP 服务器:', + 'Use /mcp auth to authenticate.': + '使用 /mcp auth 进行认证', + "MCP server '{{name}}' not found.": "未找到 MCP 服务器 '{{name}}'", + "Successfully authenticated and refreshed tools for '{{name}}'.": + "成功认证并刷新了 '{{name}}' 的工具", + "Failed to authenticate with MCP server '{{name}}': {{error}}": + "认证 MCP 服务器 '{{name}}' 失败:{{error}}", + "Re-discovering tools from '{{name}}'...": + "正在重新发现 '{{name}}' 的工具...", + + // ============================================================================ + // Commands - Chat + // ============================================================================ + 'Manage conversation history.': '管理对话历史', + 'List saved conversation checkpoints': '列出已保存的对话检查点', + 'No saved conversation checkpoints found.': '未找到已保存的对话检查点', + 'List of saved conversations:': '已保存的对话列表:', + 'Note: Newest last, oldest first': '注意:最新的在最后,最旧的在最前', + 'Save the current conversation as a checkpoint. Usage: /chat save ': + '将当前对话保存为检查点。用法:/chat save ', + 'Missing tag. Usage: /chat save ': '缺少标签。用法:/chat save ', + 'Delete a conversation checkpoint. Usage: /chat delete ': + '删除对话检查点。用法:/chat delete ', + 'Missing tag. Usage: /chat delete ': + '缺少标签。用法:/chat delete ', + "Conversation checkpoint '{{tag}}' has been deleted.": + "对话检查点 '{{tag}}' 已删除", + "Error: No checkpoint found with tag '{{tag}}'.": + "错误:未找到标签为 '{{tag}}' 的检查点", + 'Resume a conversation from a checkpoint. Usage: /chat resume ': + '从检查点恢复对话。用法:/chat resume ', + 'Missing tag. Usage: /chat resume ': + '缺少标签。用法:/chat resume ', + 'No saved checkpoint found with tag: {{tag}}.': + '未找到标签为 {{tag}} 的已保存检查点', + 'A checkpoint with the tag {{tag}} already exists. Do you want to overwrite it?': + '标签为 {{tag}} 的检查点已存在。您要覆盖它吗?', + 'No chat client available to save conversation.': + '没有可用的聊天客户端来保存对话', + 'Conversation checkpoint saved with tag: {{tag}}.': + '对话检查点已保存,标签:{{tag}}', + 'No conversation found to save.': '未找到要保存的对话', + 'No chat client available to share conversation.': + '没有可用的聊天客户端来分享对话', + 'Invalid file format. Only .md and .json are supported.': + '无效的文件格式。仅支持 .md 和 .json 文件', + 'Error sharing conversation: {{error}}': '分享对话时出错:{{error}}', + 'Conversation shared to {{filePath}}': '对话已分享到 {{filePath}}', + 'No conversation found to share.': '未找到要分享的对话', + + // ============================================================================ + // Commands - Summary + // ============================================================================ + 'Generate a project summary and save it to .qwen/PROJECT_SUMMARY.md': + '生成项目摘要并保存到 .qwen/PROJECT_SUMMARY.md', + 'No chat client available to generate summary.': + '没有可用的聊天客户端来生成摘要', + 'Already generating summary, wait for previous request to complete': + '正在生成摘要,请等待上一个请求完成', + 'No conversation found to summarize.': '未找到要总结的对话', + 'Failed to generate project context summary: {{error}}': + '生成项目上下文摘要失败:{{error}}', + + // ============================================================================ + // Commands - Model + // ============================================================================ + 'Switch the model for this session': '切换此会话的模型', + 'Content generator configuration not available.': '内容生成器配置不可用', + 'Authentication type not available.': '认证类型不可用', + 'No models available for the current authentication type ({{authType}}).': + '当前认证类型 ({{authType}}) 没有可用的模型', + + // ============================================================================ + // Commands - Clear + // ============================================================================ + 'Clearing terminal and resetting chat.': '正在清屏并重置聊天', + 'Clearing terminal.': '正在清屏', + + // ============================================================================ + // Commands - Compress + // ============================================================================ + 'Already compressing, wait for previous request to complete': + '正在压缩中,请等待上一个请求完成', + 'Failed to compress chat history.': '压缩聊天历史失败', + 'Failed to compress chat history: {{error}}': '压缩聊天历史失败:{{error}}', + + // ============================================================================ + // Commands - Docs + // ============================================================================ + 'Please open the following URL in your browser to view the documentation:\n{{url}}': + '请在浏览器中打开以下 URL 以查看文档:\n{{url}}', + 'Opening documentation in your browser: {{url}}': + '正在浏览器中打开文档:{{url}}', + + // ============================================================================ + // Dialogs - Tool Confirmation + // ============================================================================ + 'Do you want to proceed?': '是否继续?', + 'Yes, allow once': '是,允许一次', + 'Allow always': '总是允许', + No: '否', + 'No (esc)': '否 (esc)', + 'Yes, allow always for this session': '是,本次会话总是允许', + + // ============================================================================ + // Dialogs - Shell Confirmation + // ============================================================================ + 'Shell Command Execution': 'Shell 命令执行', + 'A custom command wants to run the following shell commands:': + '自定义命令想要运行以下 shell 命令:', + + // ============================================================================ + // Dialogs - Quit Confirmation + // ============================================================================ + 'What would you like to do before exiting?': '退出前您想要做什么?', + 'Quit immediately (/quit)': '立即退出 (/quit)', + 'Generate summary and quit (/summary)': '生成摘要并退出 (/summary)', + 'Save conversation and quit (/chat save)': '保存对话并退出 (/chat save)', + 'Cancel (stay in application)': '取消(留在应用程序中)', + + // ============================================================================ + // Dialogs - Pro Quota + // ============================================================================ + 'Pro quota limit reached for {{model}}.': '{{model}} 的 Pro 配额已达到上限', + 'Change auth (executes the /auth command)': '更改认证(执行 /auth 命令)', + 'Continue with {{model}}': '使用 {{model}} 继续', + + // ============================================================================ + // Dialogs - Welcome Back + // ============================================================================ + 'Current Plan:': '当前计划:', + 'Progress: {{done}}/{{total}} tasks completed': + '进度:已完成 {{done}}/{{total}} 个任务', + ', {{inProgress}} in progress': ',{{inProgress}} 个进行中', + 'Pending Tasks:': '待处理任务:', + 'What would you like to do?': '您想要做什么?', + 'Choose how to proceed with your session:': '选择如何继续您的会话:', + 'Start new chat session': '开始新的聊天会话', + 'Continue previous conversation': '继续之前的对话', + '👋 Welcome back! (Last updated: {{timeAgo}})': + '👋 欢迎回来!(最后更新:{{timeAgo}})', + '🎯 Overall Goal:': '🎯 总体目标:', + + // ============================================================================ + // Dialogs - Auth + // ============================================================================ + 'Get started': '开始使用', + 'How would you like to authenticate for this project?': + '您想如何为此项目进行认证?', + 'OpenAI API key is required to use OpenAI authentication.': + '使用 OpenAI 认证需要 OpenAI API 密钥', + 'You must select an auth method to proceed. Press Ctrl+C again to exit.': + '您必须选择认证方法才能继续。再次按 Ctrl+C 退出', + '(Use Enter to Set Auth)': '(使用 Enter 设置认证)', + 'Terms of Services and Privacy Notice for Qwen Code': + 'Qwen Code 的服务条款和隐私声明', + 'Qwen OAuth': 'Qwen OAuth', + OpenAI: 'OpenAI', + 'Failed to login. Message: {{message}}': '登录失败。消息:{{message}}', + 'Authentication is enforced to be {{enforcedType}}, but you are currently using {{currentType}}.': + '认证方式被强制设置为 {{enforcedType}},但您当前使用的是 {{currentType}}', + 'Qwen OAuth authentication timed out. Please try again.': + 'Qwen OAuth 认证超时。请重试', + 'Qwen OAuth authentication cancelled.': 'Qwen OAuth 认证已取消', + 'Qwen OAuth Authentication': 'Qwen OAuth 认证', + 'Please visit this URL to authorize:': '请访问此 URL 进行授权:', + 'Or scan the QR code below:': '或扫描下方的二维码:', + 'Waiting for authorization': '等待授权中', + 'Time remaining:': '剩余时间:', + '(Press ESC or CTRL+C to cancel)': '(按 ESC 或 CTRL+C 取消)', + 'Qwen OAuth Authentication Timeout': 'Qwen OAuth 认证超时', + 'OAuth token expired (over {{seconds}} seconds). Please select authentication method again.': + 'OAuth 令牌已过期(超过 {{seconds}} 秒)。请重新选择认证方法', + 'Press any key to return to authentication type selection.': + '按任意键返回认证类型选择', + 'Waiting for Qwen OAuth authentication...': '正在等待 Qwen OAuth 认证...', + + // ============================================================================ + // Dialogs - Permissions + // ============================================================================ + 'Manage folder trust settings': '管理文件夹信任设置', + + // ============================================================================ + // Status Bar + // ============================================================================ + 'Using:': '已加载: ', + '{{count}} open file': '{{count}} 个打开的文件', + '{{count}} open files': '{{count}} 个打开的文件', + '(ctrl+g to view)': '(按 ctrl+g 查看)', + '{{count}} {{name}} file': '{{count}} 个 {{name}} 文件', + '{{count}} {{name}} files': '{{count}} 个 {{name}} 文件', + '{{count}} MCP server': '{{count}} 个 MCP 服务器', + '{{count}} MCP servers': '{{count}} 个 MCP 服务器', + '{{count}} Blocked': '{{count}} 个已阻止', + '(ctrl+t to view)': '(按 ctrl+t 查看)', + '(ctrl+t to toggle)': '(按 ctrl+t 切换)', + 'Press Ctrl+C again to exit.': '再次按 Ctrl+C 退出', + 'Press Ctrl+D again to exit.': '再次按 Ctrl+D 退出', + 'Press Esc again to clear.': '再次按 Esc 清除', + + // ============================================================================ + // MCP Status + // ============================================================================ + 'No MCP servers configured.': '未配置 MCP 服务器', + 'Please view MCP documentation in your browser:': + '请在浏览器中查看 MCP 文档:', + 'or use the cli /docs command': '或使用 cli /docs 命令', + '⏳ MCP servers are starting up ({{count}} initializing)...': + '⏳ MCP 服务器正在启动({{count}} 个正在初始化)...', + 'Note: First startup may take longer. Tool availability will update automatically.': + '注意:首次启动可能需要更长时间。工具可用性将自动更新', + 'Configured MCP servers:': '已配置的 MCP 服务器:', + Ready: '就绪', + 'Starting... (first startup may take longer)': + '正在启动...(首次启动可能需要更长时间)', + Disconnected: '已断开连接', + '{{count}} tool': '{{count}} 个工具', + '{{count}} tools': '{{count}} 个工具', + '{{count}} prompt': '{{count}} 个提示', + '{{count}} prompts': '{{count}} 个提示', + '(from {{extensionName}})': '(来自 {{extensionName}})', + + // ============================================================================ + // Startup Tips + // ============================================================================ + 'Tips for getting started:': '入门提示:', + '1. Ask questions, edit files, or run commands.': + '1. 提问、编辑文件或运行命令', + '2. Be specific for the best results.': '2. 具体描述以获得最佳结果', + 'files to customize your interactions with Qwen Code.': + '文件以自定义您与 Qwen Code 的交互', + 'for more information.': '获取更多信息', + + // ============================================================================ + // Exit Screen / Stats + // ============================================================================ + 'Agent powering down. Goodbye!': 'Qwen Code 正在关闭,再见!', + 'Interaction Summary': '交互摘要', + 'Session ID:': '会话 ID:', + 'Tool Calls:': '工具调用:', + 'Success Rate:': '成功率:', + 'User Agreement:': '用户同意率:', + reviewed: '已审核', + 'Code Changes:': '代码变更:', + Performance: '性能', + 'Wall Time:': '总耗时:', + 'Agent Active:': '代理活跃时间:', + 'API Time:': 'API 时间:', + 'Tool Time:': '工具时间:', + 'Session Stats': '会话统计', + 'Model Usage': '模型使用情况', + Reqs: '请求数', + 'Input Tokens': '输入令牌', + 'Output Tokens': '输出令牌', + 'Savings Highlight:': '节省亮点:', + 'of input tokens were served from the cache, reducing costs.': + '的输入令牌来自缓存,降低了成本', + 'Tip: For a full token breakdown, run `/stats model`.': + '提示:要查看完整的令牌明细,请运行 `/stats model`', +}; diff --git a/scripts/copy_files.js b/scripts/copy_files.js index ddf25464..6405a7df 100644 --- a/scripts/copy_files.js +++ b/scripts/copy_files.js @@ -41,8 +41,16 @@ function copyFilesRecursive(source, target) { if (item.isDirectory()) { copyFilesRecursive(sourcePath, targetPath); - } else if (extensionsToCopy.includes(path.extname(item.name))) { - fs.copyFileSync(sourcePath, targetPath); + } else { + const ext = path.extname(item.name); + // Copy standard extensions, or .js files in i18n/locales directory + const isLocaleJs = + ext === '.js' && + (sourcePath.includes('i18n/locales') || + sourcePath.includes(path.join('i18n', 'locales'))); + if (extensionsToCopy.includes(ext) || isLocaleJs) { + fs.copyFileSync(sourcePath, targetPath); + } } } }