dojo 龙形主标识

国际化 Dojo 应用程序

配置支持的应用程序语言环境

国际化应用程序应在其 .dojorc 构建配置文件中指定所有支持的语言环境。一个语言环境应被指定为应用程序的主要/默认语言环境,其余支持的语言环境作为在需要时可以激活的辅助选项。这是通过 build-app 部分中的 locale 属性和 supportedLocales 列表完成的。

注意:由于各种格式化程序和解析器依赖于特定于语言环境的 CLDR 数据,@dojo/framework/i18n 提供的大多数功能都需要在 .dojorc 中至少设置一个 locale 才能正常工作。例如,如果未指定默认 locale,则只会返回默认的包消息,并且 ICU 消息格式化 将被禁用。

  • locale:字符串
    • 应用程序支持的主要语言环境。也就是说,如果未指定覆盖语言环境,将使用的默认语言。
  • supportedLocales:字符串[]
    • 应用程序支持的其他语言环境列表。这些语言环境需要被激活以覆盖默认的 locale,无论是通过在客户端运行时应用程序用户的语言设置,在服务器端运行时进程或主机的语言设置,还是 在应用程序本身中显式指定

例如,使用以下配置,应用程序指定其默认语言环境为英语 (en),并且支持西班牙语 (es) 和法语 (fr) 作为其他语言环境选择

.dojorc

{
	"build-app": {
		"locale": "en",
		"supportedLocales": ["es", "fr"]
	}
}

创建支持国际化的小部件

可以使用 @dojo/framework/core/middleware/i18n 中的 i18n 中间件来国际化单个小部件。使用中间件会向小部件属性接口添加一些可选的与国际化相关的属性。i18n 中间件的 API 包括一个方法 localize(bundle),用于根据消息包获取本地化的 nls 值,以及两个可用于获取和设置应用程序语言环境详细信息的方法。

i18n 小部件属性

  • locale?: 字符串
    • 小部件的语言环境。如果未指定,则假定 根应用程序语言环境其覆盖。如果指定,则小部件的 DOM 节点将具有设置为语言环境的 lang 属性。
  • rtl?: 布尔值
    • 一个可选标志,指示小部件的文本方向。如果为 true,则底层 DOM 节点的 dir 属性将设置为 "rtl"。如果为 false,则 dir 属性将设置为 "ltr"。否则,该属性不会被设置。
  • i18nBundle?: Bundle<Messages> | Map<Bundle<Messages>, Bundle<Messages>>
    • 对传递给 localizeBundle 方法的 默认语言包 的可选覆盖。如果覆盖包含 messages 对象,则它将完全替换小部件可能正在使用的底层默认语言包。如果覆盖仅包含 locales 对象,则将使用覆盖中指定的附加语言环境加载器创建一个新包。

i18n localize() 方法

小部件可以将其 默认语言包 传递到 localize 方法中,以根据小部件的 locale 属性对包进行适当的本地化。

如果包支持小部件的当前语言环境,但这些特定于语言环境的消息尚未加载,则将返回一个空白消息值的包。或者,localize 方法接受第二个布尔值参数,当为 true 时,会导致返回默认消息而不是空白包。一旦特定于语言环境的消息加载完毕,小部件将被失效,并触发重新渲染,显示本地化的消息内容。

localize 返回的对象包含以下属性和方法

  • messages
    • 包含本地化消息键值对的对象。如果消息尚未加载,则 messages 将是空白包或默认消息,具体取决于 localize 的调用方式。
  • isPlaceholder
    • 一个布尔值属性,指示返回的消息是实际的特定于语言环境的消息 (false) 还是在等待本地化消息完成加载时使用的占位符 (true)。这对于在本地化消息尚未加载时阻止小部件完全渲染很有用。
  • format(key: string, replacements: { [key: string]: string })
    • 一个方法,接受消息键作为其第一个参数,并接受替换值的数组作为其第二个参数。例如,如果包包含 greeting: 'Hello, {name}!',则调用 format('greeting', { name: 'World' }) 将返回 'Hello, World!'

使用 localize 返回的所有功能的示例

nls/en/MyI18nWidget.ts

export default {
	messages: {
		hello: 'Welcome to the shop',
		purchaseItems: 'Please confirm your purchase',
		itemCount: 'Purchase {count} items'
	}
};

widgets/MyI18nWidget.tsx

import { create, tsx } from '@dojo/framework/core/vdom';
import i18n from '@dojo/framework/core/middleware/i18n';
import Label from '@dojo/widgets/label';
import Button from '@dojo/widgets/button';

import greetingsBundle from '../nls/en/MyI18nWidget';

const factory = create({ i18n });

