动态更改Angular 2中DatePipe的区域设置

时间:2017-05-31 14:44:04

标签: angular locale angular2-pipe

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

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

4 个答案:

答案 0 :(得分:14)

要从服务设置区域设置,您需要将{ "dstOffset" : 3600, "rawOffset" : 0, "status" : "OK", "timeZoneId" : "Europe/London", "timeZoneName" : "British Summer Time" } 提供商与工厂添加到LOCALE_ID,例如@AmolBhor回答

app.module

很遗憾,您无法更改DatePipe JIT的语言。 Angular编译器在引导期间需要{ provide: LOCALE_ID, deps: [SettingsService], //some service handling global settings useFactory: (settingsService) => settingsService.getLanguage() //returns locale string }

Angular有一些错误报告:

有几种解决方法:

解决方法#1

重新启动角度模块:

LOCALE_ID

*这对于Hybrid Angular / AngularJS不起作用,因为使用UpgradeModule无法破坏AngularJS。

解决方法#2

覆盖DatePipe,NumberPipe - 无论您需要什么:

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

解决方法#3

使用已经使用自定义管道进行本地化的库,例如:

解决方法#4

使用@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); } } 的每个管道都有私有字段区域设置 _locale ,因此您可以在语言更改时在该管道上覆盖此字段,因为有一个管道的实例。

这将起作用,因为TypeScript只是JavaScript的语法糖。在JavaScript中没有私有字段。

还要记住在ApplicationRef中使用LOCALE_ID方法处理应用程序中的更改检测。

tick()

解决方法#5

在语言发生变化时重新加载应用程序。

@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()
  }
}

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

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

答案 1 :(得分:9)

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

希望这会对你有所帮助。

答案 2 :(得分:2)

让您的服务像

.nav-li, .nav-ul li{}

在App.module.ts中

首先需要导入本地化,例如

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;
      }
    }
  }
}

比提供者部分

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
}

希望它将对某人有所帮助

如果要动态更改语言环境,请在所需的组件中注入LocaleService并使用registerCulture方法,将所需的区域性传递给此

答案 3 :(得分:2)

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

总结:

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

  • LOCALE_ID 是使用本地化服务作为设置在 app.module.ts 提供程序中进行配置的,通过 useFactory 选项
  • 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));
    
  • 为了进一步改进捆绑,我们可以添加一些神奇的注释以限制我们支持的语言环境:

    /* 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"
        /* ... */ 
    }