[ ] Regression (a behavior that used to work and stopped working in a new release)
[ ] Bug report
[ ] Performance issue
[ ] Feature request
[x] Documentation issue or request
[x] Support request
[ ] Other... Please describe:
Dear Ngneat team,
We are currently migrating from ngx-translate to transloco because we use NX for handling several Angular apps and libs. In addition, we would like to separate the translations by module. The following example demonstrates it quite well, how we can use transloco with different scopes for each app and lib:
Example with NX: https://github.com/NetanelBasal/transloco-with-nx-libs
Unfortunately, I am stuck with the use case when you add a third library. For example, let’s name it “common”. This library contains all shared "dumb" components and is imported in practically all other libraries as well as by the (lazy) modules in the app itself. The common library has its own translations.
Update: 15.11.2020
How the dependency could look like:

What I am missing in the example above is exactly this use case. I am assuming, that this is a common use case and should be possible with Transloco but I cannot get it to work. Maybe I am missing anything? Please see more details in the minimal reproduction section.
Thank you very much for your help.
The translations are not loaded:

In the logs, I got a missing error:
Missing translation for 'common.validations.required'
The translations are loaded and rendered.
Clone the project above and then generate a new library as follows:
nx g library common
Under libs/common/src/lib I created a new folder i18n and added two translation files en.json and es.json.

Example en.json (same for es.json):
{
"validations.required": "This field is required"
}
The CommonsModule will look as follows:
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TranslocoModule, TRANSLOCO_SCOPE } from '@ngneat/transloco';
import { scopeLoader } from '../../../../scoped-translations';
@NgModule({
imports: [CommonModule, TranslocoModule],
exports: [TranslocoModule],
providers: [
{
provide: TRANSLOCO_SCOPE,
useValue: {
scope: 'common',
loader: scopeLoader((lang, root) => import(`./${root}/${lang}.json`))
}
}
]
})
export class CommonsModule {}
Now, I will import this module in any lib and app.
Example Library import in TranslocoLibBModule:
import { TRANSLOCO_SCOPE } from '@ngneat/transloco';
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { scopeLoader } from '../../../../scoped-translations';
import { LocationBComponent } from './location-b.component';
import { CommonsModule } from '@transloco-with-libs/common';
@NgModule({
imports: [
CommonModule,
// newly added
CommonsModule],
providers: [
{
provide: TRANSLOCO_SCOPE,
useValue: {
scope: 'compB',
loader: scopeLoader((lang, root) => import(`./${root}/${lang}.json`))
}
}
],
declarations: [LocationBComponent],
exports: [LocationBComponent]
})
export class TranslocoLibBModule {}
And the component:
@Component({
selector: 'transloco-with-libs-b',
template: `
<ng-container *transloco="let t">
<p>where am I? {{ t('compB.gpsb') }}</p>
</ng-container>
<!-- newly added -->
<p>Inside lib-b: {{ 'common.validations.required' | transloco }}</p>
<ng-container *transloco="let t">
<p>Inside lib-b: {{ t('common.validations.required') }}</p>
</ng-container>
`
})
export class LocationBComponent implements OnInit {
constructor() {}
ngOnInit() {}
}
The same thing in the app:
app.module.ts
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
HttpClientModule,
// newly added
CommonsModule,
TranslocoLibBModule,
RouterModule.forRoot(routes)
],
providers: [
{
provide: TRANSLOCO_CONFIG,
useValue: {
reRenderOnLangChange: true,
availableLangs,
defaultLang: 'en',
prodMode: environment.production
} as TranslocoConfig
},
translocoLoader
],
bootstrap: [AppComponent]
})
export class AppModule {}
app.component.html
<section>
<h1>This should tell me where I am</h1>
<button (click)="change('en')">English</button>
<button (click)="change('es')">Spanish</button>
<ng-container *transloco="let t">
<p>Root: {{ t('gps') }}</p>
</ng-container>
<!-- newly added-->
<p>Inside app: {{ 'common.validations.required' | transloco }}</p>
<ng-container *transloco="let t">
<p>Inside app: {{ t('common.validations.required') }}</p>
</ng-container>
<div style="display: flex; flex-direction: column">
<transloco-with-libs-b></transloco-with-libs-b>
</div>
<router-outlet></router-outlet>
</section>
Run and verify it:
ng serve
Angular version: 8.2.9 (minimal reproduction from the cloned repo)
Its still an issue in our project with Angular version 10.0.0 and with Transloco version ^2.19.1.
Browser:
- [x ] Chrome (desktop) version 86.0.4240.183
- [ ] Chrome (Android) version XX
- [ ] Chrome (iOS) version XX
- [ ] Firefox version XX
- [ ] Safari (desktop) version XX
- [ ] Safari (iOS) version XX
- [ ] IE version XX
- [ ] Edge version XX
For Tooling issues:
- Node version: v12.18.0
- Platform: Mac
Others:
Can't nobody help me there?
@strikeflare feel free to ask in our gitter channel as well, I'll take a look at this when I get the chance 🙂
Hi @strikeflare it seems like you are having DI conflicts because of the way that Angular's DI works.
Please follow this guide to learn more about how to handle translation in your application, you can scroll down to the "Monorepo" & "Eager Modules" section it should answer your question.
@strikeflare did you manage to solve it? can we close this issue?
@itayod I checked this guide before I wrote this question here. I tried both solutions with Scoped libraries and with eager modules. But without success. I extended the example above as follows:
import { CommonModule } from '@angular/common';
import { TranslocoModule, TranslocoService } from '@ngneat/transloco';
import en from './i18n/en.json';
import es from './i18n/es.json';
import { CommonComponent } from './common.component';
@NgModule({
declarations: [CommonComponent],
imports: [CommonModule, TranslocoModule],
exports: [TranslocoModule, CommonComponent]
})
export class CommonsModule {
constructor(private _translate: TranslocoService) {
this._translate.setTranslation(en);
this._translate.setTranslation(es);
}
}
You might have noticed that I also added a simple CommonComponent inside the Common library, which I will use it inside the lib-b library.
import { Component } from '@angular/core';
@Component({
selector: 'transloco-with-libs-common',
template: `
<p>Inside common component {{ 'common.required' | transloco }}</p>
`
})
export class CommonComponent {}
// use it inside the Lib B Component
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'transloco-with-libs-b',
template: `
<ng-container *transloco="let t">
<p>where am I? {{ t('compB.gpsb') }}</p>
</ng-container>
<!-- newly added -->
<p>Inside lib-b: {{ 'common.required' | transloco }}</p>
<ng-container *transloco="let t">
<p>Inside lib-b: {{ t('common.required') }}</p>
</ng-container>
<!-- common component added -->
<transloco-with-libs-common></transloco-with-libs-common>
`
})
export class LocationBComponent implements OnInit {
constructor() {}
ngOnInit() {}
}
The output is always common.required

