From d93a3cdd1b5435d8a285aa8b5dd875869e5256f6 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 14 Sep 2023 19:53:58 +0200 Subject: [PATCH] core-app-api: add support for explicit configuration of default language Signed-off-by: Patrik Oldsberg --- .../AppLanguageApi/AppLanguageSelector.ts | 14 ++++++++--- .../I18nextTranslationApi.test.ts | 23 +++++++++++++++++++ .../TranslationApi/I18nextTranslationApi.ts | 5 ++++ packages/core-app-api/src/app/AppManager.tsx | 1 + packages/core-app-api/src/app/types.ts | 1 + 5 files changed, 41 insertions(+), 3 deletions(-) diff --git a/packages/core-app-api/src/apis/implementations/AppLanguageApi/AppLanguageSelector.ts b/packages/core-app-api/src/apis/implementations/AppLanguageApi/AppLanguageSelector.ts index fa906c4337..93f3d6517c 100644 --- a/packages/core-app-api/src/apis/implementations/AppLanguageApi/AppLanguageSelector.ts +++ b/packages/core-app-api/src/apis/implementations/AppLanguageApi/AppLanguageSelector.ts @@ -25,6 +25,7 @@ export const DEFAULT_LANGUAGE = 'en'; /** @alpha */ export interface AppLanguageSelectorOptions { + defaultLanguage?: string; availableLanguages?: string[]; } @@ -47,7 +48,14 @@ export class AppLanguageSelector implements AppLanguageApi { throw new Error(`Supported languages must include '${DEFAULT_LANGUAGE}'`); } - return new AppLanguageSelector(languages); + const initialLanguage = options?.defaultLanguage ?? DEFAULT_LANGUAGE; + if (!languages.includes(initialLanguage)) { + throw new Error( + `Initial language must be one of the supported languages, got '${initialLanguage}'`, + ); + } + + return new AppLanguageSelector(languages, initialLanguage); } static createWithStorage(options?: AppLanguageSelectorOptions) { @@ -85,9 +93,9 @@ export class AppLanguageSelector implements AppLanguageApi { #language: string; #subject: BehaviorSubject<{ language: string }>; - private constructor(languages: string[]) { + private constructor(languages: string[], initialLanguage: string) { this.#languages = languages; - this.#language = languages[0]; + this.#language = initialLanguage; this.#subject = new BehaviorSubject<{ language: string }>({ language: this.#language, }); diff --git a/packages/core-app-api/src/apis/implementations/TranslationApi/I18nextTranslationApi.test.ts b/packages/core-app-api/src/apis/implementations/TranslationApi/I18nextTranslationApi.test.ts index 2dd9f2cd56..4b0861af30 100644 --- a/packages/core-app-api/src/apis/implementations/TranslationApi/I18nextTranslationApi.test.ts +++ b/packages/core-app-api/src/apis/implementations/TranslationApi/I18nextTranslationApi.test.ts @@ -193,6 +193,29 @@ describe('I18nextTranslationApi', () => { expect(snapshot.t('foo')).toBe('OtherFoo'); }); + it('should allow initial language to not be the default one', async () => { + const languageApi = AppLanguageSelector.create({ + defaultLanguage: 'sv', + availableLanguages: ['en', 'sv'], + }); + const translationApi = I18nextTranslationApi.create({ + languageApi, + resources: [ + createTranslationResource({ + ref: plainRef, + translations: { + sv: () => Promise.resolve({ default: { foo: 'Föö' } }), + }, + }), + ], + }); + + const snapshot = assertReady( + await waitForNext(translationApi.translation$(plainRef), s => s.ready), + ); + expect(snapshot.t('foo')).toBe('Föö'); + }); + it('should prefer the last loaded resource', async () => { const languageApi = AppLanguageSelector.create({ availableLanguages: ['en', 'sv'], diff --git a/packages/core-app-api/src/apis/implementations/TranslationApi/I18nextTranslationApi.ts b/packages/core-app-api/src/apis/implementations/TranslationApi/I18nextTranslationApi.ts index e1757f5ff5..2b9c0493b2 100644 --- a/packages/core-app-api/src/apis/implementations/TranslationApi/I18nextTranslationApi.ts +++ b/packages/core-app-api/src/apis/implementations/TranslationApi/I18nextTranslationApi.ts @@ -155,6 +155,11 @@ export class I18nextTranslationApi implements TranslationApi { i18n.init(); + const { language: initialLanguage } = options.languageApi.getLanguage(); + if (initialLanguage !== DEFAULT_LANGUAGE) { + i18n.changeLanguage(initialLanguage); + } + const loader = new ResourceLoader(loaded => { i18n.addResourceBundle( loaded.language, diff --git a/packages/core-app-api/src/app/AppManager.tsx b/packages/core-app-api/src/app/AppManager.tsx index ac386c5140..048576cd4f 100644 --- a/packages/core-app-api/src/app/AppManager.tsx +++ b/packages/core-app-api/src/app/AppManager.tsx @@ -182,6 +182,7 @@ export class AppManager implements BackstageApp { this.bindRoutes = options.bindRoutes; this.apiFactoryRegistry = new ApiFactoryRegistry(); this.appLanguageApi = AppLanguageSelector.createWithStorage({ + defaultLanguage: options.__experimentalTranslations?.defaultLanguage, availableLanguages: options.__experimentalTranslations?.availableLanguages, }); diff --git a/packages/core-app-api/src/app/types.ts b/packages/core-app-api/src/app/types.ts index 42f7ff0ef2..f203ce9278 100644 --- a/packages/core-app-api/src/app/types.ts +++ b/packages/core-app-api/src/app/types.ts @@ -284,6 +284,7 @@ export type AppOptions = { bindRoutes?(context: { bind: AppRouteBinder }): void; __experimentalTranslations?: { + defaultLanguage?: string; availableLanguages?: string[]; resources?: Array; };