export default factory(function MyI18nWidget({ middleware: { i18n } }) {
	// Load the "greetings" messages for the current locale. If the locale-specific
	// messages have not been loaded yet, then the default messages are returned,
	// and the widget will be invalidated once the locale-specific messages have
	// loaded.
	const { format, isPlaceholder, messages } = i18n.localize(greetingsBundle);

	// In many cases it makes sense to postpone rendering until the locale-specific messages have loaded,
	// which can be accomplished by returning early if `isPlaceholder` is `true`.
	if (isPlaceholder) {
		return;
	}

	return v('div', { title: messages.hello }, [
		w(Label, {}, [
			// Passing a message string to a child widget.
			messages.purchaseItems
		]),
		w(Button, {}, [
			// Passing a formatted message string to a child widget.
			format('itemCount', { count: 2 })
		])
	]);
});

请注意,使用这种模式,小部件可以从多个包中获取其消息。但是,为了简单起见,建议小部件尽可能地限制为单个包。

面向类的小部件的 I18nMixin

可以通过添加 @dojo/framework/core/mixins/I18n 中的 I18nMixin 混合器来国际化单个面向类的小部件。此混合器添加了与 i18n 中间件相同的可选与国际化相关的小部件属性,并提供了一个 localizeBundle 方法,用于将导入的消息包本地化为小部件的当前语言环境。

localizeBundle() 方法

小部件可以将其 默认语言包 传递到 localizeBundle 方法中,以根据小部件的 locale 属性对包进行适当的本地化。

如果包支持小部件的当前语言环境,但这些特定于语言环境的消息尚未加载,则将返回一个空白消息值的包。或者,localizeBundle 方法接受第二个布尔值参数,当为 true 时,会导致返回默认消息而不是空白包。一旦特定于语言环境的消息加载完毕,小部件将被失效,并触发重新渲染,显示本地化的消息内容。

localizeBundle 返回的对象包含以下属性和方法

  • messages
    • 包含本地化消息键值对的对象。如果消息尚未加载,则 messages 将是空白包或默认消息,具体取决于 localizeBundle 的调用方式。
  • isPlaceholder
    • 一个布尔值属性,指示返回的消息是实际的特定于语言环境的消息 (false) 还是在等待本地化消息完成加载时使用的占位符 (true)。这对于在本地化消息尚未加载时阻止小部件完全渲染很有用。
  • format(key: string, replacements: { [key: string]: string })
    • 一个方法,接受消息键作为其第一个参数,并接受替换值的数组作为其第二个参数。例如,如果包包含 greeting: 'Hello, {name}!',则调用 format('greeting', { name: 'World' }) 将返回 'Hello, World!'

使用 localizeBundle 返回的所有功能的示例

nls/en/MyI18nWidget.ts

export default {
	messages: {
		hello: 'Welcome to the shop',
		purchaseItems: 'Please confirm your purchase',
		itemCount: 'Purchase {count} items'
	}
};

widgets/MyI18nWidget.ts

import WidgetBase from '@dojo/framework/core/WidgetBase';
import { v, w } from '@dojo/framework/core/vdom';
import I18nMixin from '@dojo/framework/core/mixins/I18n';
import Label from '@dojo/widgets/label';
import Button from '@dojo/widgets/button';

import greetingsBundle from '../nls/en/MyI18nWidget';

export default class MyI18nWidget extends I18nMixin(WidgetBase) {
	render() {
		// Load the "greetings" messages for the current locale. If the locale-specific
		// messages have not been loaded yet, then the default messages are returned,
		// and the widget will be invalidated once the locale-specific messages have
		// loaded.
		const { format, isPlaceholder, messages } = this.localizeBundle(greetingsBundle);

		// In many cases it makes sense to postpone rendering until the locale-specific messages have loaded,
		// which can be accomplished by returning early if `isPlaceholder` is `true`.
		if (isPlaceholder) {
			return;
		}

		return v('div', { title: messages.hello }, [
			w(Label, {}, [
				// Passing a message string to a child widget.
				messages.purchaseItems
			]),
			w(Button, {}, [
				// Passing a formatted message string to a child widget.
				format('itemCount', { count: 2 })
			])
		]);
	}
}

向支持国际化的小部件提供语言环境数据

当应用程序使用支持国际化的面向类的小部件(特别是使用 I18nMixin 的小部件)时,语言环境详细信息也需要通过 Dojo 注册表进行管理。这适用于应用程序本身或作为外部依赖项的一部分包含的任何此类小部件 - 包括从 Dojo 的 @dojo/widgets 套件中使用的任何小部件。语言环境数据通过 Dojo 注册表系统注入到所有此类小部件中;当应用程序语言环境发生更改时,这些小部件将被失效并重新渲染,使用更新的语言环境数据。

