Skip to content

TranslateService fails to load translations in standalone component within micro-frontend architecture​ #1539

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

Closed
1 task
ap-sunilkamble opened this issue Apr 16, 2025 · 6 comments

Comments

@ap-sunilkamble
Copy link

ap-sunilkamble commented Apr 16, 2025

We are encountering an issue where TranslateService does not load translations in a standalone component when used within a micro-frontend setup. The translation files are accessible directly via the browser, but the translations are not applied within the component.

I have the following setup for my microfrontend angular19 + native federation
host --running on-- localhost:4200
profile --running on-- localhost:4202
settings --running on-- localhost:4203
reports --running on-- localhost:4204
each project has its own/separate en.json

Current behavior

Navigating directly to the host app (e.g., http://localhost:4200) loads en.json and translations work. But if I navigate from host app to reports app the respective en.json from the reports app is not getting called.

Visiting http://localhost:4204/assets/i18n/en.json directly returns the JSON correctly.

TranslateService.use('en') is called in FindReportsComponent in the MFE.

MFE uses TranslateHttpLoader with import.meta.url to determine its own base path.

Expected behavior

When navigating to the reports app via the host app using native federation, the translation file should be fetched and The standalone component should display translated strings as defined in en.json

How do you think that we should fix this?

As per the documentation, it is understood that we can have two separate mechanisms,
TranslateModule.forRoot() and TranslateModule.forChild() here forChild() is not working as expected

Minimal reproduction of the problem with instructions

Set up a host Angular application.
Create a remote Angular application exposing a standalone component.
In the remote application's app.config.ts, configure the translation loader

import { ApplicationConfig, importProvidersFrom } from '@angular/core';
import { provideRouter } from '@angular/router';
import { HttpClient } from '@angular/common/http';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';

export const httpLoaderFactory = (http: HttpClient) =>
  new TranslateHttpLoader(http, './assets/i18n/', '.json');

export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter([]),
    importProvidersFrom([
      TranslateModule.forChild({
        loader: {
          provide: TranslateLoader,
          useFactory: httpLoaderFactory,
          deps: [HttpClient],
        },
      }),
    ]),
  ],
};
  1. In the standalone component, inject TranslateService and set the default language:
import { Component, OnInit } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';

@Component({
  selector: 'app-standalone-component',
  templateUrl: './standalone-component.component.html',
})
export class StandaloneComponent implements OnInit {
  constructor(private translate: TranslateService) {}

  ngOnInit(): void {
    this.translate.setDefaultLang('en');
    this.translate.use('en');
  }
}
  1. Expose the standalone component via Module Federation. 6. In the host application, navigate to the remote component.

Environment

Angular Version: 18
@ngx-translate/core Version: 16.0.4
@ngx-translate/http-loader Version: 16.0.4
Architecture: Micro-frontend using Angular's standalone components
Translation Files Location: /assets/i18n/en.json

Browser:

  • Chrome (desktop)

For Tooling issues:

  • Node version: v22.13.0
  • Platform: Windows

package.json

@luisrmmalaga
Copy link

luisrmmalaga commented Apr 28, 2025

I'm stuck with the same issue. I'm ussing Module Federation to build my microfrontend architecture. I'm configured ngx-translate in the app bootstraping files and I added i18n json files to each mfe. When I navigate to host app, translations don`t work but if I navigate to functional mfe url, I can see translations. I want each mfe translations to be independent from host.

@CodeAndWeb
Copy link
Member

Can you please make a stand-alone demo in a GitHub repo for this?

@stherrienaspnet
Copy link

stherrienaspnet commented May 14, 2025

I have a similar issue. It is random. It feel like remote loaded component do not share the same instance of translateService.

@rbalet
Copy link
Collaborator

rbalet commented May 15, 2025

Hey @ap-sunilkamble!

I'll try to help you solve your issue, but I'll need your help ;)
Here's what we can try, please let me know if it helped. If it doesn't keep it like that as this is either way cleaner ; )

1. Move to standalone

Replace the old

    importProvidersFrom([
      TranslateModule.forChild({
        loader: {
          provide: TranslateLoader,
          useFactory: httpLoaderFactory,
          deps: [HttpClient],
        },
      }),
    ]),

With the new

 provideTranslateService({
      loader: {
          provide: TranslateLoader,
          useFactory: httpLoaderFactory,
          deps: [HttpClient]
      }
  })

2. Replace .forChild

Follow the following answer to handle per route load.
If you need it per component, then just call the method inside an ngOnInit or else.

Bonus. Instantiate sooner

@stherrienaspnet This one could help you

The following is known to be error prone, prefer calling .setDefaultLang and use as soon as possible.

export class StandaloneComponent implements OnInit {
  constructor(private translate: TranslateService) {}

  ngOnInit(): void {
    this.translate.setDefaultLang('en');
    this.translate.use('en');
  }
}

Why not directly inside the main.ts file ?

export function initApp(
  translateService: TranslateService,
) { 
  return () => {
     const fallbackLang = 'en';
     const browserLang = translateService.getBrowserLang();
     const usedLang = localStorage.getItem('LNG_KEY') || browserLang?.match(/en|fr|it/) ? browserLang : fallbackLang;

    translateService.setDefaultLang(fallbackLang);
    translateService.use(usedLang);
  }
}

//...
providers: [
  provideAppInitializer( async () => {
   const translateService = inject(TranslateService)    ;
   initApp(translateService)();
  }
]

@sunilmkamble17
Copy link

@rbalet @CodeAndWeb :
I have tried the suggested changes, but they didn't work.
Here I am providing GitHub repo to reproduce the scenario as mentioned above,

https://github.com/sunilmkamble17/mfe_profile.git

  1. npm i (if "npm i" doesn't work, try "npm i --force")
  2. ng serve (project will run on port 4202)

https://github.com/sunilmkamble17/mfe_host.git

  1. npm i
  2. ng serve (project will run on port 4200)

In this setup, both projects have their own/individual en.json files
for the host app, translation works properly, but for profile mfe it doesn't work

@CodeAndWeb
Copy link
Member

The problem in your code is that the loader does not find the translations.
Please check the error messages in the console.

export const httpLoaderFactory = (http: HttpClient) =>
  new TranslateHttpLoader(http, new URL('./i18n/', import.meta.url).toString(), '.json');

With this, there's a / missing and the file it's looking for is now "i18nen.json" instead of "i18n/en.json".

Also the assets don't seem to be properly shared...

Image

vs

Image

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