1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-10-14 06:48:18 +02:00

Auth/PM-13318 - AnonLayoutWrapperData Refactor to add full Translation support (#11513)

* PM-13318 - AnonLayoutWrapperData refactor to support all possible string scenarios (untranslated string, translated string, and translated string with placeholders)

* PM-13318 - Fix accidental check in

* PM-13318 - Revert the correct change.

* PM-13318 - Fix test failures
This commit is contained in:
Jared Snider 2024-10-11 16:02:47 -04:00 committed by GitHub
parent 784dd3c9a0
commit 7297d0fccd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 191 additions and 92 deletions

View File

@ -9,7 +9,7 @@ import {
AnonLayoutWrapperDataService, AnonLayoutWrapperDataService,
} from "@bitwarden/auth/angular"; } from "@bitwarden/auth/angular";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { Icon, IconModule } from "@bitwarden/components"; import { Icon, IconModule, Translation } from "@bitwarden/components";
import { PopOutComponent } from "../../../platform/popup/components/pop-out.component"; import { PopOutComponent } from "../../../platform/popup/components/pop-out.component";
import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component"; import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component";
@ -90,11 +90,11 @@ export class ExtensionAnonLayoutWrapperComponent implements OnInit, OnDestroy {
} }
if (firstChildRouteData["pageTitle"] !== undefined) { if (firstChildRouteData["pageTitle"] !== undefined) {
this.pageTitle = this.i18nService.t(firstChildRouteData["pageTitle"]); this.pageTitle = this.handleStringOrTranslation(firstChildRouteData["pageTitle"]);
} }
if (firstChildRouteData["pageSubtitle"] !== undefined) { if (firstChildRouteData["pageSubtitle"] !== undefined) {
this.pageSubtitle = this.i18nService.t(firstChildRouteData["pageSubtitle"]); this.pageSubtitle = this.handleStringOrTranslation(firstChildRouteData["pageSubtitle"]);
} }
if (firstChildRouteData["pageIcon"] !== undefined) { if (firstChildRouteData["pageIcon"] !== undefined) {
@ -132,19 +132,11 @@ export class ExtensionAnonLayoutWrapperComponent implements OnInit, OnDestroy {
} }
if (data.pageTitle) { if (data.pageTitle) {
this.pageTitle = this.i18nService.t(data.pageTitle); this.pageTitle = this.handleStringOrTranslation(data.pageTitle);
} }
if (data.pageSubtitle) { if (data.pageSubtitle) {
// If you pass just a string, we translate it by default this.pageSubtitle = this.handleStringOrTranslation(data.pageSubtitle);
if (typeof data.pageSubtitle === "string") {
this.pageSubtitle = this.i18nService.t(data.pageSubtitle);
} else {
// if you pass an object, you can specify if you want to translate it or not
this.pageSubtitle = data.pageSubtitle.translate
? this.i18nService.t(data.pageSubtitle.subtitle)
: data.pageSubtitle.subtitle;
}
} }
if (data.pageIcon) { if (data.pageIcon) {
@ -168,6 +160,16 @@ export class ExtensionAnonLayoutWrapperComponent implements OnInit, OnDestroy {
} }
} }
private handleStringOrTranslation(value: string | Translation): string {
if (typeof value === "string") {
// If it's a string, return it as is
return value;
}
// If it's a Translation object, translate it
return this.i18nService.t(value.key, ...(value.placeholders ?? []));
}
private resetPageData() { private resetPageData() {
this.pageTitle = null; this.pageTitle = null;
this.pageSubtitle = null; this.pageSubtitle = null;

View File

@ -221,8 +221,12 @@ export const DefaultContentExample: Story = {
// Dynamic Content Example // Dynamic Content Example
const initialData: ExtensionAnonLayoutWrapperData = { const initialData: ExtensionAnonLayoutWrapperData = {
pageTitle: "setAStrongPassword", pageTitle: {
pageSubtitle: "finishCreatingYourAccountBySettingAPassword", key: "setAStrongPassword",
},
pageSubtitle: {
key: "finishCreatingYourAccountBySettingAPassword",
},
pageIcon: LockIcon, pageIcon: LockIcon,
showAcctSwitcher: true, showAcctSwitcher: true,
showBackButton: true, showBackButton: true,
@ -230,8 +234,12 @@ const initialData: ExtensionAnonLayoutWrapperData = {
}; };
const changedData: ExtensionAnonLayoutWrapperData = { const changedData: ExtensionAnonLayoutWrapperData = {
pageTitle: "enterpriseSingleSignOn", pageTitle: {
pageSubtitle: "checkYourEmail", key: "enterpriseSingleSignOn",
},
pageSubtitle: {
key: "checkYourEmail",
},
pageIcon: RegistrationCheckEmailIcon, pageIcon: RegistrationCheckEmailIcon,
showAcctSwitcher: false, showAcctSwitcher: false,
showBackButton: false, showBackButton: false,

View File

@ -449,7 +449,9 @@ const routes: Routes = [
canActivate: [canAccessFeature(FeatureFlag.ExtensionRefresh), lockGuard()], canActivate: [canAccessFeature(FeatureFlag.ExtensionRefresh), lockGuard()],
data: { data: {
pageIcon: LockIcon, pageIcon: LockIcon,
pageTitle: "yourVaultIsLockedV2", pageTitle: {
key: "yourVaultIsLockedV2",
},
showReadonlyHostname: true, showReadonlyHostname: true,
showAcctSwitcher: true, showAcctSwitcher: true,
} satisfies ExtensionAnonLayoutWrapperData, } satisfies ExtensionAnonLayoutWrapperData,
@ -471,7 +473,9 @@ const routes: Routes = [
canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()], canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()],
data: { data: {
state: "signup", state: "signup",
pageTitle: "createAccount", pageTitle: {
key: "createAccount",
},
} satisfies RouteDataProperties & AnonLayoutWrapperData, } satisfies RouteDataProperties & AnonLayoutWrapperData,
children: [ children: [
{ {
@ -492,8 +496,12 @@ const routes: Routes = [
path: "finish-signup", path: "finish-signup",
canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()], canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()],
data: { data: {
pageTitle: "setAStrongPassword", pageTitle: {
pageSubtitle: "finishCreatingYourAccountBySettingAPassword", key: "setAStrongPassword",
},
pageSubtitle: {
key: "finishCreatingYourAccountBySettingAPassword",
},
state: "finish-signup", state: "finish-signup",
} satisfies RouteDataProperties & AnonLayoutWrapperData, } satisfies RouteDataProperties & AnonLayoutWrapperData,
children: [ children: [
@ -508,8 +516,12 @@ const routes: Routes = [
canActivate: [canAccessFeature(FeatureFlag.EmailVerification)], canActivate: [canAccessFeature(FeatureFlag.EmailVerification)],
component: SetPasswordJitComponent, component: SetPasswordJitComponent,
data: { data: {
pageTitle: "joinOrganization", pageTitle: {
pageSubtitle: "finishJoiningThisOrganizationBySettingAMasterPassword", key: "joinOrganization",
},
pageSubtitle: {
key: "finishJoiningThisOrganizationBySettingAMasterPassword",
},
state: "set-password-jit", state: "set-password-jit",
} satisfies RouteDataProperties & AnonLayoutWrapperData, } satisfies RouteDataProperties & AnonLayoutWrapperData,
}, },

View File

@ -141,8 +141,12 @@ const routes: Routes = [
path: "hint", path: "hint",
canActivate: [unauthGuardFn()], canActivate: [unauthGuardFn()],
data: { data: {
pageTitle: "requestPasswordHint", pageTitle: {
pageSubtitle: "enterYourAccountEmailAddressAndYourPasswordHintWillBeSentToYou", key: "requestPasswordHint",
},
pageSubtitle: {
key: "enterYourAccountEmailAddressAndYourPasswordHintWillBeSentToYou",
},
pageIcon: UserLockIcon, pageIcon: UserLockIcon,
} satisfies AnonLayoutWrapperData, } satisfies AnonLayoutWrapperData,
children: [ children: [
@ -164,7 +168,11 @@ const routes: Routes = [
{ {
path: "signup", path: "signup",
canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()], canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()],
data: { pageTitle: "createAccount" } satisfies AnonLayoutWrapperData, data: {
pageTitle: {
key: "createAccount",
},
} satisfies AnonLayoutWrapperData,
children: [ children: [
{ {
path: "", path: "",
@ -184,8 +192,12 @@ const routes: Routes = [
path: "finish-signup", path: "finish-signup",
canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()], canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()],
data: { data: {
pageTitle: "setAStrongPassword", pageTitle: {
pageSubtitle: "finishCreatingYourAccountBySettingAPassword", key: "setAStrongPassword",
},
pageSubtitle: {
key: "finishCreatingYourAccountBySettingAPassword",
},
} satisfies AnonLayoutWrapperData, } satisfies AnonLayoutWrapperData,
children: [ children: [
{ {
@ -199,7 +211,9 @@ const routes: Routes = [
canActivate: [canAccessFeature(FeatureFlag.ExtensionRefresh), lockGuard()], canActivate: [canAccessFeature(FeatureFlag.ExtensionRefresh), lockGuard()],
data: { data: {
pageIcon: LockIcon, pageIcon: LockIcon,
pageTitle: "yourVaultIsLockedV2", pageTitle: {
key: "yourVaultIsLockedV2",
},
showReadonlyHostname: true, showReadonlyHostname: true,
} satisfies AnonLayoutWrapperData, } satisfies AnonLayoutWrapperData,
children: [ children: [
@ -214,8 +228,12 @@ const routes: Routes = [
canActivate: [canAccessFeature(FeatureFlag.EmailVerification)], canActivate: [canAccessFeature(FeatureFlag.EmailVerification)],
component: SetPasswordJitComponent, component: SetPasswordJitComponent,
data: { data: {
pageTitle: "joinOrganization", pageTitle: {
pageSubtitle: "finishJoiningThisOrganizationBySettingAMasterPassword", key: "joinOrganization",
},
pageSubtitle: {
key: "finishJoiningThisOrganizationBySettingAMasterPassword",
},
} satisfies AnonLayoutWrapperData, } satisfies AnonLayoutWrapperData,
}, },
], ],

View File

@ -14,22 +14,24 @@ describe("freeTrialTextResolver", () => {
it("shows password manager text", () => { it("shows password manager text", () => {
route.queryParams.product = `${ProductType.PasswordManager}`; route.queryParams.product = `${ProductType.PasswordManager}`;
expect(freeTrialTextResolver(route, routerStateSnapshot)).toBe( expect(freeTrialTextResolver(route, routerStateSnapshot)).toEqual({
"continueSettingUpFreeTrialPasswordManager", key: "continueSettingUpFreeTrialPasswordManager",
); });
}); });
it("shows secret manager text", () => { it("shows secret manager text", () => {
route.queryParams.product = `${ProductType.SecretsManager}`; route.queryParams.product = `${ProductType.SecretsManager}`;
expect(freeTrialTextResolver(route, routerStateSnapshot)).toBe( expect(freeTrialTextResolver(route, routerStateSnapshot)).toEqual({
"continueSettingUpFreeTrialSecretsManager", key: "continueSettingUpFreeTrialSecretsManager",
); });
}); });
it("shows default text", () => { it("shows default text", () => {
route.queryParams.product = `${ProductType.PasswordManager},${ProductType.SecretsManager}`; route.queryParams.product = `${ProductType.PasswordManager},${ProductType.SecretsManager}`;
expect(freeTrialTextResolver(route, routerStateSnapshot)).toBe("continueSettingUpFreeTrial"); expect(freeTrialTextResolver(route, routerStateSnapshot)).toEqual({
key: "continueSettingUpFreeTrial",
});
}); });
}); });

View File

@ -1,10 +1,11 @@
import { ActivatedRouteSnapshot, ResolveFn } from "@angular/router"; import { ActivatedRouteSnapshot, ResolveFn } from "@angular/router";
import { ProductType } from "@bitwarden/common/billing/enums"; import { ProductType } from "@bitwarden/common/billing/enums";
import { Translation } from "@bitwarden/components";
export const freeTrialTextResolver: ResolveFn<string | null> = ( export const freeTrialTextResolver: ResolveFn<Translation | null> = (
route: ActivatedRouteSnapshot, route: ActivatedRouteSnapshot,
): string | null => { ): Translation | null => {
const { product } = route.queryParams; const { product } = route.queryParams;
const products: ProductType[] = (product ?? "").split(",").map((p: string) => parseInt(p)); const products: ProductType[] = (product ?? "").split(",").map((p: string) => parseInt(p));
@ -13,10 +14,16 @@ export const freeTrialTextResolver: ResolveFn<string | null> = (
switch (true) { switch (true) {
case onlyPasswordManager: case onlyPasswordManager:
return "continueSettingUpFreeTrialPasswordManager"; return {
key: "continueSettingUpFreeTrialPasswordManager",
};
case onlySecretsManager: case onlySecretsManager:
return "continueSettingUpFreeTrialSecretsManager"; return {
key: "continueSettingUpFreeTrialSecretsManager",
};
default: default:
return "continueSettingUpFreeTrial"; return {
key: "continueSettingUpFreeTrial",
};
} }
}; };

View File

@ -228,7 +228,9 @@ const routes: Routes = [
path: "signup", path: "signup",
canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()], canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()],
data: { data: {
pageTitle: "createAccount", pageTitle: {
key: "createAccount",
},
titleId: "createAccount", titleId: "createAccount",
} satisfies RouteDataProperties & AnonLayoutWrapperData, } satisfies RouteDataProperties & AnonLayoutWrapperData,
children: [ children: [
@ -250,8 +252,12 @@ const routes: Routes = [
path: "finish-signup", path: "finish-signup",
canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()], canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()],
data: { data: {
pageTitle: "setAStrongPassword", pageTitle: {
pageSubtitle: "finishCreatingYourAccountBySettingAPassword", key: "setAStrongPassword",
},
pageSubtitle: {
key: "finishCreatingYourAccountBySettingAPassword",
},
titleId: "setAStrongPassword", titleId: "setAStrongPassword",
} satisfies RouteDataProperties & AnonLayoutWrapperData, } satisfies RouteDataProperties & AnonLayoutWrapperData,
children: [ children: [
@ -264,7 +270,9 @@ const routes: Routes = [
{ {
path: "send/:sendId/:key", path: "send/:sendId/:key",
data: { data: {
pageTitle: "viewSend", pageTitle: {
key: "viewSend",
},
showReadonlyHostname: true, showReadonlyHostname: true,
} satisfies RouteDataProperties & AnonLayoutWrapperData, } satisfies RouteDataProperties & AnonLayoutWrapperData,
children: [ children: [
@ -284,15 +292,21 @@ const routes: Routes = [
canActivate: [canAccessFeature(FeatureFlag.EmailVerification)], canActivate: [canAccessFeature(FeatureFlag.EmailVerification)],
component: SetPasswordJitComponent, component: SetPasswordJitComponent,
data: { data: {
pageTitle: "joinOrganization", pageTitle: {
pageSubtitle: "finishJoiningThisOrganizationBySettingAMasterPassword", key: "joinOrganization",
},
pageSubtitle: {
key: "finishJoiningThisOrganizationBySettingAMasterPassword",
},
} satisfies AnonLayoutWrapperData, } satisfies AnonLayoutWrapperData,
}, },
{ {
path: "signup-link-expired", path: "signup-link-expired",
canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()], canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()],
data: { data: {
pageTitle: "expiredLink", pageTitle: {
key: "expiredLink",
},
} satisfies AnonLayoutWrapperData, } satisfies AnonLayoutWrapperData,
children: [ children: [
{ {
@ -308,7 +322,9 @@ const routes: Routes = [
path: "sso", path: "sso",
canActivate: [unauthGuardFn()], canActivate: [unauthGuardFn()],
data: { data: {
pageTitle: "enterpriseSingleSignOn", pageTitle: {
key: "enterpriseSingleSignOn",
},
titleId: "enterpriseSingleSignOn", titleId: "enterpriseSingleSignOn",
} satisfies RouteDataProperties & AnonLayoutWrapperData, } satisfies RouteDataProperties & AnonLayoutWrapperData,
children: [ children: [
@ -338,7 +354,9 @@ const routes: Routes = [
}, },
], ],
data: { data: {
pageTitle: "logIn", pageTitle: {
key: "logIn",
},
}, },
}, },
...extensionRefreshSwap( ...extensionRefreshSwap(
@ -354,7 +372,9 @@ const routes: Routes = [
}, },
], ],
data: { data: {
pageTitle: "yourVaultIsLockedV2", pageTitle: {
key: "yourVaultIsLockedV2",
},
pageIcon: LockIcon, pageIcon: LockIcon,
showReadonlyHostname: true, showReadonlyHostname: true,
} satisfies AnonLayoutWrapperData, } satisfies AnonLayoutWrapperData,
@ -369,7 +389,9 @@ const routes: Routes = [
}, },
], ],
data: { data: {
pageTitle: "yourAccountIsLocked", pageTitle: {
key: "yourAccountIsLocked",
},
pageIcon: LockIcon, pageIcon: LockIcon,
showReadonlyHostname: true, showReadonlyHostname: true,
} satisfies AnonLayoutWrapperData, } satisfies AnonLayoutWrapperData,
@ -390,7 +412,9 @@ const routes: Routes = [
}, },
], ],
data: { data: {
pageTitle: "verifyIdentity", pageTitle: {
key: "verifyIdentity",
},
} satisfies RouteDataProperties & AnonLayoutWrapperData, } satisfies RouteDataProperties & AnonLayoutWrapperData,
}, },
{ {
@ -408,7 +432,9 @@ const routes: Routes = [
}, },
], ],
data: { data: {
pageTitle: "recoverAccountTwoStep", pageTitle: {
key: "recoverAccountTwoStep",
},
titleId: "recoverAccountTwoStep", titleId: "recoverAccountTwoStep",
} satisfies RouteDataProperties & AnonLayoutWrapperData, } satisfies RouteDataProperties & AnonLayoutWrapperData,
}, },
@ -416,7 +442,9 @@ const routes: Routes = [
path: "accept-emergency", path: "accept-emergency",
canActivate: [deepLinkGuard()], canActivate: [deepLinkGuard()],
data: { data: {
pageTitle: "emergencyAccess", pageTitle: {
key: "emergencyAccess",
},
titleId: "acceptEmergency", titleId: "acceptEmergency",
doNotSaveUrl: false, doNotSaveUrl: false,
} satisfies RouteDataProperties & AnonLayoutWrapperData, } satisfies RouteDataProperties & AnonLayoutWrapperData,
@ -434,7 +462,9 @@ const routes: Routes = [
path: "recover-delete", path: "recover-delete",
canActivate: [unauthGuardFn()], canActivate: [unauthGuardFn()],
data: { data: {
pageTitle: "deleteAccount", pageTitle: {
key: "deleteAccount",
},
titleId: "deleteAccount", titleId: "deleteAccount",
} satisfies RouteDataProperties & AnonLayoutWrapperData, } satisfies RouteDataProperties & AnonLayoutWrapperData,
children: [ children: [
@ -453,7 +483,9 @@ const routes: Routes = [
path: "verify-recover-delete", path: "verify-recover-delete",
canActivate: [unauthGuardFn()], canActivate: [unauthGuardFn()],
data: { data: {
pageTitle: "deleteAccount", pageTitle: {
key: "deleteAccount",
},
titleId: "deleteAccount", titleId: "deleteAccount",
} satisfies RouteDataProperties & AnonLayoutWrapperData, } satisfies RouteDataProperties & AnonLayoutWrapperData,
children: [ children: [
@ -468,7 +500,9 @@ const routes: Routes = [
component: RemovePasswordComponent, component: RemovePasswordComponent,
canActivate: [authGuard], canActivate: [authGuard],
data: { data: {
pageTitle: "removeMasterPassword", pageTitle: {
key: "removeMasterPassword",
},
titleId: "removeMasterPassword", titleId: "removeMasterPassword",
} satisfies RouteDataProperties & AnonLayoutWrapperData, } satisfies RouteDataProperties & AnonLayoutWrapperData,
}, },

View File

@ -157,8 +157,8 @@ export class AccessComponent implements OnInit {
if (this.creatorIdentifier != null) { if (this.creatorIdentifier != null) {
this.layoutWrapperDataService.setAnonLayoutWrapperData({ this.layoutWrapperDataService.setAnonLayoutWrapperData({
pageSubtitle: { pageSubtitle: {
subtitle: this.i18nService.t("sendAccessCreatorIdentifier", this.creatorIdentifier), key: "sendAccessCreatorIdentifier",
translate: false, placeholders: [this.creatorIdentifier],
}, },
}); });
} }

View File

@ -4,20 +4,34 @@ import { Subject, filter, switchMap, takeUntil, tap } from "rxjs";
import { AnonLayoutComponent } from "@bitwarden/auth/angular"; import { AnonLayoutComponent } from "@bitwarden/auth/angular";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { Icon } from "@bitwarden/components"; import { Icon, Translation } from "@bitwarden/components";
import { AnonLayoutWrapperDataService } from "./anon-layout-wrapper-data.service"; import { AnonLayoutWrapperDataService } from "./anon-layout-wrapper-data.service";
export interface AnonLayoutWrapperData { export interface AnonLayoutWrapperData {
pageTitle?: string; /**
pageSubtitle?: * The optional title of the page.
| string * If a string is provided, it will be presented as is (ex: Organization name)
| { * If a Translation object (supports placeholders) is provided, it will be translated
subtitle: string; */
translate: boolean; pageTitle?: string | Translation;
}; /**
* The optional subtitle of the page.
* If a string is provided, it will be presented as is (ex: user's email)
* If a Translation object (supports placeholders) is provided, it will be translated
*/
pageSubtitle?: string | Translation;
/**
* The optional icon to display on the page.
*/
pageIcon?: Icon; pageIcon?: Icon;
/**
* Optional flag to either show the optional environment selector (false) or just a readonly hostname (true).
*/
showReadonlyHostname?: boolean; showReadonlyHostname?: boolean;
/**
* Optional flag to set the max-width of the page. Defaults to 'md' if not provided.
*/
maxWidth?: "md" | "3xl"; maxWidth?: "md" | "3xl";
} }
@ -71,11 +85,11 @@ export class AnonLayoutWrapperComponent implements OnInit, OnDestroy {
} }
if (firstChildRouteData["pageTitle"] !== undefined) { if (firstChildRouteData["pageTitle"] !== undefined) {
this.pageTitle = this.i18nService.t(firstChildRouteData["pageTitle"]); this.pageTitle = this.handleStringOrTranslation(firstChildRouteData["pageTitle"]);
} }
if (firstChildRouteData["pageSubtitle"] !== undefined) { if (firstChildRouteData["pageSubtitle"] !== undefined) {
this.pageSubtitle = this.i18nService.t(firstChildRouteData["pageSubtitle"]); this.pageSubtitle = this.handleStringOrTranslation(firstChildRouteData["pageSubtitle"]);
} }
if (firstChildRouteData["pageIcon"] !== undefined) { if (firstChildRouteData["pageIcon"] !== undefined) {
@ -101,19 +115,11 @@ export class AnonLayoutWrapperComponent implements OnInit, OnDestroy {
} }
if (data.pageTitle) { if (data.pageTitle) {
this.pageTitle = this.i18nService.t(data.pageTitle); this.pageTitle = this.handleStringOrTranslation(data.pageTitle);
} }
if (data.pageSubtitle) { if (data.pageSubtitle) {
// If you pass just a string, we translate it by default this.pageSubtitle = this.handleStringOrTranslation(data.pageSubtitle);
if (typeof data.pageSubtitle === "string") {
this.pageSubtitle = this.i18nService.t(data.pageSubtitle);
} else {
// if you pass an object, you can specify if you want to translate it or not
this.pageSubtitle = data.pageSubtitle.translate
? this.i18nService.t(data.pageSubtitle.subtitle)
: data.pageSubtitle.subtitle;
}
} }
if (data.pageIcon) { if (data.pageIcon) {
@ -129,6 +135,16 @@ export class AnonLayoutWrapperComponent implements OnInit, OnDestroy {
this.changeDetectorRef.detectChanges(); this.changeDetectorRef.detectChanges();
} }
private handleStringOrTranslation(value: string | Translation): string {
if (typeof value === "string") {
// If it's a string, return it as is
return value;
}
// If it's a Translation object, translate it
return this.i18nService.t(value.key, ...(value.placeholders ?? []));
}
private resetPageData() { private resetPageData() {
this.pageTitle = null; this.pageTitle = null;
this.pageSubtitle = null; this.pageSubtitle = null;

View File

@ -163,17 +163,20 @@ export const DefaultContentExample: Story = {
// Dynamic Content Example // Dynamic Content Example
const initialData: AnonLayoutWrapperData = { const initialData: AnonLayoutWrapperData = {
pageTitle: "setAStrongPassword", pageTitle: {
pageSubtitle: "finishCreatingYourAccountBySettingAPassword", key: "setAStrongPassword",
},
pageSubtitle: {
key: "finishCreatingYourAccountBySettingAPassword",
},
pageIcon: LockIcon, pageIcon: LockIcon,
}; };
const changedData: AnonLayoutWrapperData = { const changedData: AnonLayoutWrapperData = {
pageTitle: "enterpriseSingleSignOn", pageTitle: {
pageSubtitle: { key: "enterpriseSingleSignOn",
subtitle: "user@email.com (non-translated)",
translate: false,
}, },
pageSubtitle: "user@email.com (non-translated)",
pageIcon: RegistrationCheckEmailIcon, pageIcon: RegistrationCheckEmailIcon,
}; };

View File

@ -233,10 +233,7 @@ export class LockV2Component implements OnInit, OnDestroy {
private setEmailAsPageSubtitle(email: string) { private setEmailAsPageSubtitle(email: string) {
this.anonLayoutWrapperDataService.setAnonLayoutWrapperData({ this.anonLayoutWrapperDataService.setAnonLayoutWrapperData({
pageSubtitle: { pageSubtitle: email,
subtitle: email,
translate: false,
},
}); });
} }