此机制通过 registerI18nInjector 启用,这是 @dojo/framework/core/mixins/I18n 提供的一个便捷方法。调用此方法将在特定注册表实例中注册 i18n 注入器。通常,这在应用程序引导时完成,其中 i18n 注入器针对传递给 renderer.mount() 方法的全局注册表进行注册。

main.ts

import renderer from '@dojo/framework/core/vdom';
import { w } from '@dojo/framework/core/vdom';
import Registry from '@dojo/framework/core/Registry';
import { registerI18nInjector } from '@dojo/framework/core/mixins/I18n';

import App from './App';

const registry = new Registry();
registerI18nInjector({ locale: 'us', rtl: false }, registry);

const r = renderer(() => w(App, {}));
r.mount({ registry });

更改语言环境

i18n 中间件可用于更改应用程序的区域设置。调用 i18n.set({ locale: string, rtl: boolean }); 将把新的区域设置传播到所有使用 i18n 中间件的小部件,以及任何使用 I18nMixin 的小部件(假设 registerI18nInjector 已在应用程序中设置)。

示例用法

以下示例展示了一个支持 i18n 的小部件,它渲染两个按钮,允许在英语和法语之间切换应用程序的区域设置。

import { create, tsx } from '@dojo/framework/core/vdom';
import i18n from '@dojo/framework/core/middleware/i18n';

import nlsBundle from '../nls/main';

const factory = create({ i18n });

export default factory(function LocaleChanger({ middleware: { i18n } }) {
	const { messages } = localize(nlsBundle);
	return (
		<div>
			<button
				onclick={() => {
					i18n.set({ locale: 'en' });
				}}
			>
				English
			</button>
			<button
				onclick={() => {
					i18n.set({ locale: 'fr' });
				}}
			>
				French
			</button>
			<div>{messages.greetings}</div>
		</div>
	);
});

覆盖每个小部件的区域设置和捆绑包

使用 i18n 中间件或 I18nMixin 的小部件可以在由父级实例化时覆盖其 i18n 小部件属性。当在一个应用程序中渲染多个具有不同区域设置的小部件(即在一个应用程序中使用多个区域设置)时,这很有用,以及覆盖第三方小部件可能使用的消息集并在应用程序的上下文中对齐它们。

每个支持 i18n 的小部件都可以通过提供 locale 小部件属性来拥有自己的独立区域设置。如果没有设置 locale 属性,则假定使用 默认区域设置

还可以通过传递 i18nBundle 小部件属性来替换小部件的默认捆绑包。Dojo 建议不要在一个小部件中使用多个捆绑包,但有时应用程序可能需要使用一个使用多个捆绑包的第三方小部件。因此,i18nBundle 也可以是用于覆盖捆绑包的默认捆绑包的 Map

在子小部件中覆盖捆绑包的示例

import { Bundle } from '@dojo/framework/i18n/i18n';

// A complete bundle to replace WidgetA's message bundle
import overrideBundleForWidgetA from './nls/widgetA';

// Bundles for WidgetB
import widgetB1 from 'third-party/nls/widgetB1';
import overrideBundleForWidgetB from './nls/widgetB';

// WidgetB uses multiple bundles, but only `thirdy-party/nls/widgetB1` needs to be overridden
const overrideMapForWidgetB = new Map<Bundle<any>, Bundle<any>>();
map.set(widgetB1, overrideBundleForWidgetB);

export class MyWidget extends WidgetBase {
	protected render() {
		return [
			w(WidgetA, {
				i18nBundle: overrideBundleForWidgetA
			}),
			w(WidgetB, {
				i18nBundle: overrideMapForWidgetB
			}),
			// This example partially overrides the overrideKey value in the widgetB1 bundle
			w(WidgetC, {
				i18nBundle: { ...widgetB1, { overrideKey: 'abc' }}
			})
		];
	}
}

默认区域设置

支持 i18n 的小部件 将使用的区域设置将根据应用程序使用哪些 i18n 功能,按照以下顺序确定,直到找到一个值。

顺序 I18n 功能 区域设置设置
1 I18nMixin/i18n 中间件 通过 小部件的 locale 属性 提供的显式覆盖。
2 I18nMixin/i18n 中间件和 i18n 注入器 已在应用程序中 选择或更改的区域设置
3 I18nMixin/i18n 中间件和 i18n 注入器 最初注册 i18n 注入器 时设置的默认区域设置。
4 .dojorc 用户的当前区域设置,例如他们的浏览器语言设置,如果该区域设置 在应用程序的 build-app.supportedLocales 列表中
5 .dojorc 应用程序在 build-app.locale指定的默认区域设置
6 @dojo/framework/i18n 通过 Dojo i18n 的 switchLocale 方法 设置的显式区域设置。
7 @dojo/framework/i18n 当前执行环境的 systemLocale