And the console:

I don't know if this approach is correct what I am trying to do. But how do I achieve that multiple apps consuming one common library you don't have to put all the translations in all apps?
@strikeflare what you are trying to do is quite common and should be simple, but I couldn't understand much from your code snippets here, would you like to create a reproduction project on github, so I can take a better look at your problem?
@strikeflare Can you provide a reproduction so we can better understand your issue? it's always preferred over code snippets 🙂
Sorry for being late. I uploaded the reproduction here
@strikeflare looking at your code, there are several issues (all written in the guide).
The first issue is in CommonsModule, you forgot to add the common to setTranslation function.
I have changed the code as follows and now it works:
constructor(private _translate: TranslocoService) {
this._translate.setTranslation({
'common': en
}, 'en');
this._translate.setTranslation({
'common': es
}, 'es');
}
The second issue is that you declared a few TranslocoScope providers which override one another because of the way Angular's DI works.
There are several ways to overcome it, one of them is to move the Scope declaration to the Component's providers, I did that in LocationBComponent and then everything worked.
@itayod Thank you very much for your help! I changed to this snippet you provided and it works! It seems that I forgot to declare the scope common...
For the second issue. In other words, we cannot declare different scopes like compA in the module from library A und compB in library B, since Angular's DI overrides the second TRANSLOCO_SCOPE provider? To bypass this problem, we need to add the scope in the component's provider?
I updated the code as you have suggested.
@strikeflare glad you figured it out.
we cannot declare different scopes like compA in the module from library A und compB in library B, since Angular's DI overrides the second TRANSLOCO_SCOPE provider?
That is not entirely true, you may declare one in each lazy-loaded module
To bypass this problem, we need to add the scope in the component's provider?
Like I said there are several ways to deal with it, one of them is to use the component's providers.
Either way, I strongly recommend you to read about this stuff since it is an important part of Angular and you might run into more problems like this in the future (not just in Transloco...).
Most helpful comment
Sorry for being late. I uploaded the reproduction here