-
-
Notifications
You must be signed in to change notification settings - Fork 577
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
Lazy-loaded modules dont get their translations when "isolate" is false. #1193
Comments
Found a workaround to this issue thanks to a @ye3i comment in a PR. The trick seams that ngx-translate wont load anything if the language doesn't change one way or another. So when isolate is false the currentLang inherits from parent and if it is the same as the one in "use" it wont make the neccesary http request. |
app.module.tsexport function createTranslateLoader(http: HttpClient) {
return new TranslateHttpLoader(http, './assets/i18n/app/', '.json');
}
@NgModule({
declarations: [AppComponent],
imports: [
HttpClientModule,
TranslateModule.forRoot({
loader: { provide: TranslateLoader, useFactory: createTranslateLoader, deps: [HttpClient] }
})
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {} app/en.json{
"action": "Create"
} Lazy loadedorder.module.ts export function createTranslateLoader(http: HttpClient) {
return new TranslateHttpLoader(http, './assets/i18n/order/', '.json');
}
@NgModule({
declarations: [OrderComponent],
imports: [
TranslateModule.forChild({
loader: { provide: TranslateLoader, useFactory: createTranslateLoader, deps: [HttpClient] },
isolate: true
})
]
})
export class OrderModule {} order/en.json{
"title": "Order"
} order.component.html<div>{{'title' | translate}}</div>
<div>{{'action' | translate}}</div> app.component.html<div>{{'action' | translate}}</div>
<div>-----Order Component-----</div>
<app-order></app-order> ResultCreate
-----Order Component-----
Order
action How to access "action" key in order component? |
I've got the same issue here, even if I try every combinaison with extend boolean, it doesn't change anything. RootModule : isolate: false Is there anything I didn't understand ?
Does extend not combine with isolate ? Then, how do you limit propagation of child translations but profit from parent's ones ? Thanks |
I have the same problem. Do you have a solution? |
Nop, didn't get any movement here. Have no solution. |
My workaround until this issue has been resolved is following:
|
did anyone found a solution yet? |
Have a look at this. https://www.youtube.com/watch?v=NJctHJzy5vo |
@ocombe are you planning to fix this issue in the immediate future? |
works for me with @Juusmann solution , thanks! |
@Juusmann's solution works for me as well. To be extremely specific and to add clarity to anyone still wondering how it all fits together, I have the following setup (using abbreviated module definitions): AppModule
SharedModule
LazyLoadedModule
Key Points
|
In addition to @brianmriley solution I also had to do the following in app.component.ts constructor before this would work. Only issue I can see is that it now loads all lazy feature modules .json files upfront and not when the lazy route is hit.
|
For lazy-loaded modules with different translation loaders (loading
It's like I can't blend the two. I got pretty close though maybe you could have a look and play within |
@docwhite did you solve it? |
My company needed something as soon as possible, so I sadly decided to go with another tool called transloco. I'm a bit scared about ngx-translate getting a little bit left behind (by looking at the last release being like 1 year ago) yet being still a standard. I heard the main developer moved to another job working with the Angular team and this project has been a bit left to the community which doesn't know as much as the actual creator. I'm happy to come back to it and keep cracking this issue with modular translations. We work with the monorepo workflow as Nx recommend and this is a must for me, and since I saw a Nx example with scopes in transloco I decided to give it a whirl. You know you can't stay for too long trying to solve an issue when you work for a company :( I left this example StackBlitz to see if someone can crack the problem and come up with a solution, I couldn't. I am subscribed to this issue so I would be very happy to see someone solve it and then I would get back to ngx-translate. |
We are currently facing the same issue. Is there any progress in this? |
@KissBalazs No progress on my side. That was a blocker for me. With transloco it's working well. Maybe taking his idea of scopes and the injection system they use and bring it to ngx-translate could help. |
I ran into this issue when using TranslateModule.forRoot() outside of app.module. Make sure that you provide forRoot only inside app.module. |
For every one still stuck with this issue:
app component
lazy module
lazy module component
So the key points are
Tip
to every component in a module, my solution is to create a container component that contain this logic and set all other component as children of that component
Of cours don't forget to add |
any news on this? I think this is related to my issue, I'm trying to translate a specific module which is loaded on lazy-loaded module, with no success. So my CustomModule is imported in LazyModuleA < import CustomModule |
Thanks @brianmriley it works, so now on use translateService to get Key values dont works |
Thanks for the valuable replay, i did same but nothing worked for me, i got it working now this is what i did....
// in lazy module
TranslateModule.forChild({
loader: {
provide: TranslateLoader,
useFactory: createTranslateLoader,
deps: [HttpClient],
},
extend: true,
}),
// in lazy component ( without it it's not working )
// this.translate.getDefaultLang() <- use any lang you wish here 'en', 'jp' etc
ngOnInit() {
this.translate.use(this.translate.getDefaultLang());
}
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: ModuleHttpLoaderFactory,
deps: [HttpClient],
},
extend: true,
}), Note Finally Got Perfect Solution ( what i wanted)
Here I got the solution to above problem, Thanks to Chatgpt, almost i gave up, and was thinking to stop thinking about it :), was in position to say bye bye to Chatgpt but finally it gave me something that worked like charm, here is our conversation with it https://github.com/hs2504785/ngdemos/blob/master/docs/images/i18n.png |
my work around import {
Inject,
Injectable,
InjectionToken,
ModuleWithProviders,
NgModule,
Provider,
} from '@angular/core';
import { CommonModule } from '@angular/common';
import { languagesList } from './translations.helper';
import { removeConstStringValues } from './translations.helper';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import {
Observable,
catchError,
firstValueFrom,
forkJoin,
from,
map,
of,
skip,
throwError,
} from 'rxjs';
import { AppTranslateService } from './translate.service';
@Injectable()
export class TranslateModuleLoader implements TranslateLoader {
constructor(
@Inject(TRANSLATE_MODULE_CONFIG)
private configs?: TranslateModuleConfig<any>[]
) {}
getTranslation(lang: languagesList): Observable<any> {
const emptyTranslate = () => firstValueFrom(of({ default: {} }));
// console.log('TranslateModuleConfig getTranslation:', this.configs);
const lazyTranslations = (
config: TranslateModuleConfig<any>
): Promise<{
default: removeConstStringValues<translationsObject>;
}> => {
switch (lang) {
case 'none': {
return emptyTranslate();
break;
}
case 'he':
case 'en': {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion, @typescript-eslint/no-extra-non-null-assertion
return config?.translationsChunks?.[lang]!?.();
break;
}
default: {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion, @typescript-eslint/no-extra-non-null-assertion
return config?.translationsChunks?.['he']!?.();
break;
}
}
};
return forkJoin([
...this.configs.map((config) =>
from(lazyTranslations(config) || emptyTranslate()).pipe(
map((x) => x?.default || {}),
catchError(() =>
throwError(
() => new Error(`Please check language ${lang} is supported`)
)
)
)
),
]).pipe(
// tap((x) => {
// debugger;
// }),
map((x) => Object.assign({}, ...x))
// tap((x) => {
// debugger;
// })
);
}
}
export const TRANSLATE_MODULE_CONFIG: InjectionToken<
TranslateModuleConfig<any>
> = new InjectionToken<TranslateModuleConfig<any>>('TranslateModuleConfig');
export const TranslateModuleConfigDefault: Partial<TranslateModuleConfig<any>> =
{};
export const TranslateModuleConfigProvider = (
config: TranslateModuleConfig<any>
): Provider => {
const mergedConfig = { ...TranslateModuleConfigDefault, ...config };
return {
provide: TRANSLATE_MODULE_CONFIG,
useValue: mergedConfig,
multi: true,
};
};
type TranslateModuleConfigTranslations<
defaultTranslations extends translationsObject,
T extends languagesList = languagesList
> = {
// defaultLanguage: T;
defaultLanguage?: T;
supportedLanguages?: T[];
moduleType: 'root' | 'child' | 'lazyChild';
translationsChunks: {
[P in Exclude<T, 'none'>]: P extends 'he'
? () => Promise<{ default: defaultTranslations }>
: () => Promise<{
default: removeConstStringValues<defaultTranslations>;
}>;
};
};
type StringsJSON = { [k: string]: string | StringsJSON };
type translationsObject = {
[k: `${'LIBS' | 'APPS'}_${string}_${string}`]: StringsJSON;
};
type TranslateModuleConfig<
defaultTranslations extends translationsObject
// T extends languagesList = languagesList
> =
// {
// [P in T]:
TranslateModuleConfigTranslations<defaultTranslations>;
// }
type TranslateModuleConfigForRoot<
defaultTranslations extends translationsObject
// T extends languagesList = languagesList
> = Omit<Required<TranslateModuleConfig<defaultTranslations>>, 'moduleType'>;
type TranslateModuleConfigForChild<
defaultTranslations extends translationsObject
// T extends languagesList = languagesList
> = Omit<
TranslateModuleConfig<defaultTranslations>,
'moduleType' | 'defaultLanguage' | 'supportedLanguages'
> & {
isLazy: boolean;
};
/**
please import only using forRoot or forChild
```ts
AppTranslateModule.forRoot({
defaultLanguage: 'he',
supportedLanguages: ['he'],
translationsChunks: {
he: () => firstValueFrom(of({ default: he })),
en: () => import('./i18n/en'),
},
});
AppTranslateModule.forChild({
isLazy: true,
translationsChunks: {
he: () => firstValueFrom(of({ default: he })),
en: () => import('./i18n/en'),
},
});
* ```
* @author Mor Bargig <morb4@fnx.co.il>
*/
@NgModule({
declarations: [],
imports: [CommonModule, TranslateModule],
providers: [AppTranslateService, TranslateModuleLoader],
exports: [TranslateModule],
})
export class AppTranslateModule {
constructor(
appTranslateService: AppTranslateService,
translateModuleLoader: TranslateModuleLoader,
@Inject(TRANSLATE_MODULE_CONFIG)
configs?: TranslateModuleConfig<any>[]
) {
if (!configs?.length) {
throw new Error(
'Please use module AppTranslateModule only with forRoot or forChild'
);
return;
}
const rootConfig = configs?.find((config) => config?.moduleType === 'root');
if (rootConfig) {
appTranslateService.init(
rootConfig?.defaultLanguage,
rootConfig?.supportedLanguages
);
} else {
const lazyChildConfig = configs?.find(
(config) => config?.moduleType === 'lazyChild'
);
if (lazyChildConfig) {
const currentLang: languagesList =
appTranslateService.currentLang || appTranslateService?.defaultLang;
appTranslateService.currentLang = '' as any;
appTranslateService.use(currentLang);
}
}
appTranslateService.onLangChange
.pipe(skip(configs?.length))
.subscribe((event) => {
firstValueFrom(translateModuleLoader.getTranslation(event.lang)).then(
(res) => {
appTranslateService.setTranslation(event.lang, res, true);
}
);
});
}
static forRoot<defaultTranslations extends translationsObject>(
config: TranslateModuleConfigForRoot<defaultTranslations>
): ModuleWithProviders<AppTranslateModule> {
// TODO: add environment configuration
const forRoot = TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: TranslateModuleLoader,
},
defaultLanguage: config?.defaultLanguage,
});
return {
ngModule: AppTranslateModule,
providers: [
TranslateModuleConfigProvider({ ...config, moduleType: 'root' }),
...forRoot.providers,
],
};
}
static forChild<defaultTranslations extends translationsObject>(
config: TranslateModuleConfigForChild<defaultTranslations>
): ModuleWithProviders<AppTranslateModule> {
const forChild = TranslateModule.forChild({
loader: {
provide: TranslateLoader,
useClass: TranslateModuleLoader,
},
extend: config?.isLazy,
});
return {
ngModule: AppTranslateModule,
providers: [
TranslateModuleConfigProvider({
...config,
moduleType: config?.isLazy ? 'lazyChild' : 'child',
}),
...forChild.providers,
],
};
}
} |
my shortest workaround FYI: example in shared core module export class SomeCoreModule implements OnDestroy {
private destroySubject = new Subject<void>();
constructor(translateService: TranslateService) {
const setLocaleForChildModule = (locale) => {
translateService.currentLoader
.getTranslation(locale)
.pipe(takeUntil(translateService.onLangChange), takeUntil(this.destroySubject))
.subscribe((translations: { [key: string]: string }) => {
translateService.setTranslation(locale, translations);
translateService.onTranslationChange.next({ lang: locale, translations: translations });
});
};
translateService.onLangChange
.pipe(
filter((x) => !!x.lang?.length),
debounceTime(1),
takeUntil(this.destroySubject)
)
.subscribe((event) => setLocaleForChildModule(event.lang));
}
static forRoot(): ModuleWithProviders<SomeCoreModule> {
return {
ngModule: SomeCoreModule,
providers: [
TranslateModule.forRoot({
isolate: false,
loader: {
provide: TranslateLoader,
useClass: CoreTranslateLoader,// for localize core module components and common cases
deps: [HttpRepositoryService],
},
}).providers,
],
};
}
ngOnDestroy(): void {
this.destroySubject.next();
this.destroySubject.complete();
}
} Profit |
I was also facing a similar issue and after some debugging, I found out(perhaps assumption) that for lazy-loaded modules the translations are not provided quickly enough to be made available in HTML. The proof of it is that if you get translations in TS or call a function to get through a TS function or if you add a condition in HTML that will be true after a few seconds then translations will work fine.
|
Works for me to. Easy and short. Thanks dude. |
Current behavior
Alternate i18n files are not loaded when using lazy-loaded modules if "isolate" param is false. So the module can access the main file translations but not theirs.
When "isolate" is true the file is correctly loaded but the module doesn't have access to previously loaded translations.
Expected behavior
Lazy loaded modules should be able to load their own translation files and at the same time being able to access previously loaded translation files as stated in the docs.
How do you think that we should fix this?
Minimal reproduction of the problem with instructions
For reproduction please follow the steps of the ngx-translate docs in a freshly angular created application with one or more lazy-loaded modules and one shared module exporting TranslateModule.
Environment
The text was updated successfully, but these errors were encountered: