Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

staticInjectorError[InjectionToken TranslationsFormat] when building with AOT #4

Closed
BSchuhmacher opened this issue Dec 8, 2017 · 18 comments

Comments

@BSchuhmacher
Copy link

BSchuhmacher commented Dec 8, 2017

Hi
I am in the progress of testing i18n-polyfill.
After setting up my ts files with the i18n-polyfill I can extract all marked parts and build the project with:
ng xi18n -of i18n/source.xlf -f xlf --locale en
node ./node_modules/@ngx-translate/i18n-polyfill/extractor/ngx-extractor -i "src/**/*.ts" -f xlf -o src/i18n/source.xlf
After this step I inserted the "<target>...</target>" parts into src/i18n/source.xlf and saved it as src/i18n/source.en.xlf.
ng build --i18nFile src/i18n/source.en.xlf --i18nFormat xlf --locale en
The build process finished with no errors.
When opening the project with the Webbrowser I get the following error:

ERROR Error: StaticInjectorError[InjectionToken TranslationsFormat]: 
  StaticInjectorError[InjectionToken TranslationsFormat]: 
    NullInjectorError: No provider for InjectionToken TranslationsFormat!
    at _NullInjector.webpackJsonp.../../../core/esm5/core.js._NullInjector.get (core.js:993)
    at resolveToken (core.js:1281)
    at tryResolveToken (core.js:1223)
    at StaticInjector.webpackJsonp.../../../core/esm5/core.js.StaticInjector.get (core.js:1094)
    at resolveToken (core.js:1281)
    at tryResolveToken (core.js:1223)
    at StaticInjector.webpackJsonp.../../../core/esm5/core.js.StaticInjector.get (core.js:1094)
    at resolveNgModuleDep (core.js:10883)
    at _createClass (core.js:10928)
    at _createProviderInstance$1 (core.js:10894)
