Skip to content

Commit 2bc8079

Browse files
authored
Merge pull request #1332 from QwenLM/fix-language
Fix multi-language and documentation related issues.
2 parents 17eb20c + 25dbe98 commit 2bc8079

File tree

18 files changed

+949
-903
lines changed

18 files changed

+949
-903
lines changed

docs/users/features/_meta.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ export default {
1010
mcp: 'MCP',
1111
'token-caching': 'Token Caching',
1212
sandbox: 'Sandboxing',
13+
language: 'i18n',
1314
};

docs/users/features/commands.md

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ Commands specifically for controlling interface and output language.
4848
|`ui [language]` | Set UI interface language | `/language ui zh-CN` |
4949
|`output [language]` | Set LLM output language | `/language output Chinese` |
5050

51-
- Available UI languages: `zh-CN` (Simplified Chinese), `en-US` (English)
51+
- Available built-in UI languages: `zh-CN` (Simplified Chinese), `en-US` (English), `ru-RU` (Russian), `de-DE` (German)
5252
- Output language examples: `Chinese`, `English`, `Japanese`, etc.
5353

5454
### 1.4 Tool and Model Management
@@ -72,17 +72,16 @@ Commands for managing AI tools and models.
7272

7373
Commands for obtaining information and performing system settings.
7474

75-
| Command | Description | Usage Examples |
76-
| --------------- | ----------------------------------------------- | ------------------------------------------------ |
77-
| `/help` | Display help information for available commands | `/help` or `/?` |
78-
| `/about` | Display version information | `/about` |
79-
| `/stats` | Display detailed statistics for current session | `/stats` |
80-
| `/settings` | Open settings editor | `/settings` |
81-
| `/auth` | Change authentication method | `/auth` |
82-
| `/bug` | Submit issue about Qwen Code | `/bug Button click unresponsive` |
83-
| `/copy` | Copy last output content to clipboard | `/copy` |
84-
| `/quit-confirm` | Show confirmation dialog before quitting | `/quit-confirm` (shortcut: press `Ctrl+C` twice) |
85-
| `/quit` | Exit Qwen Code immediately | `/quit` or `/exit` |
75+
| Command | Description | Usage Examples |
76+
| ----------- | ----------------------------------------------- | -------------------------------- |
77+
| `/help` | Display help information for available commands | `/help` or `/?` |
78+
| `/about` | Display version information | `/about` |
79+
| `/stats` | Display detailed statistics for current session | `/stats` |
80+
| `/settings` | Open settings editor | `/settings` |
81+
| `/auth` | Change authentication method | `/auth` |
82+
| `/bug` | Submit issue about Qwen Code | `/bug Button click unresponsive` |
83+
| `/copy` | Copy last output content to clipboard | `/copy` |
84+
| `/quit` | Exit Qwen Code immediately | `/quit` or `/exit` |
8685

8786
### 1.6 Common Shortcuts
8887

