Spartacus Storefront Multisite I18n with Backend

660 Views Asked by At

We've run into some problems for our MultiSite Spartacus setup when doing I18n.

  1. We'd like to have different translations for each site, so we put these on an API that can give back the messages dependent on the baseSite, eg: backend.org/baseSiteX/messages?group=common
    But the Spartacus setup doesn't let us pass the baseSite? We can pass {{lng}} and {{ns}}, but no baseSite.
    See https://sap.github.io/spartacus-docs/i18n/#lazy-loading We'd could do it by overriding i18nextInit, but I'm unsure how to achieve this.

  2. In the documentation, it says you can use crossOrigin: true in the config, but that does not seem to work. The type-checking say it's unsupported, and it still shows uw CORS-issues

Does someone have ideas for these problems?

3

There are 3 best solutions below

0
On BEST ANSWER

Currently only language {{lng}} and chunk name {{ns}} are supported as dynamic params in the i18n.backend.loadPath config.

To achieve your goal, you can implement a custom Spartacus CONFIG_INITIALIZER to will populate your i18n.backend.loadPath config based on the value from the BaseSiteService.getActive():

@Injectable({ providedIn: 'root' })
export class I18nBackendPathConfigInitializer implements ConfigInitializer {
  readonly scopes = ['i18n.backend.loadPath']; // declare config key that you will resolve
  readonly configFactory = () => this.resolveConfig().toPromise();

  constructor(protected baseSiteService: BaseSiteService) {}

  protected resolveConfig(): Observable<I18nConfig> {
    return this.baseSiteService.getActive().pipe(
      take(1),
      map((baseSite) => ({
        i18n: {
          backend: {
            // initialize your i18n backend path using the basesite value:
            loadPath: `https://backend.org/${baseSite}/messages?lang={{lng}}&group={{ns}}`,
          },
        },
      }))
    );
  }
}

and provide it in your module (i.e. in app.module):

@NgModule({
  providers: [
    {
      provide: CONFIG_INITIALIZER,
      useExisting: I18nBackendPathConfigInitializer,
      multi: true,
    },
  ],
  /* ... */
})

Note: the above solution assumes the active basesite is set only once, on app start (which is the case in Spartacus by default).

1
On

@krzysztof-platis we implemented this suggestion, it seemed to work in Spartacus 4.x locally and in CCV2, with the new 5.2 Version, it does not seem to work anymore.. (neither locally nor CCV2, as long as no occ url is defined in the environment file)

our config initializer:

export class CustomI18nConfigInitializer extends I18nConfigInitializer {
  readonly scopes = ['i18n.backend.loadPath']; // declare config key that you will resolve
  readonly configFactory = () => this.resolveConfig().toPromise();

  constructor(
    protected configInit: ConfigInitializerService,
    private occEndpoints: OccEndpointsService,
    private baseSiteService: BaseSiteService
  ) {
    super(configInit);
  }

  protected resolveConfig(): Observable<I18nConfig> {
    return this.baseSiteService.getActive().pipe(
      take(1),
      map(() => {
        // eslint-disable-next-line no-console
        console.log('i18n config initializer, load path: ' + this.occEndpoints.buildUrl('i18n'));
        return {
          i18n: {
            backend: {
              loadPath: this.occEndpoints.buildUrl('i18n'),
            },
          },
        } as Config;
      })
    );
  }
}

our app module:

providers: [
    {
      provide: CONFIG_INITIALIZER,
      useExisting: CustomI18nConfigInitializer,
      multi: true,
    },
  ],

our i18n config factory:

export function i18nConfigFactory(): I18nConfig {
  const prefix = environment.occ.prefix ?? '';
  const baseUrl = environment.occ.baseUrl ?? '';

  const url = `${baseUrl}${prefix}<site-id>/i18n/{{lng}}/{{ns}}`;
  // eslint-disable-next-line no-console
  console.log('i18n config factory, base url: ' + baseUrl);
  return {
    i18n: {
      backend: {
        // value get's replaced in the CustomI18nConfigInitializer with the proper OCC url since we need the base site id in the url
        loadPath: url,
      },
      chunks: {
        ...translationChunksConfig,
        cart: [...cartBaseTranslationChunksConfig.cart],
        checkout: [...checkoutTranslationChunksConfig.checkout, 'checkoutGuest', 'checkoutShipping'],
        order: [...orderTranslationChunksConfig.order],
        storeFinder: [...storeFinderTranslationChunksConfig.storeFinder],
        userAccount: [...userAccountTranslationChunksConfig.userAccount],
        userProfile: [...userProfileTranslationChunksConfig.userProfile],
      },
      fallbackLang: 'de',
    },
  };
}

Is the idea to have both an i18nConfig AND an i18nConfigInitializer or should I use just one of them?

I tried to not use a config factory and entirely use the dynamic config intializer, but then it seems that spartacus thinks, the translations will be provided within the app and not be lazy loaded.

If I just use the i18nConfig with a placeholder loadPath value, I get a 404. For me, it therefore seems, that the logic from the dynamic config initializer is run too late after the translations where already loaded.

any idea?

0
On

I can confirm that after update Spartacus to 5.x version dynami i18n backend loadPath is not working anymore :(

We have a little bit different aproach, but the same like for @user2477219 it stopped working :(

const initializeBackendUrl = (meta: Meta) => {
    const backendUrlFromEnvConfig = environment.backend.occ.baseUrl; // local
    const backendUrlFromMetaTag = meta.getTag('name=occ-backend-base-url').content; // ccv2
    const backendUrlToUse = backendUrlFromEnvConfig ? backendUrlFromEnvConfig : backendUrlFromMetaTag; // env config has priority
    const baseUrl = backendUrlToUse.replace('https://int-', 'https://');
    i18Backend.loadPath = baseUrl + '/rest/v2/customerPortal/translations/{{ns}}.json';
    return {
        backend: {
            occ: {
                baseUrl: baseUrl,
            },
        },
    };
};

const occConfig: OccConfig = {
    backend: {
        occ: {
            prefix: '/rest/v2/',
        },
    },
};



provideConfigFactory(initializeBackendUrl, [Meta]),
provideConfig({
    backend: occConfig.backend,
    i18n: {
        backend: i18Backend,
        chunks: {
            ...translationChunksConfig,
            custom: ['tok'],
        },
        fallbackLang: 'en',
    },
});