View_AppComponent_Host_0 @ AppComponent_Host.ngfactory.js? [sm]:1
proxyClass @ compiler.js:14642
webpackJsonp.../../../core/esm5/core.js.DebugContext_.logError @ core.js:15031
webpackJsonp.../../../core/esm5/core.js.ErrorHandler.handleError @ core.js:1488
(anonymous) @ core.js:5688
webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invoke @ zone.js:392
webpackJsonp.../../../../zone.js/dist/zone.js.Zone.run @ zone.js:142
webpackJsonp.../../../core/esm5/core.js.NgZone.runOutsideAngular @ core.js:4704
(anonymous) @ core.js:5688
webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invoke @ zone.js:392
onInvoke @ core.js:4756
webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invoke @ zone.js:391
webpackJsonp.../../../../zone.js/dist/zone.js.Zone.run @ zone.js:142
(anonymous) @ zone.js:873
webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invokeTask @ zone.js:425
onInvokeTask @ core.js:4747
webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invokeTask @ zone.js:424
webpackJsonp.../../../../zone.js/dist/zone.js.Zone.runTask @ zone.js:192
drainMicroTaskQueue @ zone.js:602
Promise resolved (async)
scheduleMicroTask @ zone.js:585
webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.scheduleTask @ zone.js:414
webpackJsonp.../../../../zone.js/dist/zone.js.Zone.scheduleTask @ zone.js:236
webpackJsonp.../../../../zone.js/dist/zone.js.Zone.scheduleMicroTask @ zone.js:256
scheduleResolveOrReject @ zone.js:871
ZoneAwarePromise.then @ zone.js:981
webpackJsonp.../../../core/esm5/core.js.PlatformRef.bootstrapModule @ core.js:5574
../../../../../src/main.ts @ main.ts:11
__webpack_require__ @ bootstrap 6c8bf590f265d8ba9ec9:54
0 @ main.ts:12
__webpack_require__ @ bootstrap 6c8bf590f265d8ba9ec9:54
webpackJsonpCallback @ bootstrap 6c8bf590f265d8ba9ec9:25
(anonymous) @ main.bundle.js:1
AppComponent_Host.ngfactory.js? [sm]:1 ERROR CONTEXT DebugContext_ {view: {…}, nodeIndex: 1, nodeDef: {…}, elDef: {…}, elView: {…}}
View_AppComponent_Host_0 @ AppComponent_Host.ngfactory.js? [sm]:1
proxyClass @ compiler.js:14642
webpackJsonp.../../../core/esm5/core.js.DebugContext_.logError @ core.js:15031
webpackJsonp.../../../core/esm5/core.js.ErrorHandler.handleError @ core.js:1493
(anonymous) @ core.js:5688
webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invoke @ zone.js:392
webpackJsonp.../../../../zone.js/dist/zone.js.Zone.run @ zone.js:142
webpackJsonp.../../../core/esm5/core.js.NgZone.runOutsideAngular @ core.js:4704
(anonymous) @ core.js:5688
webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invoke @ zone.js:392
onInvoke @ core.js:4756
webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invoke @ zone.js:391
webpackJsonp.../../../../zone.js/dist/zone.js.Zone.run @ zone.js:142
(anonymous) @ zone.js:873
webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invokeTask @ zone.js:425
onInvokeTask @ core.js:4747
webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invokeTask @ zone.js:424
webpackJsonp.../../../../zone.js/dist/zone.js.Zone.runTask @ zone.js:192
drainMicroTaskQueue @ zone.js:602
Promise resolved (async)
scheduleMicroTask @ zone.js:585
webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.scheduleTask @ zone.js:414
webpackJsonp.../../../../zone.js/dist/zone.js.Zone.scheduleTask @ zone.js:236
webpackJsonp.../../../../zone.js/dist/zone.js.Zone.scheduleMicroTask @ zone.js:256
scheduleResolveOrReject @ zone.js:871
ZoneAwarePromise.then @ zone.js:981
webpackJsonp.../../../core/esm5/core.js.PlatformRef.bootstrapModule @ core.js:5574
../../../../../src/main.ts @ main.ts:11
__webpack_require__ @ bootstrap 6c8bf590f265d8ba9ec9:54
0 @ main.ts:12
__webpack_require__ @ bootstrap 6c8bf590f265d8ba9ec9:54
webpackJsonpCallback @ bootstrap 6c8bf590f265d8ba9ec9:25
(anonymous) @ main.bundle.js:1
main.ts:12 Error: StaticInjectorError[InjectionToken TranslationsFormat]: 
  StaticInjectorError[InjectionToken TranslationsFormat]: 
    NullInjectorError: No provider for InjectionToken TranslationsFormat!
    at _NullInjector.webpackJsonp.../../../core/esm5/core.js._NullInjector.get (core.js:993)
    at resolveToken (core.js:1281)
    at tryResolveToken (core.js:1223)
    at StaticInjector.webpackJsonp.../../../core/esm5/core.js.StaticInjector.get (core.js:1094)
    at resolveToken (core.js:1281)
    at tryResolveToken (core.js:1223)
    at StaticInjector.webpackJsonp.../../../core/esm5/core.js.StaticInjector.get (core.js:1094)
    at resolveNgModuleDep (core.js:10883)
    at _createClass (core.js:10928)
    at _createProviderInstance$1 (core.js:10894)

I tried this with a new angular project (ng new i18nTest). The changed files are the following:
app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';


import { AppComponent } from './app.component';
import { I18n } from '@ngx-translate/i18n-polyfill';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule
  ],
  providers: [I18n],
  bootstrap: [AppComponent]
})
export class AppModule { }

app.component.ts

import {Component} from '@angular/core';
import {I18n} from '@ngx-translate/i18n-polyfill';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title: string;
  constructor(i18n: I18n) {
    this.title = i18n('app');
  }
}

app.component.html

<!--The content below is only a placeholder and can be replaced.-->
<div style="text-align:center">
  <h1 i18n>
    Welcome to {{ title }}!
  </h1>
  <img width="300" alt="Angular Logo" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg==">
</div>
<h2 i18n>Here are some links to help you start: </h2>
<ul>
  <li>
    <h2><a target="_blank" rel="noopener" href="https://angular.io/tutorial" i18n>Tour of Heroes</a></h2>
  </li>
  <li>
    <h2><a target="_blank" rel="noopener" href="https://github.com/angular/angular-cli/wiki" i18n>CLI Documentation</a></h2>
  </li>
  <li>
    <h2><a target="_blank" rel="noopener" href="https://blog.angular.io/" i18n>Angular blog</a></h2>
  </li>
