Skip to content

Commit 48b7754

Browse files
authored
feat(i18n): Add Internationalization Support for UI and LLM Output (#1058)
1 parent 640f306 commit 48b7754

File tree

98 files changed

+4740
-636
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

98 files changed

+4740
-636
lines changed

docs/cli/commands.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,16 @@ Slash commands provide meta-level control over the CLI itself.
195195
- **`/init`**
196196
- **Description:** Analyzes the current directory and creates a `QWEN.md` context file by default (or the filename specified by `contextFileName`). If a non-empty file already exists, no changes are made. The command seeds an empty file and prompts the model to populate it with project-specific instructions.
197197

198+
- [**`/language`**](./language.md)
199+
- **Description:** View or change the language setting for both UI and LLM output.
200+
- **Sub-commands:**
201+
- **`ui`**: Set the UI language (zh-CN or en-US)
202+
- **`output`**: Set the LLM output language
203+
- **Usage:** `/language [ui|output] [language]`
204+
- **Examples:**
205+
- `/language ui zh-CN` (set UI language to Simplified Chinese)
206+
- `/language output English` (set LLM output language to English)
207+
198208
### Custom Commands
199209

200210
For a quick start, see the [example](#example-a-pure-function-refactoring-command) below.

docs/cli/language.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# Language Command
2+
3+
The `/language` command allows you to customize the language settings for both the Qwen Code user interface (UI) and the language model's output. This command supports two distinct functionalities:
4+
5+
1. Setting the UI language for the Qwen Code interface
6+
2. Setting the output language for the language model (LLM)
7+
8+
## UI Language Settings
9+
10+
To change the UI language of Qwen Code, use the `ui` subcommand:
11+
12+
```
13+
/language ui [zh-CN|en-US]
14+
```
15+
16+
### Available UI Languages
17+
18+
- **zh-CN**: Simplified Chinese (简体中文)
19+
- **en-US**: English
20+
21+
### Examples
22+
23+
```
24+
/language ui zh-CN # Set UI language to Simplified Chinese
25+
/language ui en-US # Set UI language to English
26+
```
27+
28+
### UI Language Subcommands
29+
30+
You can also use direct subcommands for convenience:
31+
32+
- `/language ui zh-CN` or `/language ui zh` or `/language ui 中文`
33+
- `/language ui en-US` or `/language ui en` or `/language ui english`
34+
35+
## LLM Output Language Settings
36+
37+
To set the language for the language model's responses, use the `output` subcommand:
38+
39+
```
40+
/language output <language>
41+
```
42+
43+
This command generates a language rule file that instructs the LLM to respond in the specified language. The rule file is saved to `~/.qwen/output-language.md`.
44+
45+
### Examples
46+
47+
```
48+
/language output 中文 # Set LLM output language to Chinese
49+
/language output English # Set LLM output language to English
50+
/language output 日本語 # Set LLM output language to Japanese
51+
```
52+
53+
## Viewing Current Settings
54+
55+
When used without arguments, the `/language` command displays the current language settings:
56+
57+
```
58+
/language
59+
```
60+
61+
This will show:
62+
63+
- Current UI language
64+
- Current LLM output language (if set)
65+
- Available subcommands
66+
67+
## Notes
68+
69+
- UI language changes take effect immediately and reload all command descriptions
70+
- LLM output language settings are persisted in a rule file that is automatically included in the model's context
71+
- To request additional UI language packs, please open an issue on GitHub

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
"lint:all": "node scripts/lint.js",
4747
"format": "prettier --experimental-cli --write .",
4848
"typecheck": "npm run typecheck --workspaces --if-present",
49+
"check-i18n": "npm run check-i18n --workspace=packages/cli",
4950
"preflight": "npm run clean && npm ci && npm run format && npm run lint:ci && npm run build && npm run typecheck && npm run test:ci",
5051
"prepare": "husky && npm run bundle",
5152
"prepare:package": "node scripts/prepare-package.js",

packages/cli/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626
"format": "prettier --write .",
2727
"test": "vitest run",
2828
"test:ci": "vitest run",
29-
"typecheck": "tsc --noEmit"
29+
"typecheck": "tsc --noEmit",
30+
"check-i18n": "tsx ../../scripts/check-i18n.ts"
3031
},
3132
"files": [
3233
"dist"

packages/cli/src/config/config.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
WriteFileTool,
2424
resolveTelemetrySettings,
2525
FatalConfigError,
26+
Storage,
2627
InputFormat,
2728
OutputFormat,
2829
} from '@qwen-code/qwen-code-core';
@@ -602,6 +603,20 @@ export async function loadCliConfig(
602603
(e) => e.contextFiles,
603604
);
604605

606+
// Automatically load output-language.md if it exists
607+
const outputLanguageFilePath = path.join(
608+
Storage.getGlobalQwenDir(),
609+
'output-language.md',
610+
);
611+
if (fs.existsSync(outputLanguageFilePath)) {
612+
extensionContextFilePaths.push(outputLanguageFilePath);
613+
if (debugMode) {
614+
logger.debug(
615+
`Found output-language.md, adding to context files: ${outputLanguageFilePath}`,
616+
);
617+
}
618+
}
619+
605620
const fileService = new FileDiscoveryService(cwd);
606621

607622
const fileFiltering = {

packages/cli/src/config/settingsSchema.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,23 @@ const SETTINGS_SCHEMA = {
176176
description: 'Enable debug logging of keystrokes to the console.',
177177
showInDialog: true,
178178
},
179+
language: {
180+
type: 'enum',
181+
label: 'Language',
182+
category: 'General',
183+
requiresRestart: false,
184+
default: 'auto',
185+
description:
186+
'The language for the user interface. Use "auto" to detect from system settings. ' +
187+
'You can also use custom language codes (e.g., "es", "fr") by placing JS language files ' +
188+
'in ~/.qwen/locales/ (e.g., ~/.qwen/locales/es.js).',
189+
showInDialog: true,
190+
options: [
191+
{ value: 'auto', label: 'Auto (detect from system)' },
192+
{ value: 'en', label: 'English' },
193+
{ value: 'zh', label: '中文 (Chinese)' },
194+
],
195+
},
179196
},
180197
},
181198
output: {

packages/cli/src/core/initializer.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
import { type LoadedSettings, SettingScope } from '../config/settings.js';
1515
import { performInitialAuth } from './auth.js';
1616
import { validateTheme } from './theme.js';
17+
import { initializeI18n } from '../i18n/index.js';
1718

1819
export interface InitializationResult {
1920
authError: string | null;
@@ -33,6 +34,13 @@ export async function initializeApp(
3334
config: Config,
3435
settings: LoadedSettings,
3536
): Promise<InitializationResult> {
37+
// Initialize i18n system
38+
const languageSetting =
39+
process.env['QWEN_CODE_LANG'] ||
40+
settings.merged.general?.language ||
41+
'auto';
42+
await initializeI18n(languageSetting);
43+
3644
const authType = settings.merged.security?.auth?.selectedType;
3745
const authError = await performInitialAuth(config, authType);
3846

@@ -44,7 +52,6 @@ export async function initializeApp(
4452
undefined,
4553
);
4654
}
47-
4855
const themeError = validateTheme(settings);
4956

5057
const shouldOpenAuthDialog =

packages/cli/src/core/theme.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import { themeManager } from '../ui/themes/theme-manager.js';
88
import { type LoadedSettings } from '../config/settings.js';
9+
import { t } from '../i18n/index.js';
910

1011
/**
1112
* Validates the configured theme.
@@ -15,7 +16,9 @@ import { type LoadedSettings } from '../config/settings.js';
1516
export function validateTheme(settings: LoadedSettings): string | null {
1617
const effectiveTheme = settings.merged.ui?.theme;
1718
if (effectiveTheme && !themeManager.findThemeByName(effectiveTheme)) {
18-
return `Theme "${effectiveTheme}" not found.`;
19+
return t('Theme "{{themeName}}" not found.', {
20+
themeName: effectiveTheme,
21+
});
1922
}
2023
return null;
2124
}

0 commit comments

Comments
 (0)