Search code examples
angulardatelocalizationlocale

Angular "InvalidPipeArgument: Missing locale data" when build or serve with optimization=true (--prod option)


Initial context

I build an Angular app in french.

I use the native date pipe | date and also a datepicker component (@danielmoncada/angular-datetime-picker).

I'd like to display dates in french style and that the datepicker component is localized in french (months names, ...)

Code example:

<p>raw: {{today}}</p>
<p>date pipe: {{today | date}}</p>
<p>date pipe 'dd/MM/yyyy': {{today | date:'dd/MM/yyyy'}}</p>
<p>
  date picker:
  <input [owlDateTime]="dt1" [owlDateTimeTrigger]="dt1" placeholder="Date Time">
  <owl-date-time [firstDayOfWeek]="1" [pickerType]="'calendar'" #dt1></owl-date-time>
</p>

Results without any locale set

This is in english, as expected.

result without locale set enter image description here

Adding some localization in french

Now I follow what I understood about localization in angular and I import in my AppModule the french locale with registerLocaleData and set the LOCALE_ID accordingly:

import { registerLocaleData } from '@angular/common';
import localeFr from '@angular/common/locales/fr';

registerLocaleData(localeFr, 'fr');

@NgModule({
  declarations: [],
  imports     : [],
  providers   : [
    { provide: LOCALE_ID, useValue: 'fr'}
  ]
})

Results with this french locale set

This is in french, as expected.

enter image description here enter image description here

OK, fine!

I have to say that here I'm launching the app in development using the classic ng serve angular-cli command.

Deploying in a test environment = Problem!

But when deploying the app to a test environment, these date-related things doesn't work and break the app because of a javascript error in the browser's console :

ERROR Error: InvalidPipeArgument: 'Missing locale data for the locale "fr".' for pipe 'e'

Results with this french locale set AND serve using --prod / optimization=true

enter image description here enter image description here

The dates and datepicker are not displayed because of the console error breaking the page generation...

enter image description here

First analysis

We guess this is because we are using optimization=true when building for production environment (parameter set in the angular.json file)

This parameter is located at architect.build.configurations.production in angular.json:

  "architect": {
    "build": {
      "builder": "@angular-devkit/build-angular:browser",
      "options": {...},
      "configurations": {
        "production": {
          "fileReplacements": [...],
          "optimization": true,
          "outputHashing": "all",
          "sourceMap": false,
          "extractCss": true,
          "namedChunks": false,
          "extractLicenses": true,
          "vendorChunk": false,
          "buildOptimizer": true,
...

Indeed, if we set it to false, the problem goes!

But this is not accpetable to disable optimization in production.

Trying to reproduce locally in development

I launched my app locally using the command ng serve --prod, that will use the production build described above, with optimization set to true.

>> I reproduce the error!!

Then I tried to override with optimization: false at the architect.serve.configurations.production location in angular.json:

    "serve": {
      "builder": "@angular-devkit/build-angular:dev-server",
      "options": {
        "browserTarget": "dpe-front:build"
      },
      "configurations": {
        "production": {
          "browserTarget": "dpe-front:build:production",
          "optimization": false
        }
      }
    },

>> I DO NOT reproduce the error!!

Conclusion:

The problem seems to deal with "optimization".

When set to true, my LOCALE definition doesn't works and it breaks the app...

Why locale configuration doesn't works in this case?? Am I missing something?


Solution

  • I found the problem origin!

    TL;DR

    It seems to be a bug in Angular 10.x caused by too agressive tree shaking during the build when in production mode and if your project is configured with angular strict mode.

    Indeed:

    • Upgrading to angular 11.x solved the problem

    or

    • Setting sideEffects to true in app's package.json file solved the problem

    More explanations

    When starting a new project in strict mode, a package.json file is created in project's src/app directory.

    {
      "name": "myproject",
      "private": true,
      "description_1": "This is a special package.json file that is not used by package managers.",
      "description_2": "It is used to tell the tools and bundlers whether the code under this directory is free of code with non-local side-effect. Any code that does have non-local side-effects can't be well optimized (tree-shaken) and will result in unnecessary increased payload size.",
      "description_3": "It should be safe to set this option to 'false' for new applications, but existing code bases could be broken when built with the production config if the application code does contain non-local side-effects that the application depends on.",
      "description_4": "To learn more about this file see: https://angular.io/config/app-package-json.",
      "sideEffects": false
    }
    

    It has the sideEffects option to tell to the webpack bundler that it can do more agressive tree shaking for all files under this directory, as they are "pure". The goal is to have smaller files at the end.

    More details here: https://webpack.js.org/guides/tree-shaking/

    So when sideEffects is set to false (the default), it tells that all files can be used for "more agressive tree shaking".

    Setting it to true or removing this package.json file solved my problem.

    However, doing the same thing in a brand new test project doesn't leads to the same problem...

    The difference is that this new project is in angular v11, whereas my project was started using angular v10.

    So this bug seems to have been solved with angular 11 release.

    I had the problem using the DatePipe, but apparently other pipes (like AsyncPipe) can lead to same kind of "too agressive tree shaking problems", as in this example:

    Tree shaking for Angular 10 shook out AsyncPipe when using sideEffects: false

    I hope this will help other people!