Bug(transloco): Missing translation error in lazy-loaded pages on initial load
Is there an existing issue for this?
- [X] I have searched the existing issues
Which Transloco package(s) are the source of the bug?
Transloco
Is this a regression?
No
Current behavior
I'm experiencing an issue with Transloco in my Angular project. In a lazy-loaded page, after refreshing, a "Missing translation for 'home'" error appears in the console on the initial load. However, when I navigate to another page and then return to the lazy-loaded page, the error no longer appears.
It seems that on the initial load, the translation file doesn’t load in time, causing the page to render before translations are available.
Steps to Reproduce: 1- Open the lazy-loaded page. 2- Refresh the page. 3- Observe the console for the "Missing translation" error. 4- Navigate to a different page. 5- Return to the lazy-loaded page. 6- Notice that the error is no longer present.
Expected behavior
When navigating to a lazy-loaded page, all required translation files should be fully loaded before the page renders, so that no "Missing translation" errors appear in the console. The page should display all translations correctly, even on the initial load after a refresh.
Please provide a link to a minimal reproduction of the bug, if you won't provide a link the issue won't be handled.
https://codesandbox.io/p/sandbox/vibrant-water-flm8df?workspaceId=c3977a48-d7c1-411a-83ed-053355f0c600
Transloco Config
No response
Please provide the environment you discovered this bug in
"@jsverse/transloco": "7.4.2"
Browser
No response
Additional context
No response
I would like to make a pull request for this bug
No
Is there an existing issue for this?
- [x] I have searched the existing issues
Which Transloco package(s) are the source of the bug?
Transloco
Is this a regression?
No
Current behavior
I'm experiencing an issue with Transloco in my Angular project. In a lazy-loaded page, after refreshing, a "Missing translation for 'home'" error appears in the console on the initial load. However, when I navigate to another page and then return to the lazy-loaded page, the error no longer appears.
It seems that on the initial load, the translation file doesn’t load in time, causing the page to render before translations are available.
Steps to Reproduce: 1- Open the lazy-loaded page. 2- Refresh the page. 3- Observe the console for the "Missing translation" error. 4- Navigate to a different page. 5- Return to the lazy-loaded page. 6- Notice that the error is no longer present.
Expected behavior
When navigating to a lazy-loaded page, all required translation files should be fully loaded before the page renders, so that no "Missing translation" errors appear in the console. The page should display all translations correctly, even on the initial load after a refresh.
Please provide a link to a minimal reproduction of the bug, if you won't provide a link the issue won't be handled.
https://codesandbox.io/p/sandbox/vibrant-water-flm8df?workspaceId=c3977a48-d7c1-411a-83ed-053355f0c600
Transloco Config
No response
Please provide the environment you discovered this bug in
"@jsverse/transloco": "7.4.2"Browser
No response
Additional context
No response
I would like to make a pull request for this bug
No
Yes, I am experiencing the same issue. When using Transloco in an Angular project, specifically on a lazy-loaded page, refreshing the page results in a "Missing translation for 'key'" error in the console. However, the issue resolves itself when navigating to another page and then returning to the lazy-loaded page.
is there any solution for it??
I tried to find a workaround for this and added this code to my app configuration, which helped me solve the problem :
{
provide: APP_INITIALIZER,
useFactory: (translocoService: TranslocoService) => () => {
// this line to make sure to load translations on first load and avoid missing trads
// https://github.com/jsverse/transloco/issues/811
lastValueFrom(translocoService.selectTranslate('app.title')).then();
},
deps: [TranslocoService],
multi: true,
},
I applied this approach to preload the general application language file.
{
provide: APP_INITIALIZER,
multi: true,
useFactory: (translocoService: TranslocoService) => {
return async () => {
await translocoService.load('fa').toPromise();
};
},
deps: [TranslocoService],
},
The problem is related to lazy loading translation files. Here's an example configuration I am using with Transloco for lazy loading a route:
{
path: "lazy",
loadComponent: () =>
import("./lazy.component").then((LazyComponent) => LazyComponent),
providers: [provideTranslocoScope("users")],
}
In the HTML file, translations work perfectly when using the pipe. However, in the TypeScript file, when I use the TranslocoService to fetch translations, the keys are not translated.
HTML example (this works fine):
<h1>{{ 'users.title' | transloco }}</h1>
TypeScript example (this has an issue):
this.translocoService.translate('users.title'); // Returns the key 'title', not the translated value
It seems that when using TranslocoService in lazy-loaded components, the translation files have not been fully loaded yet, resulting in untranslated keys in TypeScript.
Is there any specific configuration or method to ensure that translation files are fully loaded before using the TranslocoService? Or do I need an async approach to fetch translations when working in TypeScript?
Problem is that APP_INITIALIZER is deprecated and provideAppInitializer should be used instead. Not sure yet how to adjust the app.config to use it so translations would be loaded before guards.
Update: that's how we solved this (we use only single translation file per language, not separating by lazy-loaded components)
// Need to force preload of translations before the app starts (because they are used even in some guards)
makeEnvironmentProviders([
provideTranslocoLoader(TranslocoHttpLoader),
provideAppInitializer(() => firstValueFrom(inject(TranslocoService).load(DEFAULT_SYSTEM_LANGUAGE_SPECIFIC_CULTURE))),
]),
The issue I'm facing with provideTranslocoScope is that while it works fine in the HTML when using the Transloco pipe (translations are correctly applied), in the TypeScript files, when I use the TranslocoService to fetch translations, they are not recognized after the lazy-loaded module.
In other words, the translations are available in the HTML, but not in TypeScript after the module is loaded using the scope provided in the route.
Do you have any suggestions for how I can make sure the translations are accessible in TypeScript when using provideTranslocoScope in lazy-loaded routes?
Everything in my project works correctly, except in cases where I use the provideTranslocoScope provider, I encounter an issue.
I expect this to work:
{
path: "lazy",
loadComponent: () =>
import("./lazy.component").then((LazyComponent) => LazyComponent),
providers: [
provideTranslocoScope("users"),
],
}
But should it be resolved with a Resolver instead?
{
path: 'lazy',
loadComponent: () =>
import('./lazy.component').then((LazyComponent) => LazyComponent),
resolve: {
translations: TranslationResolver,
},
}
@Injectable({
providedIn: 'root',
})
export class TranslationResolver implements Resolve<any> {
constructor(private translocoService: TranslocoService) {}
resolve() {
return this.translocoService.load('users').toPromise();
}
}
Same issue, any solutions ?
happened for me, mainly when I was using declarative code with signals. I think the signals execute their first computation too soon in the component lifecycle. (the signals do not have a bug, and I think there is no bug in transloco either)
issue (simplified example):
protected readonly someComputedTranslation = computed<SomeDto>(() => {
const userName = this.userNameSignal();
return {
label: this.transloco.translate('username'),
value: userName,
};
});
solution:
import { toSignal } from '@angular/core/rxjs-interop';
private readonly usernameTranslation = toSignal(this.transloco.selectTranslate<string>('username'));
protected readonly someComputedTranslation = computed<SomeDto>(() => {
const userName = this.userNameSignal();
const userNameTranslation = this.usernameTranslation();
// translation not available yet
if (!userNameTranslation) return { label: 'user name', value: userName }
return {
label: userNameTranslation,
value: userName,
};
});
my use case was larger, and I didnt want alot of signals for each translation, so I combined them with rxjs forkJoin and used strongly typed strings.
import { forkJoin } from 'rxjs';
private readonly translations = toSignal(
forkJoin({
'first-label': this.transloco.selectTranslate<string>('first-label'),
'second-label': this.transloco.selectTranslate<string>('second-label'),
'third-label': this.transloco.selectTranslate<string>('third-label'),
}),
);
// usage
protected readonly firstLabel = computed<string>(() => this.translations()['first-label']);