docs/users/features/language.md

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# Internationalization (i18n) & Language
2+
3+
Qwen Code is built for multilingual workflows: it supports UI localization (i18n/l10n) in the CLI, lets you choose the assistant output language, and allows custom UI language packs.
4+
5+
## Overview
6+
7+
From a user point of view, Qwen Code’s “internationalization” spans multiple layers:
8+
9+
| Capability / Setting | What it controls | Where stored |
10+
| ------------------------ | ---------------------------------------------------------------------- | ---------------------------- |
11+
| `/language ui` | Terminal UI text (menus, system messages, prompts) | `~/.qwen/settings.json` |
12+
| `/language output` | Language the AI responds in (an output preference, not UI translation) | `~/.qwen/output-language.md` |
13+
| Custom UI language packs | Overrides/extends built-in UI translations | `~/.qwen/locales/*.js` |
14+
15+
## UI Language
16+
17+
This is the CLI’s UI localization layer (i18n/l10n): it controls the language of menus, prompts, and system messages.
18+
19+
### Setting the UI Language
20+
21+
Use the `/language ui` command:
22+
23+
```bash
24+
/language ui zh-CN # Chinese
25+
/language ui en-US # English
26+
/language ui ru-RU # Russian
27+
/language ui de-DE # German
28+
```
29+
30+
Aliases are also supported:
31+
32+
```bash
33+
/language ui zh # Chinese
34+
/language ui en # English
35+
/language ui ru # Russian
36+
/language ui de # German
37+
```
38+
39+
### Auto-detection
40+
41+
On first startup, Qwen Code detects your system locale and sets the UI language automatically.
42+
43+
Detection priority:
44+
45+
1. `QWEN_CODE_LANG` environment variable
46+
2. `LANG` environment variable
47+
3. System locale via JavaScript Intl API
48+
4. Default: English
49+
50+
## LLM Output Language
51+
52+
The LLM output language controls what language the AI assistant responds in, regardless of what language you type your questions in.
53+
54+
### How It Works
55+
56+
The LLM output language is controlled by a rule file at `~/.qwen/output-language.md`. This file is automatically included in the LLM's context during startup, instructing it to respond in the specified language.
57+
58+
### Auto-detection
59+
60+
On first startup, if no `output-language.md` file exists, Qwen Code automatically creates one based on your system locale. For example:
61+
62+
- System locale `zh` creates a rule for Chinese responses
63+
- System locale `en` creates a rule for English responses
64+
- System locale `ru` creates a rule for Russian responses
65+
- System locale `de` creates a rule for German responses
66+
67+
### Manual Setting
68+
69+
Use `/language output <language>` to change:
70+
71+
```bash
72+
/language output Chinese
73+
/language output English
74+
/language output Japanese
75+
/language output German
76+
```
77+
78+
Any language name works. The LLM will be instructed to respond in that language.
79+
80+
> [!note]
81+
>
82+
> After changing the output language, restart Qwen Code for the change to take effect.
83+
84+
### File Location
85+
86+
```
87+
~/.qwen/output-language.md
88+
```
89+
90+
## Configuration
91+
92+
### Via Settings Dialog
93+
94+
1. Run `/settings`
95+
2. Find "Language" under General
96+
3. Select your preferred UI language
97+
98+
### Via Environment Variable
99+
100+
```bash
101+
export QWEN_CODE_LANG=zh
102+
```
103+
104+
This influences auto-detection on first startup (if you haven’t set a UI language and no `output-language.md` file exists yet).
105+
106+
## Custom Language Packs
107+
108+
For UI translations, you can create custom language packs in `~/.qwen/locales/`:
109+
110+
- Example: `~/.qwen/locales/es.js` for Spanish
111+
- Example: `~/.qwen/locales/fr.js` for French
112+
113+
User directory takes precedence over built-in translations.
114+
115+
> [!tip]
116+
>
117+
> Contributions are welcome! If you’d like to improve built-in translations or add new languages.
118+
> For a concrete example, see [PR #1238: feat(i18n): add Russian language support](https://github.com/QwenLM/qwen-code/pull/1238).
119+
120+
### Language Pack Format
121+
122+
```javascript
123+
// ~/.qwen/locales/es.js
124+
export default {
125+
Hello: 'Hola',
126+
Settings: 'Configuracion',
127+
// ... more translations
128+
};
129+
```
130+
131+
## Related Commands
132+
133+
- `/language` - Show current language settings
134+
- `/language ui [lang]` - Set UI language
135+
- `/language output <language>` - Set LLM output language
136+
- `/settings` - Open settings dialog

eslint.config.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ export default tseslint.config(
2424
'.integration-tests/**',
2525
'packages/**/.integration-test/**',
2626
'dist/**',
27+
'docs-site/.next/**',
28+
'docs-site/out/**',
2729
],
2830
},
2931
eslint.configs.recommended,

packages/cli/src/core/initializer.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { type LoadedSettings, SettingScope } from '../config/settings.js';
1515
import { performInitialAuth } from './auth.js';
1616
import { validateTheme } from './theme.js';
1717
import { initializeI18n } from '../i18n/index.js';
18+
import { initializeLlmOutputLanguage } from '../ui/commands/languageCommand.js';
1819

