Skip to content

Commit 818244b

Browse files
authored
feat: support missing key handler (#4414)
1 parent e3adcbf commit 818244b

File tree

6 files changed

+174
-0
lines changed

6 files changed

+174
-0
lines changed

packages/i18n/src/config/config.default.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,5 @@ export const i18n: I18nOptions = {
2020
},
2121
},
2222
localsField: 'i18n',
23+
missingKeyHandler: message => message,
2324
};

packages/i18n/src/i18nService.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,12 @@ export class MidwayI18nServiceSingleton {
108108
);
109109
}
110110
}
111+
112+
// If still no message found, use missingKeyHandler
113+
if (!msg && this.i18nConfig.missingKeyHandler) {
114+
msg = this.i18nConfig.missingKeyHandler(message, options);
115+
}
116+
111117
return msg;
112118
}
113119

packages/i18n/src/interface.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export interface I18nOptions {
2020
writeCookie: boolean;
2121
resolver: RequestResolver | false,
2222
localsField: string;
23+
missingKeyHandler?: (message: string, options?: TranslateOptions) => string;
2324
}
2425

2526
export const I18N_ATTR_KEY = 'i18n:locale';

packages/i18n/test/index.test.ts

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,92 @@ describe('test/index.test.ts', () => {
9191
await close(app);
9292
});
9393

94+
it('should test missingKeyHandler with default behavior', async () => {
95+
const app = await createLightApp(join(__dirname, './fixtures/base-app'), {
96+
globalConfig: {
97+
i18n: {
98+
defaultLocale: 'en_US',
99+
localeTable: {
100+
en_US: {
101+
'hello': 'hello world'
102+
}
103+
}
104+
}
105+
}
106+
});
107+
108+
const i18nService = await app.getApplicationContext().getAsync(MidwayI18nService);
109+
110+
// Test existing key
111+
expect(i18nService.translate('hello')).toEqual('hello world');
112+
113+
// Test missing key with default missingKeyHandler (should return the key itself)
114+
expect(i18nService.translate('nonexistent_key')).toEqual('nonexistent_key');
115+
116+
await close(app);
117+
});
118+
119+
it('should test custom missingKeyHandler', async () => {
120+
const app = await createLightApp(join(__dirname, './fixtures/base-app'), {
121+
globalConfig: {
122+
i18n: {
123+
defaultLocale: 'en_US',
124+
localeTable: {
125+
en_US: {
126+
'hello': 'hello world'
127+
}
128+
},
129+
missingKeyHandler: (message, options) => {
130+
return `[Missing: ${message}${options?.locale ? ` (${options.locale})` : ''}]`;
131+
}
132+
}
133+
}
134+
});
135+
136+
const i18nService = await app.getApplicationContext().getAsync(MidwayI18nService);
137+
138+
// Test existing key
139+
expect(i18nService.translate('hello')).toEqual('hello world');
140+
141+
// Test missing key with custom missingKeyHandler
142+
expect(i18nService.translate('nonexistent_key')).toEqual('[Missing: nonexistent_key]');
143+
expect(i18nService.translate('nonexistent_key', { locale: 'zh_CN' })).toEqual('[Missing: nonexistent_key (zh_CN)]');
144+
145+
await close(app);
146+
});
147+
148+
it('should test missingKeyHandler with groups', async () => {
149+
const app = await createLightApp(join(__dirname, './fixtures/base-app'), {
150+
globalConfig: {
151+
i18n: {
152+
defaultLocale: 'en_US',
153+
localeTable: {
154+
en_US: {
155+
'user': {
156+
'hello': 'Hello user'
157+
}
158+
}
159+
},
160+
missingKeyHandler: (message, options) => {
161+
const group = options?.group || 'default';
162+
return `[${group}.${message}]`;
163+
}
164+
}
165+
}
166+
});
167+
168+
const i18nService = await app.getApplicationContext().getAsync(MidwayI18nService);
169+
170+
// Test existing key in group
171+
expect(i18nService.translate('hello', { group: 'user' })).toEqual('Hello user');
172+
173+
// Test missing key in group
174+
expect(i18nService.translate('goodbye', { group: 'user' })).toEqual('[user.goodbye]');
175+
expect(i18nService.translate('goodbye')).toEqual('[default.goodbye]');
176+
177+
await close(app);
178+
});
179+
94180
it('should test fallbacks', async () => {
95181
const app = await createLightApp(join(__dirname, './fixtures/base-app'), {
96182
globalConfig: {

site/docs/extensions/i18n.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,8 @@ export default {
491491
},
492492
},
493493
localsField: 'i18n',
494+
// 缺失翻译键的处理函数,默认返回原始键名
495+
missingKeyHandler: (message, options) => message,
494496
}
495497
}
496498
```
@@ -554,7 +556,43 @@ export default {
554556
```
555557

556558

559+
### 缺失翻译键处理
557560

561+
当翻译键在所有语言文件中都找不到时,i18n 组件会调用 `missingKeyHandler` 函数来处理这种情况。
562+
563+
默认情况下,当找不到翻译时,会返回原始的键名:
564+
565+
```typescript
566+
// 默认的 missingKeyHandler
567+
missingKeyHandler: (message, options) => message
568+
```
569+
570+
例如:
571+
```typescript
572+
// 假设 "unknown_key" 在任何语言文件中都不存在
573+
this.i18nService.translate('unknown_key');
574+
// 返回: "unknown_key"
575+
```
576+
577+
你可以自定义缺失键的处理方式,比如添加特殊标记、记录日志等:
578+
579+
```typescript
580+
// src/config/config.default.ts
581+
export default {
582+
i18n: {
583+
missingKeyHandler: (message, options) => {
584+
// 记录缺失的翻译键
585+
console.warn(`Missing translation: ${message}`, options);
586+
587+
// 根据不同的组返回不同的格式
588+
const group = options?.group || 'default';
589+
const locale = options?.locale || 'default';
590+
591+
return `[${group}:${locale}:${message}]`;
592+
}
593+
}
594+
}
595+
```
558596

559597

560598
## 常用语言

site/i18n/en/docusaurus-plugin-content-docs/current/extensions/i18n.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,48 @@ export default {
493493
},
494494
},
495495
localsField: 'i18n',
496+
// Handler for missing translation keys; returns original key by default
497+
missingKeyHandler: (message, options) => message,
498+
}
499+
}
500+
```
501+
502+
503+
504+
### Missing translation key handling
505+
506+
When a translation key cannot be found in any locale files, the i18n component calls the `missingKeyHandler` function to handle it.
507+
508+
By default, when a translation is missing, the original key is returned:
509+
510+
```typescript
511+
// Default missingKeyHandler
512+
missingKeyHandler: (message, options) => message
513+
```
514+
515+
For example:
516+
```typescript
517+
// Suppose "unknown_key" does not exist in any locale files
518+
this.i18nService.translate('unknown_key');
519+
// Returns: "unknown_key"
520+
```
521+
522+
You can customize how missing keys are handled, such as adding special markers or logging:
523+
524+
```typescript
525+
// src/config/config.default.ts
526+
export default {
527+
i18n: {
528+
missingKeyHandler: (message, options) => {
529+
// Log the missing translation key
530+
console.warn(`Missing translation: ${message}`, options);
531+
532+
// Return different formats based on group and locale
533+
const group = options?.group || 'default';
534+
const locale = options?.locale || 'default';
535+
536+
return `[${group}:${locale}:${message}]`;
537+
}
496538
}
497539
}
498540
```

0 commit comments

Comments
 (0)