在 Angular 2 中动态更改 DatePipe 的语言环境
Dynamically change locale for DatePipe in Angular 2
我正在制作一个 Angular 项目,用户可以在其中切换语言。是否可以使语言环境动态化?
我看到您可以将它添加到 NgModule 中,但我猜当我把它放在那里时它不是动态的?或者我可以通过服务或其他方式以某种方式更改它吗?
使用 providers
您可以更改 NgModule
中的默认语言环境。
为此,您需要从 angular/core 导入 LOCALE_ID 并获取您的区域设置语言以将其传递给提供商。
import { LOCALE_ID } from '@angular/core';
@NgModule({
imports: [//your imports],
providers: [
{ provide: LOCALE_ID, useValue: "en-US" }
]
})
...
...
{
provide: LOCALE_ID,
deps: [SettingsService], //some service handling global settings
useFactory: (settingsService) => settingsService.getLanguage() //returns locale string
}
希望对您有所帮助。
要从服务中设置语言环境,您需要将 LOCALE_ID
带有工厂的提供程序添加到 app.module
,就像@AmolBhor 中的回答
{
provide: LOCALE_ID,
deps: [SettingsService], //some service handling global settings
useFactory: (settingsService) => settingsService.getLanguage() //returns locale string
}
很遗憾,您无法更改 DatePipe JIT 的语言。 Angular 编译器在引导过程中需要 LOCALE_ID
。
Angular 有一些错误报告:
- https://github.com/angular/angular/issues/15039(关闭-不
已解决)
- https://github.com/angular/angular/issues/16960(关闭
解决方法在评论中)
有几种解决方法:
解决方法#1
Re-bootstrapping angular 模块:
let _platformRef: NgModuleRef<Object>;
if(_platformRef) { _platformRef.destroy(); }
platformBrowserDynamic(providers)
.bootstrapModule(AppModule, {providers})
.then(platformRef => {
_platformRef = platformRef;
})
*这不适用于 Hybrid Angular/AngularJS,因为无法使用 UpgradeModule 破坏 AngularJS。
解决方法#2
要覆盖 DatePipe、NumberPipe - 无论您需要什么:
@Pipe({name: 'datepipe', pure: true})
export class MyDatePipe implements PipeTransform {
transform(value: any, pattern?: string): string | null {
// transform value as you like (you can use moment.js and format by locale_id from your custom service)
return DateUtils.format(value);
}
}
解决方法 #3
要使用已经通过自定义管道处理本地化的库,例如:
解决方法 #4
每个使用 LOCALE_ID
的管道都有私有字段 locale 或 _locale,因此您可以在该管道上覆盖此字段关于语言更改,因为有一个管道实例。
这会起作用,因为 TypeScript 只是 JavaScript 的语法糖。 JavaScript 中没有私有字段。
还记得使用 ApplicationRef 中的 tick()
方法处理应用程序中的变更检测。
@Injectable()
export class DynamicLocaleService {
private i18nPipes: PipeTransform[];
constructor(
datePipe: DatePipe,
currencyPipe: CurrencyPipe,
decimalPipe: DecimalPipe,
percentPipe: PercentPipe,
private applicationRef: ApplicationRef,
) {
this.i18nPipes = [
datePipe,
currencyPipe,
decimalPipe,
percentPipe,
]
}
setLocale(lang: string): void {
this.i18nPipes.forEach(pipe => {
if(pipe.hasOwnProperty("locale")) {
pipe["locale"] = lang;
} else if (pipe.hasOwnProperty("_locale")) {
pipe["_locale"] = lang
}
})
this.applicationRef.tick()
}
}
解决方法 #5
更改语言时重新加载应用程序。
window.location.reload()
不幸的是,以上所有都是解决方法。
但还有另一种解决方案 - 您可以为每种语言设置多个捆绑包,这可能是更好的方法,因为应用程序会更快。但是这个解决方案并不适用于所有应用程序,也没有回答问题。
让您的服务像
import { Injectable } from '@angular/core';
@Injectable()
export class LocaleService {
//Chosse Locale From This Link
//https://github.com/angular/angular/tree/master/packages/common/locales
constructor() { }
private _locale: string;
set locale(value: string) {
this._locale = value;
}
get locale(): string {
return this._locale || 'en-US';
}
public registerCulture(culture: string) {
debugger;
if (!culture) {
return;
}
switch (culture) {
case 'en-uk': {
this._locale = 'en';
console.log('Application Culture Set to English');
break;
}
case 'zh-hk': {
this._locale = 'zh-Hant';
console.log('Application Culture Set to Traditional Chinese');
break;
}
case 'zh-cn': {
this._locale = 'zh-Hans';
console.log('Application Culture Set to Simplified Chinese');
break;
}
default: {
this._locale = 'en';
console.log('Application Culture Set to English');
break;
}
}
}
}
并在 App.module.ts
首先导入你需要的本地化,比如说
import localeEN from '@angular/common/locales/en';
import localezhHant from '@angular/common/locales/zh-Hant';
import localezhHans from '@angular/common/locales/zh-Hans';
比供应商部分
{
provide: LOCALE_ID,
deps: [LocaleService],
useFactory: (LocaleService: { locale: string; }) => LocaleService.locale
}
最后
registerLocaleData(localeEN);
registerLocaleData(localezhHant);
registerLocaleData(localezhHans);
希望对大家有所帮助
如果您想动态更改区域设置,请在您想要的组件中注入 LocaleService 并使用 registerCulture 方法,将您需要的区域性传递给此
这里已经提供了很好的答案!但是,它并不能完全满足我在混合 AngularJs/Angular 环境中工作的场景。这是我的解决方案,其中包括先前答案的各个方面,以及使用动态导入导入语言环境的替代方法,通过延迟加载使捆绑更加高效。
总结:
要点(也包含在以前的答案中)
LOCALE_ID
通过 useFactory
选项 在 app.module.ts
提供程序中使用本地化服务进行配置
registerLocaleData
函数全局注册地区数据
扩展实施点(未包含在以前的答案中)
registerLocaleData
函数需要导入语言环境,在以前的答案中包含硬编码并导致每个语言环境的捆绑:
`import localeEN from '@angular/common/locales/en';
我们可以使用动态加载(自 TypeScript 2.4 起可用)按需加载给定的语言环境,从而使我们的代码和捆绑更加高效。 import
returns 一个 Promise 然后我们可以注册我们的语言环境:
import(`@angular/common/locales/${localeId}.js`)
.then(lang => registerLocaleData(lang.default));
为了进一步改进捆绑,我们可以添加一些神奇的注释来限制我们仅支持的语言环境:
/* webpackInclude: /(en|fr|es)\.js$/ */
要利用动态导入,我们必须将模块类型配置为 esnext
,请参阅 tsconfig.json
您可以在此处阅读有关动态导入和 webpack 魔术注释的信息:https://webpack.js.org/api/module-methods/#dynamic-expressions-in-import
代码:
app.module.ts
@NgModule({
declarations: [ /* ... */ ],
imports: [ /* ... */ ],
providers: [
{ provide: LOCALE_ID, deps: [LocalizationService], useFactory: (localizationService) => localizationService.getLocale() }
]
})
localization.service.ts
export class LocalizationService {
/**
* Gets the current locale (abbreviation)
*/
getLocale() : string {
return localStorage.getItem("current-locale");
}
/**
* Set the locale across the app
* @param {string} abbr Abbreviation of the locale to set
*/
setLocale(abbr : string) : Promise<any> {
return new Promise(resolve => {
return this.http.get<Translation[]>(url)
.subscribe((response) => {
//code ommited to load response data into translation cache
if (localStorage) {
localStorage.setItem("current-locale", abbr);
}
moment.locale(abbr);
this.loadLocale(abbr).then;
resolve();
},
(error) => {
resolve;
});
});
}
/**
* Imports the Angular data for the given locale
* @param {string} localeId The locale to load data
*/
private loadLocale(localeId : string) : Promise<any> {
localeId = localeId.substr(0, 2);
//limit loading to the languages defined in webpack comment below
return import(
/* webpackInclude: /(en|fr|es)\.js$/ */
`@angular/common/locales/${localeId}.js`
).then(lang =>
registerLocaleData(lang.default)
);
}
}
tsconfig.json
"compilerOptions": {
/* ... */
"module": "esnext"
/* ... */
}
我的最终解决方案是:https://github.com/armanozak/angular-dynamic-locale。尝试使用 Angular 12 并允许动态更改 LOCALE_ID 而无需重新加载应用程序。
我正在制作一个 Angular 项目,用户可以在其中切换语言。是否可以使语言环境动态化?
我看到您可以将它添加到 NgModule 中,但我猜当我把它放在那里时它不是动态的?或者我可以通过服务或其他方式以某种方式更改它吗?
使用 providers
您可以更改 NgModule
中的默认语言环境。
为此,您需要从 angular/core 导入 LOCALE_ID 并获取您的区域设置语言以将其传递给提供商。
import { LOCALE_ID } from '@angular/core';
@NgModule({
imports: [//your imports],
providers: [
{ provide: LOCALE_ID, useValue: "en-US" }
]
})
...
...
{
provide: LOCALE_ID,
deps: [SettingsService], //some service handling global settings
useFactory: (settingsService) => settingsService.getLanguage() //returns locale string
}
希望对您有所帮助。
要从服务中设置语言环境,您需要将 LOCALE_ID
带有工厂的提供程序添加到 app.module
,就像@AmolBhor 中的回答
{
provide: LOCALE_ID,
deps: [SettingsService], //some service handling global settings
useFactory: (settingsService) => settingsService.getLanguage() //returns locale string
}
很遗憾,您无法更改 DatePipe JIT 的语言。 Angular 编译器在引导过程中需要 LOCALE_ID
。
Angular 有一些错误报告:
- https://github.com/angular/angular/issues/15039(关闭-不 已解决)
- https://github.com/angular/angular/issues/16960(关闭 解决方法在评论中)
有几种解决方法:
解决方法#1
Re-bootstrapping angular 模块:
let _platformRef: NgModuleRef<Object>;
if(_platformRef) { _platformRef.destroy(); }
platformBrowserDynamic(providers)
.bootstrapModule(AppModule, {providers})
.then(platformRef => {
_platformRef = platformRef;
})
*这不适用于 Hybrid Angular/AngularJS,因为无法使用 UpgradeModule 破坏 AngularJS。
解决方法#2
要覆盖 DatePipe、NumberPipe - 无论您需要什么:
@Pipe({name: 'datepipe', pure: true})
export class MyDatePipe implements PipeTransform {
transform(value: any, pattern?: string): string | null {
// transform value as you like (you can use moment.js and format by locale_id from your custom service)
return DateUtils.format(value);
}
}
解决方法 #3
要使用已经通过自定义管道处理本地化的库,例如:
解决方法 #4
每个使用 LOCALE_ID
的管道都有私有字段 locale 或 _locale,因此您可以在该管道上覆盖此字段关于语言更改,因为有一个管道实例。
这会起作用,因为 TypeScript 只是 JavaScript 的语法糖。 JavaScript 中没有私有字段。
还记得使用 ApplicationRef 中的 tick()
方法处理应用程序中的变更检测。
@Injectable()
export class DynamicLocaleService {
private i18nPipes: PipeTransform[];
constructor(
datePipe: DatePipe,
currencyPipe: CurrencyPipe,
decimalPipe: DecimalPipe,
percentPipe: PercentPipe,
private applicationRef: ApplicationRef,
) {
this.i18nPipes = [
datePipe,
currencyPipe,
decimalPipe,
percentPipe,
]
}
setLocale(lang: string): void {
this.i18nPipes.forEach(pipe => {
if(pipe.hasOwnProperty("locale")) {
pipe["locale"] = lang;
} else if (pipe.hasOwnProperty("_locale")) {
pipe["_locale"] = lang
}
})
this.applicationRef.tick()
}
}
解决方法 #5
更改语言时重新加载应用程序。
window.location.reload()
不幸的是,以上所有都是解决方法。
但还有另一种解决方案 - 您可以为每种语言设置多个捆绑包,这可能是更好的方法,因为应用程序会更快。但是这个解决方案并不适用于所有应用程序,也没有回答问题。
让您的服务像
import { Injectable } from '@angular/core';
@Injectable()
export class LocaleService {
//Chosse Locale From This Link
//https://github.com/angular/angular/tree/master/packages/common/locales
constructor() { }
private _locale: string;
set locale(value: string) {
this._locale = value;
}
get locale(): string {
return this._locale || 'en-US';
}
public registerCulture(culture: string) {
debugger;
if (!culture) {
return;
}
switch (culture) {
case 'en-uk': {
this._locale = 'en';
console.log('Application Culture Set to English');
break;
}
case 'zh-hk': {
this._locale = 'zh-Hant';
console.log('Application Culture Set to Traditional Chinese');
break;
}
case 'zh-cn': {
this._locale = 'zh-Hans';
console.log('Application Culture Set to Simplified Chinese');
break;
}
default: {
this._locale = 'en';
console.log('Application Culture Set to English');
break;
}
}
}
}
并在 App.module.ts
首先导入你需要的本地化,比如说
import localeEN from '@angular/common/locales/en';
import localezhHant from '@angular/common/locales/zh-Hant';
import localezhHans from '@angular/common/locales/zh-Hans';
比供应商部分
{
provide: LOCALE_ID,
deps: [LocaleService],
useFactory: (LocaleService: { locale: string; }) => LocaleService.locale
}
最后
registerLocaleData(localeEN);
registerLocaleData(localezhHant);
registerLocaleData(localezhHans);
希望对大家有所帮助
如果您想动态更改区域设置,请在您想要的组件中注入 LocaleService 并使用 registerCulture 方法,将您需要的区域性传递给此
这里已经提供了很好的答案!但是,它并不能完全满足我在混合 AngularJs/Angular 环境中工作的场景。这是我的解决方案,其中包括先前答案的各个方面,以及使用动态导入导入语言环境的替代方法,通过延迟加载使捆绑更加高效。
总结:
要点(也包含在以前的答案中)
LOCALE_ID
通过useFactory
选项 在 registerLocaleData
函数全局注册地区数据
app.module.ts
提供程序中使用本地化服务进行配置
扩展实施点(未包含在以前的答案中)
registerLocaleData
函数需要导入语言环境,在以前的答案中包含硬编码并导致每个语言环境的捆绑:`import localeEN from '@angular/common/locales/en';
我们可以使用动态加载(自 TypeScript 2.4 起可用)按需加载给定的语言环境,从而使我们的代码和捆绑更加高效。
import
returns 一个 Promise 然后我们可以注册我们的语言环境:import(`@angular/common/locales/${localeId}.js`) .then(lang => registerLocaleData(lang.default));
为了进一步改进捆绑,我们可以添加一些神奇的注释来限制我们仅支持的语言环境:
/* webpackInclude: /(en|fr|es)\.js$/ */
要利用动态导入,我们必须将模块类型配置为
esnext
,请参阅tsconfig.json
您可以在此处阅读有关动态导入和 webpack 魔术注释的信息:https://webpack.js.org/api/module-methods/#dynamic-expressions-in-import
代码:
app.module.ts
@NgModule({
declarations: [ /* ... */ ],
imports: [ /* ... */ ],
providers: [
{ provide: LOCALE_ID, deps: [LocalizationService], useFactory: (localizationService) => localizationService.getLocale() }
]
})
localization.service.ts
export class LocalizationService {
/**
* Gets the current locale (abbreviation)
*/
getLocale() : string {
return localStorage.getItem("current-locale");
}
/**
* Set the locale across the app
* @param {string} abbr Abbreviation of the locale to set
*/
setLocale(abbr : string) : Promise<any> {
return new Promise(resolve => {
return this.http.get<Translation[]>(url)
.subscribe((response) => {
//code ommited to load response data into translation cache
if (localStorage) {
localStorage.setItem("current-locale", abbr);
}
moment.locale(abbr);
this.loadLocale(abbr).then;
resolve();
},
(error) => {
resolve;
});
});
}
/**
* Imports the Angular data for the given locale
* @param {string} localeId The locale to load data
*/
private loadLocale(localeId : string) : Promise<any> {
localeId = localeId.substr(0, 2);
//limit loading to the languages defined in webpack comment below
return import(
/* webpackInclude: /(en|fr|es)\.js$/ */
`@angular/common/locales/${localeId}.js`
).then(lang =>
registerLocaleData(lang.default)
);
}
}
tsconfig.json
"compilerOptions": {
/* ... */
"module": "esnext"
/* ... */
}
我的最终解决方案是:https://github.com/armanozak/angular-dynamic-locale。尝试使用 Angular 12 并允许动态更改 LOCALE_ID 而无需重新加载应用程序。