i18n
Wizard provides internationalization support across three levels: core
, cli
, and plugins
. Each level is loaded in different namespaces, so you need to use the appropriate namespace based on your specific use case.
-
The core namespace is primarily used for error messages and log information within the wizard core. It does not support external extensions.
-
The cli namespace is used for information required when initializing a wizard instance, such as wizard descriptions and versions.
-
The plugins namespace is used for plugin internationalization. When creating custom plugins and loading commands, all internationalization-related information is loaded in the plugins namespace.
Wizard’s internationalization is built on top of @hyperse/translator
.
Through TypeScript interface merging, different plugins and loading processes
can achieve excellent type support.
Automatic Fallback Mechanism
Wizard provides a robust fallback mechanism for internationalization:
- Language Fallback: When a message is not found in the current locale, Wizard automatically falls back to English (
en
) messages - English as Default: You only need to define English messages by default - other languages will automatically fall back to English if not configured
- Message Validation: The translator uses
plainMessageCheck
to filter special characters using thePlainMessageRegex
pattern/[!\/\<\|\{]/
This ensures that your application always has fallback text available, even when translations are incomplete.
How Fallback Works
Wizard implements a three-tier fallback system:
// Fallback Priority:
// 1. Primary Locale (e.g., 'zh')
// 2. English Fallback ('en')
// 3. Key Fallback (e.g., 'zh.command.[messageKey]')
Implementation Details
The fallback mechanism is implemented in the useLocale
function:
export const useLocale = (
locale: SupportedLocales,
messages: LocaleMessagesObject = coreMessages,
logger?: Logger
): I18n['t'] => {
// Create fallback translator for English
const fallbackTranslator = createFallbackTranslator(messages, logger);
return createTranslator({
locale: locale,
messages: messages,
namespace: locale,
getMessageFallback: ({ error, key, namespace }) => {
if (namespace === 'en') {
return `${namespace}.${key}`;
}
// Fall back to English translator
return fallbackTranslator(key);
},
// ... other options
});
};
Message Validation
Wizard automatically validates messages using the plainMessageCheck
function to ensure consistency and prevent formatting issues.
Validation Rules
The PlainMessageRegex
pattern /[!\/\<\|\{]/
filters out special characters that could cause issues:
!
- Exclamation marks/
- Forward slashes<
- Less than symbols|
- Pipe characters{
- Opening braces
Why Validation Matters
- Consistency: Ensures all messages follow the same format
- Reliability: Prevents runtime errors from malformed messages
- Maintainability: Makes message management easier for teams
- User Experience: Provides consistent output across different locales
core namespace
The built-in internationalization messages are as follows:
export const messages = {
en: {
core: {
command: {
notProvider:
'No command specified. Please provide a command to execute.',
nameConflict:
'Command name "{newCmdName}" conflicts with existing command "{oldCmdName}". This may be caused by duplicate aliases.',
flagNotProvided:
'Command "{cmdName}" requires the flag "{flagName}" but it was not provided.',
notConfiguration: 'Command "{cmdName}" is not properly configured.',
notFound:
'Command "{cmdName}" not found. Use --help to see available commands.',
processNotFound: 'Command "{cmdName}" has no process defined.',
invalidName:
'Invalid command name "{cmdName}" command names cannot contain spaces or multiple consecutive spaces.',
invalidFlagsValue:
'Invalid value "{flagValue}" for flag "{flagName}". Supported values are: {flagValues}',
},
flags: {
noColor: 'Disable colored output in terminal',
logLevel: 'Set log level. options: error, warn, info, debug, verbose',
},
},
},
zh: {
core: {
command: {
notProvider: '未指定命令。请提供一个要执行的命令。',
flagNotProvided: '命令 "{cmdName}" 需要参数 "{flagName}" 但未提供。',
nameConflict:
'命令名称 "{newCmdName}" 与现有命令 "{oldCmdName}" 冲突。这可能是由重复的别名导致的。',
notConfiguration: '命令 "{cmdName}" 配置不正确。',
notFound: '未找到命令 "{cmdName}"。使用 --help 查看可用命令。',
processNotFound: '命令 "{cmdName}" 未定义处理函数。',
invalidName:
'无效的命令名称 "{cmdName}" 命令名称不能包含空格或连续多个空格。',
invalidFlagsValue:
'无效的参数值 "{flagValue}",参数 "{flagName}" 支持的值为:{flagValues}',
},
flags: {
noColor: '禁用终端日志输出颜色',
logLevel: '设置日志级别,可选值:error, warn, info, debug, verbose',
},
},
},
};
cli namespace
Messages
// @filename: @hyperse/wizard/dist/index.d.ts
import { } from '@hyperse/wizard';
export const = ({
: {
: {
: 'CLI description',
: 'CLI v1.0.0',
},
},
: {
: {
: '帮助信息插件',
: 'CLI v1.0.0',
},
},
});
Fallback Support: You can define only English messages and other languages will automatically fall back to English:
export const minimalMessages = defineLocaleMessages({
en: {
helpCli: {
description: 'CLI description',
version: 'CLI v1.0.0',
},
},
// zh is not defined - will automatically fall back to English
});
Type Merging
Use interface merging to combine your custom internationalization messages with CliLocaleMessages
.
// @filename: @hyperse/wizard/dist/index.d.ts
import type { } from '@hyperse/wizard';
declare module '@hyperse/wizard' {
export interface CliLocaleMessages
extends <typeof helpCliMessages> {}
}
Using
// @filename: @hyperse/wizard/dist/index.d.ts
import {
,
,
,
} from '@hyperse/wizard';
// define cli locale messages
const = ({
: {
: {
: 'CLI description',
: 'CLI v1.0.0',
},
},
: {
: {
: '帮助信息插件',
: 'CLI v1.0.0',
},
},
});
// merge cli locale messages
declare module '@hyperse/wizard' {
export interface CliLocaleMessages
extends <typeof > {}
}
const = ({
: 'hps_cli',
: ,
// Use internationalization messages
: 'cli.helpCli.- cli.helpCli.description
- cli.helpCli.version
description',
// Use internationalization messages
: 'cli.helpCli.version',
: () => {
.('CLI errorHandler \n', );
},
});
plugins namespace
Messages
Use interface merging to combine your custom internationalization messages with PluginLocaleMessages
.
// @filename: @hyperse/wizard/dist/index.d.ts
import { } from '@hyperse/wizard';
export const = ({
: {
: {
: 'CLI Help Plugin',
: {
: 'Show help information',
: 'cli --help or cli -h',
},
: {
: 'Show help information',
},
: {
: 'Name:',
: 'Version:',
},
},
},
: {
: {
: '帮助信息插件',
: {
: '展示帮助信息',
: 'cli --help 或 cli -h',
},
: {
: '展示帮助信息',
},
: {
: '名称:',
: '版本:',
},
},
},
});
Type Merging
// @filename: @hyperse/wizard/dist/index.d.ts
import type { } from '@hyperse/wizard';
declare module '@hyperse/wizard' {
export interface PluginLocaleMessages
extends <typeof helpMessages> {}
}
Using
// @filename: @hyperse/wizard/dist/index.d.ts
import {
,
,
PluginLocaleMessages,
,
,
} from '@hyperse/wizard';
// define plugin locale messages
const = ({
: {
: {
: 'CLI Help Plugin',
: {
: 'Show help information',
: 'cli --help or cli -h',
},
: {
: 'Show help information',
},
: {
: 'Name:',
: 'Version:',
},
},
},
: {
: {
: '帮助信息插件',
: {
: '展示帮助信息',
: 'cli --help 或 cli -h',
},
: {
: '展示帮助信息',
},
: {
: '名称:',
: '版本:',
},
},
},
});
// merge plugin locale messages
declare module '@hyperse/wizard' {
export interface PluginLocaleMessages
extends <typeof > {}
}
({
: ,
// Use internationalization messages
: 'plugins.helpPlugin.- plugins.helpPlugin.name
- plugins.helpPlugin.command.description
- plugins.helpPlugin.command.example
- plugins.helpPlugin.flags.help
- plugins.helpPlugin.message.name
- plugins.helpPlugin.message.version
name',
: (, ) => {
const = .(
('help', {
// Use internationalization messages
: 'plugins.helpPlugin.command.description',
// Use internationalization messages
: 'plugins.helpPlugin.command.example',
}).(() => {})
);
if (flag) {
.('help', {
: ,
// Use internationalization messages
: 'plugins.helpPlugin.fl- plugins.helpPlugin.flags.help
ags.help',
: false,
: 'h',
})
.(async (, ) => {});
}
return ;
},
});