1920
export interface InitializationResult {
2021
authError: string | null;
@@ -41,6 +42,9 @@ export async function initializeApp(
4142
'auto';
4243
await initializeI18n(languageSetting);
4344

45+
// Auto-detect and set LLM output language on first use
46+
initializeLlmOutputLanguage();
47+
4448
const authType = settings.merged.security?.auth?.selectedType;
4549
const authError = await performInitialAuth(config, authType);
4650

packages/cli/src/i18n/index.ts

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,28 @@
11
/**
22
* @license
3-
* Copyright 2025 Qwen
3+
* Copyright 2025 Qwen team
44
* SPDX-License-Identifier: Apache-2.0
55
*/
66

77
import * as fs from 'node:fs';
88
import * as path from 'node:path';
99
import { fileURLToPath, pathToFileURL } from 'node:url';
1010
import { homedir } from 'node:os';
11+
import {
12+
type SupportedLanguage,
13+
getLanguageNameFromLocale,
14+
} from './languages.js';
1115

12-
export type SupportedLanguage = 'en' | 'zh' | 'ru' | string; // Allow custom language codes
16+
export type { SupportedLanguage };
17+
export { getLanguageNameFromLocale };
1318

1419
// State
1520
let currentLanguage: SupportedLanguage = 'en';
16-
let translations: Record<string, string> = {};
21+
let translations: Record<string, string | string[]> = {};
1722

1823
// Cache
19-
type TranslationDict = Record<string, string>;
24+
type TranslationValue = string | string[];
25+
type TranslationDict = Record<string, TranslationValue>;
2026
const translationCache: Record<string, TranslationDict> = {};
2127
const loadingPromises: Record<string, Promise<TranslationDict>> = {};
2228

@@ -52,11 +58,13 @@ export function detectSystemLanguage(): SupportedLanguage {
5258
if (envLang?.startsWith('zh')) return 'zh';
5359
if (envLang?.startsWith('en')) return 'en';
5460
if (envLang?.startsWith('ru')) return 'ru';
61+
if (envLang?.startsWith('de')) return 'de';
5562

5663
try {
5764
const locale = Intl.DateTimeFormat().resolvedOptions().locale;
5865
if (locale.startsWith('zh')) return 'zh';
5966
if (locale.startsWith('ru')) return 'ru';
67+
if (locale.startsWith('de')) return 'de';
6068
} catch {
6169
// Fallback to default
6270
}
@@ -224,9 +232,25 @@ export function getCurrentLanguage(): SupportedLanguage {
224232

225233
export function t(key: string, params?: Record<string, string>): string {
226234
const translation = translations[key] ?? key;
235+
if (Array.isArray(translation)) {
236+
return key;
237+
}
227238
return interpolate(translation, params);
228239
}
229240

241+
/**
242+
* Get a translation that is an array of strings.
243+
* @param key The translation key
244+
* @returns The array of strings, or an empty array if not found or not an array
245+
*/
246+
export function ta(key: string): string[] {
247+
const translation = translations[key];
248+
if (Array.isArray(translation)) {
249+
return translation;
250+
}
251+
return [];
252+
}
253+
230254
export async function initializeI18n(
231255
lang?: SupportedLanguage | 'auto',
232256
): Promise<void> {

packages/cli/src/i18n/languages.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/**
2+
* @license
3+
* Copyright 2025 Qwen team
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
export type SupportedLanguage = 'en' | 'zh' | 'ru' | 'de' | string;
8+
9+
export interface LanguageDefinition {
10+
/** The internal locale code used by the i18n system (e.g., 'en', 'zh'). */
11+
code: SupportedLanguage;
12+
/** The standard name used in UI settings (e.g., 'en-US', 'zh-CN'). */
13+
id: string;
14+
/** The full English name of the language (e.g., 'English', 'Chinese'). */
15+
fullName: string;
16+
}
17+
18+
export const SUPPORTED_LANGUAGES: readonly LanguageDefinition[] = [
19+
{
20+
code: 'en',
21+
id: 'en-US',
22+
fullName: 'English',
23+
},
24+
{
25+
code: 'zh',
26+
id: 'zh-CN',
27+
fullName: 'Chinese',
28+
},
29+
{
30+
code: 'ru',
31+
id: 'ru-RU',
32+
fullName: 'Russian',
33+
},
34+
{
35+
code: 'de',
36+
id: 'de-DE',
37+
fullName: 'German',
38+
},
39+
];
40+
41+
/**
42+
* Maps a locale code to its English language name.
43+
* Used for LLM output language instructions.
44+
*/
45+
export function getLanguageNameFromLocale(locale: SupportedLanguage): string {
46+
const lang = SUPPORTED_LANGUAGES.find((l) => l.code === locale);
47+
return lang?.fullName || 'English';
48+
}

0 commit comments

Comments
 (0)