在 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 有一些错误报告:

有几种解决方法:

解决方法#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 而无需重新加载应用程序。