</ul>

As I don't have a idea where a problem on my siede could be I fear it is a problem with the polyfill.
Any Ideas?

Regards
Bernd

@BSchuhmacher BSchuhmacher changed the title taticInjectorError[InjectionToken TranslationsFormat] when building with AOT staticInjectorError[InjectionToken TranslationsFormat] when building with AOT Dec 8, 2017
@vqoph
Copy link

vqoph commented Dec 12, 2017

Same issue on my side, anything new about this ?

@ocombe
Copy link
Member

ocombe commented Dec 12, 2017

Yes sorry, I will update the docs, you need to define those if you don't use the angular cli + aot:

// format of translations that you use
{provide: TRANSLATIONS_FORMAT, useValue: "xlf"},
// the translations that you need to load on your own
{provide: TRANSLATIONS, useValue: XLIFF},
// locale id that you're using (default en-US)
{provide: LOCALE_ID, useValue: "fr"},
// optional, defines how error will be handled
{provide: MISSING_TRANSLATION_STRATEGY, useValue: MissingTranslationStrategy.Error}

Those are the exact same options that you need to define for angular i18n, see the docs on angular.io: https://angular.io/guide/i18n#merge-with-the-jit-compiler

@vqoph
Copy link

vqoph commented Dec 12, 2017

I use ng cli (tested with 1.5.0 and 1.6.0 version and Angular 5.0.0 and 5.1.0 version) and aot compiler, and i still have an error

I tried with the i18n example from https://angular.io/guide/i18n. Added my translations tested on both aot and jit with specific language providers . Ran the project with commands below and everything is working as expected (if I don't inject the I18n service)

# JIT
ng serve 

# AOT
ng serve --aot --i18nFile=src/i18n/source.fr.xlf --i18nFormat=xlf --locale=fr --missingTranslation=ignore 

// my i18n JIT configuration in main.ts

declare const require
const translations = require('raw-loader!./i18n/source.fr.xlf');

platformBrowserDynamic().bootstrapModule(AppModule, {
  providers: [
    {provide: TRANSLATIONS, useValue: translations},
    {provide: TRANSLATIONS_FORMAT, useValue: 'xlf'},
    {provide: LOCALE_ID, useValue: "fr"},
    {provide: MISSING_TRANSLATION_STRATEGY, useValue: MissingTranslationStrategy.Ignore},
  ],
})

note that MISSING_TRANSLATION_STRATEGY seems to not already exist on @angular/core, I needed to import it from @ngx-translate/i18n-polyfill

then I inject i18n in my component controller,
as this :

constructor( i18n: I18n ) {
    this.translatedFromCode = i18n('This was translated from code');
  }

add the provider providers: [I18n], in my app.module.ts and on both JIT and AOT (tried with and without additional providers). I have this error now :

ERROR Error: StaticInjectorError[InjectionToken Translations]: 
  StaticInjectorError[InjectionToken Translations]: 
    NullInjectorError: No provider for InjectionToken Translations!
    at _NullInjector.get (core.js:993)
    at resolveToken (core.js:1281)
    at tryResolveToken (core.js:1223)
    at StaticInjector.get (core.js:1094)
    at resolveToken (core.js:1281)
    at tryResolveToken (core.js:1223)
    at StaticInjector.get (core.js:1094)
    at resolveNgModuleDep (core.js:10883)
    at _createClass (core.js:10928)
    at _createProviderInstance$1 (core.js:10894)
