feat: Add i18n support for /settings, /theme

This commit is contained in:
pomelo-nwu
2025-11-18 14:08:41 +08:00
parent 12bebe1b5f
commit 32982a16e6
12 changed files with 323 additions and 44 deletions

View File

@@ -6,6 +6,7 @@
import { themeManager } from '../ui/themes/theme-manager.js';
import { type LoadedSettings } from '../config/settings.js';
import { t } from '../i18n/index.js';
/**
* Validates the configured theme.
@@ -15,7 +16,9 @@ import { type LoadedSettings } from '../config/settings.js';
export function validateTheme(settings: LoadedSettings): string | null {
const effectiveTheme = settings.merged.ui?.theme;
if (effectiveTheme && !themeManager.findThemeByName(effectiveTheme)) {
return `Theme "${effectiveTheme}" not found.`;
return t('Theme "{{themeName}}" not found.', {
themeName: effectiveTheme,
});
}
return null;
}

View File

@@ -18,6 +18,11 @@ export default {
'@': '@',
'@src/myFile.ts': '@src/myFile.ts',
'Shell mode': 'Shell mode',
'YOLO mode': 'YOLO mode',
'plan mode': 'plan mode',
'auto-accept edits': 'auto-accept edits',
'Accepting edits': 'Accepting edits',
'(shift + tab to cycle)': '(shift + tab to cycle)',
'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}}).',
'!': '!',
@@ -80,10 +85,23 @@ export default {
'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]',
'Available Qwen Code CLI tools:': 'Available Qwen Code CLI tools:',
'No tools available': 'No tools available',
'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',
'Select Theme': 'Select Theme',
Preview: 'Preview',
'(Use Enter to select, Tab to configure scope)':
'(Use Enter to select, Tab to configure scope)',
'(Use Enter to apply scope, Tab to select theme)':
'(Use Enter to apply scope, Tab to select theme)',
'Theme configuration unavailable due to NO_COLOR env variable.':
'Theme configuration unavailable due to NO_COLOR env variable.',
'Theme "{{themeName}}" not found.': 'Theme "{{themeName}}" not found.',
'Theme "{{themeName}}" not found in selected scope.':
'Theme "{{themeName}}" not found in selected scope.',
'clear the screen and conversation history':
'clear the screen and conversation history',
'Compresses the context by replacing it with a summary.':
@@ -236,6 +254,71 @@ export default {
// Commands - General (continued)
// ============================================================================
'View and edit Qwen Code settings': 'View and edit Qwen Code settings',
Settings: 'Settings',
'(Use Enter to select{{tabText}})': '(Use Enter to select{{tabText}})',
', Tab to change focus': ', Tab to change focus',
'To see changes, Qwen Code must be restarted. Press r to exit and apply changes now.':
'To see changes, Qwen Code must be restarted. Press r to exit and apply changes now.',
// ============================================================================
// Settings Labels
// ============================================================================
'Vim Mode': 'Vim Mode',
'Disable Auto Update': 'Disable Auto Update',
'Enable Prompt Completion': 'Enable Prompt Completion',
'Debug Keystroke Logging': 'Debug Keystroke Logging',
Language: 'Language',
'Output Format': 'Output Format',
'Hide Window Title': 'Hide Window Title',
'Show Status in Title': 'Show Status in Title',
'Hide Tips': 'Hide Tips',
'Hide Banner': 'Hide Banner',
'Hide Context Summary': 'Hide Context Summary',
'Hide CWD': 'Hide CWD',
'Hide Sandbox Status': 'Hide Sandbox Status',
'Hide Model Info': 'Hide Model Info',
'Hide Footer': 'Hide Footer',
'Show Memory Usage': 'Show Memory Usage',
'Show Line Numbers': 'Show Line Numbers',
'Show Citations': 'Show Citations',
'Custom Witty Phrases': 'Custom Witty Phrases',
'Enable Welcome Back': 'Enable Welcome Back',
'Disable Loading Phrases': 'Disable Loading Phrases',
'Screen Reader Mode': 'Screen Reader Mode',
'IDE Mode': 'IDE Mode',
'Max Session Turns': 'Max Session Turns',
'Skip Next Speaker Check': 'Skip Next Speaker Check',
'Skip Loop Detection': 'Skip Loop Detection',
'Skip Startup Context': 'Skip Startup Context',
'Enable OpenAI Logging': 'Enable OpenAI Logging',
'OpenAI Logging Directory': 'OpenAI Logging Directory',
Timeout: 'Timeout',
'Max Retries': 'Max Retries',
'Disable Cache Control': 'Disable Cache Control',
'Memory Discovery Max Dirs': 'Memory Discovery Max Dirs',
'Load Memory From Include Directories':
'Load Memory From Include Directories',
'Respect .gitignore': 'Respect .gitignore',
'Respect .qwenignore': 'Respect .qwenignore',
'Enable Recursive File Search': 'Enable Recursive File Search',
'Disable Fuzzy Search': 'Disable Fuzzy Search',
'Enable Interactive Shell': 'Enable Interactive Shell',
'Show Color': 'Show Color',
'Auto Accept': 'Auto Accept',
'Use Ripgrep': 'Use Ripgrep',
'Use Builtin Ripgrep': 'Use Builtin Ripgrep',
'Enable Tool Output Truncation': 'Enable Tool Output Truncation',
'Tool Output Truncation Threshold': 'Tool Output Truncation Threshold',
'Tool Output Truncation Lines': 'Tool Output Truncation Lines',
'Folder Trust': 'Folder Trust',
'Vision Model Preview': 'Vision Model Preview',
// Settings enum options
'Auto (detect from system)': 'Auto (detect from system)',
Text: 'Text',
JSON: 'JSON',
Plan: 'Plan',
Default: 'Default',
'Auto Edit': 'Auto Edit',
YOLO: 'YOLO',
'toggle vim mode on/off': 'toggle vim mode on/off',
'check session stats. Usage: /stats [model|tools]':
'check session stats. Usage: /stats [model|tools]',
@@ -534,6 +617,33 @@ export default {
'Failed to compress chat history.': 'Failed to compress chat history.',
'Failed to compress chat history: {{error}}':
'Failed to compress chat history: {{error}}',
'Compressing chat history': 'Compressing chat history',
'Chat history compressed from {{originalTokens}} to {{newTokens}} tokens.':
'Chat history compressed from {{originalTokens}} to {{newTokens}} tokens.',
'Compression was not beneficial for this history size.':
'Compression was not beneficial for this history size.',
'Chat history compression did not reduce size. This may indicate issues with the compression prompt.':
'Chat history compression did not reduce size. This may indicate issues with the compression prompt.',
'Could not compress chat history due to a token counting error.':
'Could not compress chat history due to a token counting error.',
'Chat history is already compressed.': 'Chat history is already compressed.',
// ============================================================================
// Commands - Directory
// ============================================================================
'Configuration is not available.': 'Configuration is not available.',
'Please provide at least one path to add.':
'Please provide at least one path to add.',
'The /directory add command is not supported in restrictive sandbox profiles. Please use --include-directories when starting the session instead.':
'The /directory add command is not supported in restrictive sandbox profiles. Please use --include-directories when starting the session instead.',
"Error adding '{{path}}': {{error}}": "Error adding '{{path}}': {{error}}",
'Successfully added GEMINI.md files from the following directories if there are:\n- {{directories}}':
'Successfully added GEMINI.md files from the following directories if there are:\n- {{directories}}',
'Error refreshing memory: {{error}}': 'Error refreshing memory: {{error}}',
'Successfully added directories:\n- {{directories}}':
'Successfully added directories:\n- {{directories}}',
'Current workspace directories:\n{{directories}}':
'Current workspace directories:\n{{directories}}',
// ============================================================================
// Commands - Docs

View File

@@ -17,6 +17,11 @@ export default {
'@': '@',
'@src/myFile.ts': '@src/myFile.ts',
'Shell mode': 'Shell 模式',
'YOLO mode': 'YOLO 模式',
'plan mode': '规划模式',
'auto-accept edits': '自动接受编辑',
'Accepting edits': '接受编辑',
'(shift + tab to cycle)': '(shift + tab 切换)',
'Execute shell commands via {{symbol}} (e.g., {{example1}}) or use natural language (e.g., {{example2}}).':
'通过 {{symbol}} 执行 shell 命令(例如,{{example1}})或使用自然语言(例如,{{example2}}',
'!': '!',
@@ -79,10 +84,23 @@ export default {
'分析项目并创建定制的 QWEN.md 文件',
'list available Qwen Code tools. Usage: /tools [desc]':
'列出可用的 Qwen Code 工具。用法:/tools [desc]',
'Available Qwen Code CLI tools:': '可用的 Qwen Code CLI 工具:',
'No tools available': '没有可用工具',
'View or change the approval mode for tool usage':
'查看或更改工具使用的审批模式',
'View or change the language setting': '查看或更改语言设置',
'change the theme': '更改主题',
'Select Theme': '选择主题',
Preview: '预览',
'(Use Enter to select, Tab to configure scope)':
'(使用 Enter 选择Tab 配置作用域)',
'(Use Enter to apply scope, Tab to select theme)':
'(使用 Enter 应用作用域Tab 选择主题)',
'Theme configuration unavailable due to NO_COLOR env variable.':
'由于 NO_COLOR 环境变量,主题配置不可用。',
'Theme "{{themeName}}" not found.': '未找到主题 "{{themeName}}"。',
'Theme "{{themeName}}" not found in selected scope.':
'在所选作用域中未找到主题 "{{themeName}}"。',
'clear the screen and conversation history': '清屏并清除对话历史',
'Compresses the context by replacing it with a summary.':
'通过用摘要替换来压缩上下文',
@@ -227,6 +245,70 @@ export default {
// Commands - General (continued)
// ============================================================================
'View and edit Qwen Code settings': '查看和编辑 Qwen Code 设置',
Settings: '设置',
'(Use Enter to select{{tabText}})': '(使用 Enter 选择{{tabText}}',
', Tab to change focus': 'Tab 切换焦点',
'To see changes, Qwen Code must be restarted. Press r to exit and apply changes now.':
'要查看更改,必须重启 Qwen Code。按 r 退出并立即应用更改。',
// ============================================================================
// Settings Labels
// ============================================================================
'Vim Mode': 'Vim 模式',
'Disable Auto Update': '禁用自动更新',
'Enable Prompt Completion': '启用提示补全',
'Debug Keystroke Logging': '调试按键记录',
Language: '语言',
'Output Format': '输出格式',
'Hide Window Title': '隐藏窗口标题',
'Show Status in Title': '在标题中显示状态',
'Hide Tips': '隐藏提示',
'Hide Banner': '隐藏横幅',
'Hide Context Summary': '隐藏上下文摘要',
'Hide CWD': '隐藏当前工作目录',
'Hide Sandbox Status': '隐藏沙箱状态',
'Hide Model Info': '隐藏模型信息',
'Hide Footer': '隐藏页脚',
'Show Memory Usage': '显示内存使用',
'Show Line Numbers': '显示行号',
'Show Citations': '显示引用',
'Custom Witty Phrases': '自定义诙谐短语',
'Enable Welcome Back': '启用欢迎回来',
'Disable Loading Phrases': '禁用加载短语',
'Screen Reader Mode': '屏幕阅读器模式',
'IDE Mode': 'IDE 模式',
'Max Session Turns': '最大会话轮次',
'Skip Next Speaker Check': '跳过下一个说话者检查',
'Skip Loop Detection': '跳过循环检测',
'Skip Startup Context': '跳过启动上下文',
'Enable OpenAI Logging': '启用 OpenAI 日志',
'OpenAI Logging Directory': 'OpenAI 日志目录',
Timeout: '超时',
'Max Retries': '最大重试次数',
'Disable Cache Control': '禁用缓存控制',
'Memory Discovery Max Dirs': '内存发现最大目录数',
'Load Memory From Include Directories': '从包含目录加载内存',
'Respect .gitignore': '遵守 .gitignore',
'Respect .qwenignore': '遵守 .qwenignore',
'Enable Recursive File Search': '启用递归文件搜索',
'Disable Fuzzy Search': '禁用模糊搜索',
'Enable Interactive Shell': '启用交互式 Shell',
'Show Color': '显示颜色',
'Auto Accept': '自动接受',
'Use Ripgrep': '使用 Ripgrep',
'Use Builtin Ripgrep': '使用内置 Ripgrep',
'Enable Tool Output Truncation': '启用工具输出截断',
'Tool Output Truncation Threshold': '工具输出截断阈值',
'Tool Output Truncation Lines': '工具输出截断行数',
'Folder Trust': '文件夹信任',
'Vision Model Preview': '视觉模型预览',
// Settings enum options
'Auto (detect from system)': '自动(从系统检测)',
Text: '文本',
JSON: 'JSON',
Plan: '规划',
Default: '默认',
'Auto Edit': '自动编辑',
YOLO: 'YOLO',
'toggle vim mode on/off': '切换 vim 模式开关',
'check session stats. Usage: /stats [model|tools]':
'检查会话统计信息。用法:/stats [model|tools]',
@@ -336,7 +418,7 @@ export default {
'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':
@@ -502,6 +584,32 @@ export default {
'正在压缩中,请等待上一个请求完成',
'Failed to compress chat history.': '压缩聊天历史失败',
'Failed to compress chat history: {{error}}': '压缩聊天历史失败:{{error}}',
'Compressing chat history': '正在压缩聊天历史',
'Chat history compressed from {{originalTokens}} to {{newTokens}} tokens.':
'聊天历史已从 {{originalTokens}} 个 token 压缩到 {{newTokens}} 个 token。',
'Compression was not beneficial for this history size.':
'对于此历史记录大小,压缩没有益处。',
'Chat history compression did not reduce size. This may indicate issues with the compression prompt.':
'聊天历史压缩未能减小大小。这可能表明压缩提示存在问题。',
'Could not compress chat history due to a token counting error.':
'由于 token 计数错误,无法压缩聊天历史。',
'Chat history is already compressed.': '聊天历史已经压缩。',
// ============================================================================
// Commands - Directory
// ============================================================================
'Configuration is not available.': '配置不可用。',
'Please provide at least one path to add.': '请提供至少一个要添加的路径。',
'The /directory add command is not supported in restrictive sandbox profiles. Please use --include-directories when starting the session instead.':
'/directory add 命令在限制性沙箱配置文件中不受支持。请改为在启动会话时使用 --include-directories。',
"Error adding '{{path}}': {{error}}": "添加 '{{path}}' 时出错:{{error}}",
'Successfully added GEMINI.md files from the following directories if there are:\n- {{directories}}':
'如果存在,已成功从以下目录添加 GEMINI.md 文件:\n- {{directories}}',
'Error refreshing memory: {{error}}': '刷新内存时出错:{{error}}',
'Successfully added directories:\n- {{directories}}':
'成功添加目录:\n- {{directories}}',
'Current workspace directories:\n{{directories}}':
'当前工作区目录:\n{{directories}}',
// ============================================================================
// Commands - Docs
@@ -593,8 +701,8 @@ export default {
'(Use Enter to Set Auth)': '(使用 Enter 设置认证)',
'Terms of Services and Privacy Notice for Qwen Code':
'Qwen Code 的服务条款和隐私声明',
'Qwen OAuth': 'Qwen OAuth (推荐)',
OpenAI: 'OpenAI (兼容 API)',
'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}}',

View File

@@ -52,7 +52,7 @@ export const directoryCommand: SlashCommand = {
addItem(
{
type: MessageType.ERROR,
text: 'Configuration is not available.',
text: t('Configuration is not available.'),
},
Date.now(),
);
@@ -69,7 +69,7 @@ export const directoryCommand: SlashCommand = {
addItem(
{
type: MessageType.ERROR,
text: 'Please provide at least one path to add.',
text: t('Please provide at least one path to add.'),
},
Date.now(),
);
@@ -80,8 +80,9 @@ export const directoryCommand: SlashCommand = {
return {
type: 'message' as const,
messageType: 'error' as const,
content:
content: t(
'The /directory add command is not supported in restrictive sandbox profiles. Please use --include-directories when starting the session instead.',
),
};
}
@@ -94,7 +95,12 @@ export const directoryCommand: SlashCommand = {
added.push(pathToAdd.trim());
} catch (e) {
const error = e as Error;
errors.push(`Error adding '${pathToAdd.trim()}': ${error.message}`);
errors.push(
t("Error adding '{{path}}': {{error}}", {
path: pathToAdd.trim(),
error: error.message,
}),
);
}
}
@@ -123,12 +129,21 @@ export const directoryCommand: SlashCommand = {
addItem(
{
type: MessageType.INFO,
text: `Successfully added GEMINI.md files from the following directories if there are:\n- ${added.join('\n- ')}`,
text: t(
'Successfully added GEMINI.md files from the following directories if there are:\n- {{directories}}',
{
directories: added.join('\n- '),
},
),
},
Date.now(),
);
} catch (error) {
errors.push(`Error refreshing memory: ${(error as Error).message}`);
errors.push(
t('Error refreshing memory: {{error}}', {
error: (error as Error).message,
}),
);
}
if (added.length > 0) {
@@ -139,7 +154,9 @@ export const directoryCommand: SlashCommand = {
addItem(
{
type: MessageType.INFO,
text: `Successfully added directories:\n- ${added.join('\n- ')}`,
text: t('Successfully added directories:\n- {{directories}}', {
directories: added.join('\n- '),
}),
},
Date.now(),
);
@@ -169,7 +186,7 @@ export const directoryCommand: SlashCommand = {
addItem(
{
type: MessageType.ERROR,
text: 'Configuration is not available.',
text: t('Configuration is not available.'),
},
Date.now(),
);
@@ -181,7 +198,9 @@ export const directoryCommand: SlashCommand = {
addItem(
{
type: MessageType.INFO,
text: `Current workspace directories:\n${directoryList}`,
text: t('Current workspace directories:\n{{directories}}', {
directories: directoryList,
}),
},
Date.now(),
);

View File

@@ -8,6 +8,7 @@ import type React from 'react';
import { Box, Text } from 'ink';
import { theme } from '../semantic-colors.js';
import { ApprovalMode } from '@qwen-code/qwen-code-core';
import { t } from '../../i18n/index.js';
interface AutoAcceptIndicatorProps {
approvalMode: ApprovalMode;
@@ -23,18 +24,18 @@ export const AutoAcceptIndicator: React.FC<AutoAcceptIndicatorProps> = ({
switch (approvalMode) {
case ApprovalMode.PLAN:
textColor = theme.status.success;
textContent = 'plan mode';
subText = ' (shift + tab to cycle)';
textContent = t('plan mode');
subText = ` ${t('(shift + tab to cycle)')}`;
break;
case ApprovalMode.AUTO_EDIT:
textColor = theme.status.warning;
textContent = 'auto-accept edits';
subText = ' (shift + tab to cycle)';
textContent = t('auto-accept edits');
subText = ` ${t('(shift + tab to cycle)')}`;
break;
case ApprovalMode.YOLO:
textColor = theme.status.error;
textContent = 'YOLO mode';
subText = ' (shift + tab to cycle)';
textContent = t('YOLO mode');
subText = ` ${t('(shift + tab to cycle)')}`;
break;
case ApprovalMode.DEFAULT:
default:

View File

@@ -28,6 +28,7 @@ import {
parseInputForHighlighting,
buildSegmentsForVisualSlice,
} from '../utils/highlight.js';
import { t } from '../../i18n/index.js';
import {
clipboardHasImage,
saveClipboardImage,
@@ -833,13 +834,13 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
let statusText = '';
if (shellModeActive) {
statusColor = theme.ui.symbol;
statusText = 'Shell mode';
statusText = t('Shell mode');
} else if (showYoloStyling) {
statusColor = theme.status.error;
statusText = 'YOLO mode';
statusText = t('YOLO mode');
} else if (showAutoAcceptStyling) {
statusColor = theme.status.warning;
statusText = 'Accepting edits';
statusText = t('Accepting edits');
}
return (

View File

@@ -11,6 +11,7 @@ import type { LoadedSettings, Settings } from '../../config/settings.js';
import { SettingScope } from '../../config/settings.js';
import { getScopeMessageForSetting } from '../../utils/dialogScopeUtils.js';
import { ScopeSelector } from './shared/ScopeSelector.js';
import { t } from '../../i18n/index.js';
import {
getDialogSettingKeys,
setPendingSettingValue,
@@ -124,7 +125,9 @@ export function SettingsDialog({
const definition = getSettingDefinition(key);
return {
label: definition?.label || key,
label: definition?.label
? t(definition.label) || definition.label
: key,
value: key,
type: definition?.type,
toggle: () => {
@@ -779,7 +782,8 @@ export function SettingsDialog({
>
<Box flexDirection="column" flexGrow={1}>
<Text bold={focusSection === 'settings'} wrap="truncate">
{focusSection === 'settings' ? '> ' : ' '}Settings
{focusSection === 'settings' ? '> ' : ' '}
{t('Settings')}
</Text>
<Box height={1} />
{showScrollUp && <Text color={theme.text.secondary}></Text>}
@@ -916,13 +920,15 @@ export function SettingsDialog({
<Box height={1} />
<Text color={theme.text.secondary}>
(Use Enter to select
{showScopeSelection ? ', Tab to change focus' : ''})
{t('(Use Enter to select{{tabText}})', {
tabText: showScopeSelection ? t(', Tab to change focus') : '',
})}
</Text>
{showRestartPrompt && (
<Text color={theme.status.warning}>
To see changes, Qwen Code must be restarted. Press r to exit and
apply changes now.
{t(
'To see changes, Qwen Code must be restarted. Press r to exit and apply changes now.',
)}
</Text>
)}
</Box>

View File

@@ -17,6 +17,7 @@ import { SettingScope } from '../../config/settings.js';
import { getScopeMessageForSetting } from '../../utils/dialogScopeUtils.js';
import { useKeypress } from '../hooks/useKeypress.js';
import { ScopeSelector } from './shared/ScopeSelector.js';
import { t } from '../../i18n/index.js';
interface ThemeDialogProps {
/** Callback function when a theme is selected */
@@ -198,7 +199,8 @@ export function ThemeDialog({
{/* Left Column: Selection */}
<Box flexDirection="column" width="45%" paddingRight={2}>
<Text bold={mode === 'theme'} wrap="truncate">
{mode === 'theme' ? '> ' : ' '}Select Theme{' '}
{mode === 'theme' ? '> ' : ' '}
{t('Select Theme')}{' '}
<Text color={theme.text.secondary}>
{otherScopeModifiedMessage}
</Text>
@@ -218,7 +220,7 @@ export function ThemeDialog({
{/* Right Column: Preview */}
<Box flexDirection="column" width="55%" paddingLeft={2}>
<Text bold color={theme.text.primary}>
Preview
{t('Preview')}
</Text>
{/* Get the Theme object for the highlighted theme, fall back to default if not found */}
{(() => {
@@ -274,8 +276,9 @@ def fibonacci(n):
)}
<Box marginTop={1}>
<Text color={theme.text.secondary} wrap="truncate">
(Use Enter to {mode === 'theme' ? 'select' : 'apply scope'}, Tab to{' '}
{mode === 'theme' ? 'configure scope' : 'select theme'})
{mode === 'theme'
? t('(Use Enter to select, Tab to configure scope)')
: t('(Use Enter to apply scope, Tab to select theme)')}
</Text>
</Box>
</Box>

View File

@@ -10,6 +10,7 @@ import Spinner from 'ink-spinner';
import { theme } from '../../semantic-colors.js';
import { SCREEN_READER_MODEL_PREFIX } from '../../textConstants.js';
import { CompressionStatus } from '@qwen-code/qwen-code-core';
import { t } from '../../../i18n/index.js';
export interface CompressionDisplayProps {
compression: CompressionProps;
@@ -30,24 +31,34 @@ export function CompressionMessage({
const getCompressionText = () => {
if (isPending) {
return 'Compressing chat history';
return t('Compressing chat history');
}
switch (compressionStatus) {
case CompressionStatus.COMPRESSED:
return `Chat history compressed from ${originalTokens} to ${newTokens} tokens.`;
return t(
'Chat history compressed from {{originalTokens}} to {{newTokens}} tokens.',
{
originalTokens: String(originalTokens),
newTokens: String(newTokens),
},
);
case CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT:
// For smaller histories (< 50k tokens), compression overhead likely exceeds benefits
if (originalTokens < 50000) {
return 'Compression was not beneficial for this history size.';
return t('Compression was not beneficial for this history size.');
}
// For larger histories where compression should work but didn't,
// this suggests an issue with the compression process itself
return 'Chat history compression did not reduce size. This may indicate issues with the compression prompt.';
return t(
'Chat history compression did not reduce size. This may indicate issues with the compression prompt.',
);
case CompressionStatus.COMPRESSION_FAILED_TOKEN_COUNT_ERROR:
return 'Could not compress chat history due to a token counting error.';
return t(
'Could not compress chat history due to a token counting error.',
);
case CompressionStatus.NOOP:
return 'Chat history is already compressed.';
return t('Chat history is already compressed.');
default:
return '';
}

View File

@@ -9,6 +9,7 @@ import { Box, Text } from 'ink';
import { theme } from '../../semantic-colors.js';
import { type ToolDefinition } from '../../types.js';
import { MarkdownDisplay } from '../../utils/MarkdownDisplay.js';
import { t } from '../../../i18n/index.js';
interface ToolsListProps {
tools: readonly ToolDefinition[];
@@ -23,7 +24,7 @@ export const ToolsList: React.FC<ToolsListProps> = ({
}) => (
<Box flexDirection="column" marginBottom={1}>
<Text bold color={theme.text.primary}>
Available Qwen Code CLI tools:
{t('Available Qwen Code CLI tools:')}
</Text>
<Box height={1} />
{tools.length > 0 ? (
@@ -46,7 +47,7 @@ export const ToolsList: React.FC<ToolsListProps> = ({
</Box>
))
) : (
<Text color={theme.text.primary}> No tools available</Text>
<Text color={theme.text.primary}> {t('No tools available')}</Text>
)}
</Box>
);

View File

@@ -9,6 +9,7 @@ import { themeManager } from '../themes/theme-manager.js';
import type { LoadedSettings, SettingScope } from '../../config/settings.js'; // Import LoadedSettings, AppSettings, MergedSetting
import { type HistoryItem, MessageType } from '../types.js';
import process from 'node:process';
import { t } from '../../i18n/index.js';
interface UseThemeCommandReturn {
isThemeDialogOpen: boolean;
@@ -34,7 +35,9 @@ export const useThemeCommand = (
addItem(
{
type: MessageType.INFO,
text: 'Theme configuration unavailable due to NO_COLOR env variable.',
text: t(
'Theme configuration unavailable due to NO_COLOR env variable.',
),
},
Date.now(),
);
@@ -48,7 +51,11 @@ export const useThemeCommand = (
if (!themeManager.setActiveTheme(themeName)) {
// If theme is not found, open the theme selection dialog and set error message
setIsThemeDialogOpen(true);
setThemeError(`Theme "${themeName}" not found.`);
setThemeError(
t('Theme "{{themeName}}" not found.', {
themeName: themeName ?? '',
}),
);
} else {
setThemeError(null); // Clear any previous theme error on success
}
@@ -75,7 +82,11 @@ export const useThemeCommand = (
const isBuiltIn = themeManager.findThemeByName(themeName);
const isCustom = themeName && mergedCustomThemes[themeName];
if (!isBuiltIn && !isCustom) {
setThemeError(`Theme "${themeName}" not found in selected scope.`);
setThemeError(
t('Theme "{{themeName}}" not found in selected scope.', {
themeName: themeName ?? '',
}),
);
setIsThemeDialogOpen(true);
return;
}

View File

@@ -16,6 +16,7 @@ import type {
SettingsValue,
} from '../config/settingsSchema.js';
import { getSettingsSchema } from '../config/settingsSchema.js';
import { t } from '../i18n/index.js';
// The schema is now nested, but many parts of the UI and logic work better
// with a flattened structure and dot-notation keys. This section flattens the
@@ -446,7 +447,11 @@ export function getDisplayValue(
if (definition?.type === 'enum' && definition.options) {
const option = definition.options?.find((option) => option.value === value);
valueString = option?.label ?? `${value}`;
if (option?.label) {
valueString = t(option.label) || option.label;
} else {
valueString = `${value}`;
}
}
// Check if value is different from default OR if it's in modified settings OR if there are pending changes