【问题标题】:Dynamically change locale for DatePipe in Angular 2在 Angular 2 中动态更改 DatePipe 的语言环境
【发布时间】:2017-11-01 10:32:06
【问题描述】:

我正在制作一个用户可以切换语言的 Angular 项目。是否可以使语言环境动态化?

我看到你可以将它添加到 NgModule 中,但我猜当我把它放在那里时它不是动态的?或者我可以通过服务或其他方式改变它吗?

【问题讨论】:

    标签: angular locale angular2-pipe


    【解决方案1】:

    要从服务中设置区域设置,您需要将带有工厂的 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

    重新引导角度模块:

    let _platformRef: NgModuleRef<Object>;
    if(_platformRef) { _platformRef.destroy(); }
    platformBrowserDynamic(providers)
        .bootstrapModule(AppModule, {providers})
        .then(platformRef => {
            _platformRef = platformRef;
        })
    

    *这不适用于混合 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()
    

    不幸的是,以上所有方法都是解决方法。

    但还有另一种解决方案 - 您可以为每种语言提供多个捆绑包,这可能是更好的方法,因为应用程序会更快。但此解决方案并非适用于所有应用程序,也无法回答问题。

    【讨论】:

    • 所有语言环境都必须导入并注册到 main.ts 文件中:``` import { registerLocaleData } from '@angular/common';从“@angular/common/locales/en”导入 locale_EN;从“@angular/common/locales/ru”导入 locale_RU; registerLocaleData(locale_EN); registerLocaleData(locale_RU); ```
    • 嗯,我想使用解决方法 #4,但它不起作用:即使我在 ngOnInit 上使用 setLocale 函数,在我看来,它也不会将货币从美元更改为欧元。 Angular 7. 我错过了什么吗?在该代码之外,我执行了@artuska 提到的操作,并将管道作为提供程序添加到 sharedModule 中。区域设置已正确注册,因为我可以在管道本身内手动设置区域设置。
    • @CularBytes 语言环境不会改变您的货币,而是一种显示方式。要更改货币,请将适当的值传递给 CurrencyPipe。例如。在 html 中:{{ 数量 |货币:“欧元”}}
    • 感谢您回答 Anton,但我希望通过更改语言环境,它会从 0.50 美元变为 0.50 欧元(注意逗号)。当然我可以这样做,但在一个多国应用程序中,我正在寻找全球解决方案,并且仍然尽可能使用默认管道。
    • #4 的补充:如果它对您不起作用并且您正在使用 UI-Router,请尝试使用 StateService.reload() 而不是 applicationRef.Tick()
    【解决方案2】:

    使用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
    }
    

    希望这会对你有所帮助。

    【讨论】:

    • 不,不是,我想让“en-US”成为一个变量,以便在应用程序运行时更改它。
    • 因为你可以使用任何服务进行全局设置,然后添加提供者,请通过我已经更新的帖子。
    • 感谢您的回复,这正是我所需要的!
    • 此解决方案仅适用一次。 getLanguage() 只被调用一次。我可以让它在我的应用程序中的某些属性发生更改时再次调用 getLanguage 吗?
    • @MartijnvandenBergh 你找到解决方案了吗?我有同样的问题
    【解决方案3】:

    让你的服务喜欢

    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 方法,将你需要的文化传递给这个

    【讨论】:

      【解决方案4】:

      这里已经提供了很好的答案!但是,它并不能完全满足我在混合 AngularJs/Angular 环境中工作的场景。这是我的解决方案,其中包括先前答案的各个方面,以及使用动态导入导入语言环境的替代方法,通过延迟加载使捆绑更有效。

      总结:

      关键点(也包含在之前的答案中)

      • LOCALE_ID 通过useFactory 选项使用app.module.ts 提供程序中设置的本地化服务进行配置
      • registerLocaleData 函数在全局范围内注册语言环境数据

      扩展实施点(未包含在之前的答案中)

      • registerLocaleData 函数需要导入在之前的答案中包含硬编码的语言环境,并导致捆绑每个语言环境:

        `import localeEN from '@angular/common/locales/en';
        

        我们可以使用动态加载(从 TypeScript 2.4 开始提供)来按需加载给定的语言环境,从而使我们的代码和捆绑更高效。 import 返回一个 Promise,然后我们可以注册我们的语言环境:

        import(`@angular/common/locales/${localeId}.js`)
        .then(lang => registerLocaleData(lang.default));
        
      • 为了进一步改进捆绑,我们可以包含一些神奇的 cmets 以仅限于我们支持的语言环境:

        /* webpackInclude: /(en|fr|es)\.js$/ */

      • 要利用动态导入,我们必须将模块类型配置为esnext,请参阅tsconfig.json

      • 您可以在此处阅读有关动态导入和 webpack 魔术 cmets 的信息: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"
              /* ... */ 
          }
      

      【讨论】:

        【解决方案5】:

        对我来说最终的解决方案是:https://github.com/armanozak/angular-dynamic-locale。尝试使用 Angular 12 并允许动态更改 LOCALE_ID 而无需重新加载应用程序。

        【讨论】:

          猜你喜欢
          • 2016-04-26
          • 1970-01-01
          • 2019-01-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-04-05
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多