View_AppComponent_Host_0 @ app.component.html:5
DebugContext_.logError @ core.js:15031
ErrorHandler.handleError @ core.js:1488
(anonymous) @ core.js:5688
ZoneDelegate.invoke @ zone.js:392
Zone.run @ zone.js:142
NgZone.runOutsideAngular @ core.js:4704
(anonymous) @ core.js:5688
ZoneDelegate.invoke @ zone.js:392
onInvoke @ core.js:4756
ZoneDelegate.invoke @ zone.js:391
Zone.run @ zone.js:142
(anonymous) @ zone.js:873
ZoneDelegate.invokeTask @ zone.js:425
onInvokeTask @ core.js:4747
ZoneDelegate.invokeTask @ zone.js:424
Zone.runTask @ zone.js:192
drainMicroTaskQueue @ zone.js:602
Promise resolved (async)
scheduleMicroTask @ zone.js:585
ZoneDelegate.scheduleTask @ zone.js:414
onScheduleTask @ zone.js:301
ZoneDelegate.scheduleTask @ zone.js:405
Zone.scheduleTask @ zone.js:236
Zone.scheduleMicroTask @ zone.js:256
scheduleResolveOrReject @ zone.js:871
ZoneAwarePromise.then @ zone.js:981
ApplicationInitStatus.runInitializers @ core.js:3555
(anonymous) @ core.js:5506
_callAndReportToErrorHandler @ core.js:5685
(anonymous) @ core.js:5504
ZoneDelegate.invoke @ zone.js:392
onInvoke @ core.js:4756
ZoneDelegate.invoke @ zone.js:391
Zone.run @ zone.js:142
NgZone.run @ core.js:4573
PlatformRef.bootstrapModuleFactory @ core.js:5495
(anonymous) @ main.ts:11
../../../../../src/main.ts @ main.bundle.js:62
__webpack_require__ @ inline.bundle.js:55
0 @ main.bundle.js:77
__webpack_require__ @ inline.bundle.js:55
webpackJsonpCallback @ inline.bundle.js:26
(anonymous) @ main.bundle.js:1

Does this help ?
What am i doing wrong ?

@ocombe
Copy link
Member

ocombe commented Dec 12, 2017

What if you add the providers (for translations, ...) to your main module as well?

@vqoph
Copy link

vqoph commented Dec 12, 2017

it's working with JIT compilation ! thanks a lot :) I'm now trying with aot

my app.module.ts (for others) :

import {
  NgModule,
  TRANSLATIONS,
  TRANSLATIONS_FORMAT,
  LOCALE_ID,
  MissingTranslationStrategy
 } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { I18n, MISSING_TRANSLATION_STRATEGY } from '@ngx-translate/i18n-polyfill';

// use the require method provided by webpack
declare const require;
// we use the webpack raw-loader to return the content as a string
const translations = require('raw-loader!../i18n/source.fr.xlf');

import { AppComponent } from './app.component';

@NgModule({
  imports: [ BrowserModule ],
  declarations: [ AppComponent ],
  providers: [
    I18n,
    {provide: TRANSLATIONS, useValue: translations},
    {provide: TRANSLATIONS_FORMAT, useValue: 'xlf'},
    {provide: LOCALE_ID, useValue: "fr"},
    {provide: MISSING_TRANSLATION_STRATEGY, useValue: MissingTranslationStrategy.Ignore},
  ],
  bootstrap: [ AppComponent ],
})

export class AppModule { }

@vqoph
Copy link

vqoph commented Dec 12, 2017

It's look like doesn't work with aot : When i try to launch this code with aot i have an error

ERROR in Error: Error encountered resolving symbol values statically. Reference to a local (non-exported) symbol 'translations'. Consider exporting the symbol (position 14:7 in the original .ts file), resolving symbol AppModule in /Users/Vincent/Workspace/angular/i18n/src/app/app.module.ts

solved by adding export before declare const require; (? -> angular/angular-cli#4956 (comment))

when i create an other translation and serve the project with :

ng serve --aot --i18nFile=src/i18n/source.en-GB.xlf --i18nFormat=xlf --locale=en-GB --missingTranslation=ignore

Project build without errors, translations from html are right ones (from source.en-GB.xlf), but translation from code are not replaced and still translated with : ./i18n/source.fr.xlf

So how i can configure the module to be usable with aot ?

@ocombe
Copy link
Member

ocombe commented Dec 12, 2017

Try to define "I18n" last in your list of providers
edit: nevermind it would fail with jit too

@ocombe
Copy link
Member

ocombe commented Dec 12, 2017

You've changed the providers in the main module to use the file "source.en-GB" instead of fr ?

@vqoph
Copy link

vqoph commented Dec 12, 2017

no, I would be able to build my app in each language in --prod without using JIT
and without loading file on runtime for this, but may be it's not possible

if not i'll find a workaround

@ocombe
Copy link
Member

ocombe commented Dec 12, 2017

The translations are not added to the providers by the AOT compiler yet (it will change in v6), which means that you'll need to define them in the providers of your main module...
For now there's no way to hook into the tranformation of your bootstrap file by the cli (it should change in cli v2) which means that there's no way to automatically add it this way either.

That being said, you can use System.import in AOT, if you use a variable for the locale name, webpack will generate a chunk for each translation file, and only the right one will be loaded, but it'll be loaded at runtime (via a promise).
Another way to do that would be to have a small script that will be run before your build/serve command to rename the correct file to a fixed name based on the --locale parameter, and then require this with the raw loader should load & bundle it into your files.
Look at what I did here: https://github.com/ocombe/i18n-demo-cli with the jit-helper.js file that is run as a "prestart" script

@vqoph
Copy link

vqoph commented Dec 12, 2017

Ok, I will take a look. Thanks a lot for your time and for your work. 👍

@jrolheiser
Copy link
Contributor

jrolheiser commented Jan 3, 2018

@vqoph Not sure if you got it working. If not, or maybe for the next person to find this thread looking for help, here's what finally worked for me.

I'm passing in the locale and i18nFormat as args to my build/serve commands and found I don't need to add them to my app.module.ts's providers. I only ended up needing to provide TRANSLATIONS as @ocombe mentioned. I also needed and couldn't find a documented way to load the translation files dynamically so I used a factory loader like:

// app.module.ts
import { BrowserModule } from '@angular/platform-browser';

import {
  NgModule,
  TRANSLATIONS,
  LOCALE_ID
} from '@angular/core';

import { I18n } from '@ngx-translate/i18n-polyfill';

declare const require;
export function translationsFactory(locale: string) {
  locale = locale || 'en'; // default to english if no locale
  return require(`raw-loader!../locale/messages.${locale}.xlf`);
}

@NgModule({
  imports: [BrowserModule]
  declarations: [
    AppComponent,
  ],
  providers: [
    {
      provide: TRANSLATIONS,
      useFactory: translationsFactory,
      deps: [LOCALE_ID]
    },
    I18n
  ]
  bootstrap: [AppComponent]
})
export class AppModule { }

My project is structured like:

-app
--src
--assets
--locale
---messages.xlf
---messages.en.xlf
---messages.fr.xlf

To test out translations I can serve it like:
ng serve --aot --i18nFile=src/locale/messages.fr.xlf --i18nFormat=xlf --locale=fr

@ocombe
Copy link
Member

ocombe commented Jan 4, 2018

This is nice! Could you improve the documentation of the project (readme) to explain all this?

@chriskyndrid
Copy link

chriskyndrid commented Jan 21, 2018

Thanks @jrolheiser-va for the tip and @ocombe for the work. One thing to note, as of:

#15754

The compiler will no longer add empty <target/> tags in the output xlf file. In many projects translation will occur later on in the development lifecycle(by shipping the source translation file off to a translator). If you generate an xlf file(root language) without the <target/> the Xliff parser will throw an error, forcing the developer to add applicable <target/> tags in order to move forward. As far as I know there is no option to force the Xliff parser to proceed without this tag.

It would be really great to be able to pass an argument to ng xi18n to allow the developer to decide if empty <target/> target tag should be generated.

This is of course, unless I'm missing something here.

@ocombe
Copy link
Member

ocombe commented Jan 21, 2018

could you open an issue on the angular repository for this please?

@chriskyndrid
Copy link

Thank you @ocombe for getting back to me so quickly. See issue:

#21690

Have a wonderful day.

@clark0x
Copy link

clark0x commented Aug 27, 2018

@jrolheiser-va it seems that ng serve doesn't support --i18nFile --i18nFormat --locale now.

@jrolheiser
Copy link
Contributor

jrolheiser commented Aug 27, 2018

@clarkorz This has been reported to the Angular team here which also contains a solution you can use in the interim time until it is fixed.

Personally I switched all of our company's projects to just use the core lib as we ran into some use cases and limitations that this polyfill lib didn't support (Mainly a good interface for loading dynamic translations) While it is possible with this polyfill library, we just found the core lib easier to use.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants