diff --git a/.prettierignore b/.prettierignore index e794a80b7..4d2302555 100644 --- a/.prettierignore +++ b/.prettierignore @@ -10,3 +10,7 @@ CONTRIBUTING.md docs .next .cache + +# Generated files +CHANGELOG.md +**/api-report/* \ No newline at end of file diff --git a/packages/elements-react/src/locales/de.json b/packages/elements-react/src/locales/de.json index fa6395128..b86ae6108 100644 --- a/packages/elements-react/src/locales/de.json +++ b/packages/elements-react/src/locales/de.json @@ -201,5 +201,23 @@ "identities.messages.1050020": "", "identities.messages.4000037": "", "identities.messages.4010009": "", - "identities.messages.4010010": "" + "identities.messages.4010010": "", + "input.placeholder": "{placeholder} eingeben", + "card.header.parts.code": "einem Code per E-Mail", + "card.header.parts.identifier-first": "Ihr {identifierLabel}", + "card.header.parts.oidc": "ein sozialer Anbieter", + "card.header.parts.passkey": "ein Passkey", + "card.header.parts.password.login": "Ihrer {identifierLabel} und Ihrem Passwort", + "card.header.parts.password.registration": "Ihrer {identifierLabel} und einem Passwort", + "card.header.parts.webauthn": "ein Sicherheitsschlüssel", + "recovery.subtitle": "Geben Sie die mit Ihrem Konto verknüpfte E-Mail-Adresse ein, um einen einmaligen Zugangscode zu erhalten", + "settings.subtitle": "Aktualisieren Sie Ihre Kontoeinstellungen", + "verification.subtitle": "Geben Sie die mit Ihrem Konto verknüpfte E-Mail-Adresse ein, um es zu bestätigen", + "card.header.description.login": "Melden Sie sich mit {identifierLabel} an", + "card.header.description.registration": "Registrieren Sie sich mit {identifierLabel}", + "login.subtitle": "Melden Sie sich mit {parts} an", + "login.subtitle-refresh": "Bestätigen Sie ihre Identität mit {parts}", + "misc.or": "oder", + "registration.subtitle": "Registrieren Sie sich mit {parts}", + "forms.label.forgot-password": "Passwort vergessen?" } diff --git a/packages/elements-react/src/locales/en.json b/packages/elements-react/src/locales/en.json index 59dcf4595..63d2b945f 100644 --- a/packages/elements-react/src/locales/en.json +++ b/packages/elements-react/src/locales/en.json @@ -158,18 +158,22 @@ "login.registration-label": "Don't have an account?", "login.subtitle-oauth2": "To authenticate {clientName}", "login.title": "Sign in", + "login.subtitle": "Sign in with {parts}", "login.title-aal2": "Two-Factor Authentication", - "login.title-refresh": "Confirm it's you", + "login.title-refresh": "Reauthenticate", + "login.subtitle-refresh": "Confirm your identity with {parts}", "logout.accept-button": "Yes", "logout.reject-button": "No", "logout.title": "Do you wish to log out?", "recovery.login-button": "Sign in", "recovery.login-label": "Remember your credentials?", "recovery.title": "Recover your account", + "recovery.subtitle": "Enter the email address associated with your account to receive a one-time access code", "registration.login-button": "Sign in", "registration.login-label": "Already have an account?", "registration.subtitle-oauth2": "To authenticate {clientName}", "registration.title": "Register an account", + "registration.subtitle": "Sign up with {parts}", "settings.navigation-back-button": "Back", "settings.navigation-backup-codes": "2FA Backup Codes", "settings.navigation-logout": "Logout", @@ -180,7 +184,8 @@ "settings.navigation-webauthn": "Hardware Tokens", "settings.navigation-passkey": "Passkeys", "settings.subtitle-instructions": "Here you can manage settings related to your account. Keep in mind that certain actions require you to re-authenticate.", - "settings.title": "Account Settings", + "settings.title": "Update your account", + "settings.subtitle": "Update your account settings", "settings.title-lookup-secret": "Manage 2FA Backup Recovery Codes", "settings.title-navigation": "Account Settings", "settings.title-oidc": "Social Sign In", @@ -192,6 +197,7 @@ "verification.registration-button": "Sign up", "verification.registration-label": "Don't have an account?", "verification.title": "Verify your account", + "verification.subtitle": "Enter the email address associated with your account to verify it", "verification.back-button": "Back", "two-step.password.title": "Password", "two-step.password.description": "Enter your password associated with your account", @@ -201,5 +207,17 @@ "two-step.webauthn.description": "Use your security key to authenticate", "two-step.passkey.title": "Passkey (recommended)", "two-step.passkey.description": "Use your device's for fingerprint or face recognition", - "identities.messages.1010020": "" + "identities.messages.1010020": "", + "input.placeholder": "Enter your {placeholder}", + "card.header.parts.oidc": "a social provider", + "card.header.parts.password.registration": "your {identifierLabel} and a password", + "card.header.parts.password.login": "your {identifierLabel} and password", + "card.header.parts.code": "a code sent to your email", + "card.header.parts.passkey": "a Passkey", + "card.header.parts.webauthn": "a security key", + "card.header.parts.identifier-first": "your {identifierLabel}", + "card.header.description.login": "Sign in with {identifierLabel}", + "card.header.description.registration": "Sign up with {identifierLabel}", + "misc.or": "or", + "forms.label.forgot-password": "Forgot Password?" } diff --git a/packages/elements-react/src/locales/es.json b/packages/elements-react/src/locales/es.json index a8701cbc6..b9459aba8 100644 --- a/packages/elements-react/src/locales/es.json +++ b/packages/elements-react/src/locales/es.json @@ -201,5 +201,23 @@ "identities.messages.4010009": "", "identities.messages.4010010": "", "login.cancel-button": "", - "login.cancel-label": "" + "login.cancel-label": "", + "input.placeholder": "", + "card.header.description.login": "", + "card.header.description.registration": "", + "card.header.parts.code": "", + "card.header.parts.identifier-first": "", + "card.header.parts.oidc": "", + "card.header.parts.passkey": "", + "card.header.parts.password.login": "", + "card.header.parts.password.registration": "", + "card.header.parts.webauthn": "", + "forms.label.forgot-password": "", + "login.subtitle": "", + "login.subtitle-refresh": "", + "misc.or": "", + "recovery.subtitle": "", + "registration.subtitle": "", + "settings.subtitle": "", + "verification.subtitle": "" } diff --git a/packages/elements-react/src/locales/fr.json b/packages/elements-react/src/locales/fr.json index 783c02c5e..6944df10e 100644 --- a/packages/elements-react/src/locales/fr.json +++ b/packages/elements-react/src/locales/fr.json @@ -201,5 +201,23 @@ "identities.messages.1070014": "", "identities.messages.4000037": "", "identities.messages.4010009": "", - "identities.messages.4010010": "" + "identities.messages.4010010": "", + "input.placeholder": "", + "card.header.description.login": "", + "card.header.description.registration": "", + "card.header.parts.code": "", + "card.header.parts.identifier-first": "", + "card.header.parts.oidc": "", + "card.header.parts.passkey": "", + "card.header.parts.password.login": "", + "card.header.parts.password.registration": "", + "card.header.parts.webauthn": "", + "forms.label.forgot-password": "", + "login.subtitle": "", + "login.subtitle-refresh": "", + "misc.or": "", + "recovery.subtitle": "", + "registration.subtitle": "", + "settings.subtitle": "", + "verification.subtitle": "" } diff --git a/packages/elements-react/src/locales/nl.json b/packages/elements-react/src/locales/nl.json index 1ed1fcb8a..5e4d160b4 100644 --- a/packages/elements-react/src/locales/nl.json +++ b/packages/elements-react/src/locales/nl.json @@ -201,5 +201,23 @@ "identities.messages.1050020": "", "identities.messages.4000037": "", "identities.messages.4010009": "", - "identities.messages.4010010": "" + "identities.messages.4010010": "", + "input.placeholder": "", + "card.header.description.login": "", + "card.header.description.registration": "", + "card.header.parts.code": "", + "card.header.parts.identifier-first": "", + "card.header.parts.oidc": "", + "card.header.parts.passkey": "", + "card.header.parts.password.login": "", + "card.header.parts.password.registration": "", + "card.header.parts.webauthn": "", + "forms.label.forgot-password": "", + "login.subtitle": "", + "login.subtitle-refresh": "", + "misc.or": "", + "recovery.subtitle": "", + "registration.subtitle": "", + "settings.subtitle": "", + "verification.subtitle": "" } diff --git a/packages/elements-react/src/locales/pl.json b/packages/elements-react/src/locales/pl.json index eb2d94d1a..3fa0f6056 100644 --- a/packages/elements-react/src/locales/pl.json +++ b/packages/elements-react/src/locales/pl.json @@ -201,5 +201,23 @@ "identities.messages.4010009": "", "identities.messages.4010010": "", "login.cancel-button": "", - "login.cancel-label": "" + "login.cancel-label": "", + "input.placeholder": "", + "card.header.description.login": "", + "card.header.description.registration": "", + "card.header.parts.code": "", + "card.header.parts.identifier-first": "", + "card.header.parts.oidc": "", + "card.header.parts.passkey": "", + "card.header.parts.password.login": "", + "card.header.parts.password.registration": "", + "card.header.parts.webauthn": "", + "forms.label.forgot-password": "", + "login.subtitle": "", + "login.subtitle-refresh": "", + "misc.or": "", + "recovery.subtitle": "", + "registration.subtitle": "", + "settings.subtitle": "", + "verification.subtitle": "" } diff --git a/packages/elements-react/src/locales/pt.json b/packages/elements-react/src/locales/pt.json index 54b426d77..2646603eb 100644 --- a/packages/elements-react/src/locales/pt.json +++ b/packages/elements-react/src/locales/pt.json @@ -201,5 +201,23 @@ "identities.messages.4010010": "", "login.cancel-button": "", "login.cancel-label": "", - "identities.messages.1070015": "" + "identities.messages.1070015": "", + "input.placeholder": "", + "card.header.description.login": "", + "card.header.description.registration": "", + "card.header.parts.code": "", + "card.header.parts.identifier-first": "", + "card.header.parts.oidc": "", + "card.header.parts.passkey": "", + "card.header.parts.password.login": "", + "card.header.parts.password.registration": "", + "card.header.parts.webauthn": "", + "forms.label.forgot-password": "", + "login.subtitle": "", + "login.subtitle-refresh": "", + "misc.or": "", + "recovery.subtitle": "", + "registration.subtitle": "", + "settings.subtitle": "", + "verification.subtitle": "" } diff --git a/packages/elements-react/src/locales/sv.json b/packages/elements-react/src/locales/sv.json index 50bec3bbc..45f8d3b31 100644 --- a/packages/elements-react/src/locales/sv.json +++ b/packages/elements-react/src/locales/sv.json @@ -201,5 +201,23 @@ "two-step.webauthn.title": "Säkerhetsnyckel", "identities.messages.4000037": "Detta konto finns inte eller har ingen inloggningsmetod konfigurerad.", "identities.messages.4000038": "Captcha-verifiering misslyckades, försök igen.", - "identities.messages.1010020": "" + "identities.messages.1010020": "", + "input.placeholder": "Ange ditt {placeholder}", + "card.header.description.login": "Logga in med {identifierLabel}", + "card.header.description.registration": "Registrera dig med {identifierLabel}", + "card.header.parts.code": "en kod skickad till din e-post", + "card.header.parts.identifier-first": "din {identifierLabel}", + "card.header.parts.oidc": "en social leverantör", + "card.header.parts.passkey": "en Passkey", + "card.header.parts.password.login": "din {identifierLabel} och lösenord", + "card.header.parts.password.registration": "din {identifierLabel} och ett lösenord", + "card.header.parts.webauthn": "en säkerhetsnyckel", + "forms.label.forgot-password": "Glömt Lösenord?", + "login.subtitle": "Logga in med {parts}", + "login.subtitle-refresh": "Bekräfta din identitet med {parts}", + "misc.or": "eller", + "recovery.subtitle": "Ange e-postadressen kopplad till ditt konto för att få en engångskod för åtkomst", + "registration.subtitle": "Registrera dig med {parts}", + "settings.subtitle": "Uppdatera dina kontoinställningar", + "verification.subtitle": "Ange e-postadressen kopplad till ditt konto för att verifiera det" } diff --git a/packages/elements-react/src/theme/default/components/card/header.tsx b/packages/elements-react/src/theme/default/components/card/header.tsx index f360ea731..a48901302 100644 --- a/packages/elements-react/src/theme/default/components/card/header.tsx +++ b/packages/elements-react/src/theme/default/components/card/header.tsx @@ -1,5 +1,5 @@ import { useComponents, useOryFlow } from "@ory/elements-react" -import { constructCardHeaderText } from "../../utils/constructCardHeader" +import { useCardHeaderText } from "../../utils/constructCardHeader" function InnerCardHeader({ title, text }: { title: string; text?: string }) { const { CardLogo } = useComponents() @@ -18,7 +18,7 @@ function InnerCardHeader({ title, text }: { title: string; text?: string }) { export function DefaultCardHeader() { const context = useOryFlow() - const { title, description } = constructCardHeaderText( + const { title, description } = useCardHeaderText( context.flow.ui.nodes, context, ) diff --git a/packages/elements-react/src/theme/default/components/form/index.tsx b/packages/elements-react/src/theme/default/components/form/index.tsx index f2ec56377..1ad941fd7 100644 --- a/packages/elements-react/src/theme/default/components/form/index.tsx +++ b/packages/elements-react/src/theme/default/components/form/index.tsx @@ -1,7 +1,8 @@ import { PropsWithChildren } from "react" import { cn } from "../../utils/cn" -import { HeadlessFormProps } from "@ory/elements-react" +import { formatMessage, HeadlessFormProps } from "@ory/elements-react" import { HeadlessMessageProps } from "@ory/elements-react" +import { useIntl } from "react-intl" export function DefaultFormContainer({ children, @@ -31,6 +32,7 @@ export function DefaultMessageContainer({ children }: PropsWithChildren) { } export function DefaultMessage({ message }: HeadlessMessageProps) { + const intl = useIntl() return ( - {message.text} + {formatMessage(message, intl)} ) } diff --git a/packages/elements-react/src/theme/default/components/form/input.tsx b/packages/elements-react/src/theme/default/components/form/input.tsx index cab18f567..ac456431f 100644 --- a/packages/elements-react/src/theme/default/components/form/input.tsx +++ b/packages/elements-react/src/theme/default/components/form/input.tsx @@ -1,6 +1,7 @@ -import { useFormContext } from "react-hook-form" -import { HeadlessInputProps } from "@ory/elements-react" import { getNodeLabel } from "@ory/client-fetch" +import { formatMessage, HeadlessInputProps } from "@ory/elements-react" +import { useFormContext } from "react-hook-form" +import { useIntl } from "react-intl" export const DefaultInput = ({ node, @@ -10,6 +11,19 @@ export const DefaultInput = ({ const label = getNodeLabel(node) const { register } = useFormContext() const { value, autocomplete, name, maxlength, ...rest } = attributes + const intl = useIntl() + + const formattedLabel = label + ? intl.formatMessage( + { + id: "input.placeholder", + defaultMessage: "Enter your {placeholder}", + }, + { + placeholder: formatMessage(label, intl), + }, + ) + : "" return ( diff --git a/packages/elements-react/src/theme/default/components/form/social.tsx b/packages/elements-react/src/theme/default/components/form/social.tsx index 16e1a998e..9aa208bc2 100644 --- a/packages/elements-react/src/theme/default/components/form/social.tsx +++ b/packages/elements-react/src/theme/default/components/form/social.tsx @@ -1,10 +1,12 @@ import { + formatMessage, HeadlessSocialButtonContainerProps, HeadlessSocialButtonProps, useOryFlow, } from "@ory/elements-react" import logos from "../../provider-logos" import { cn } from "../../utils/cn" +import { useIntl } from "react-intl" function extractProvider(context: object | undefined): string | undefined { if ( @@ -32,6 +34,7 @@ export function DefaultButtonSocial({ const { flow: { ui }, } = useOryFlow() + const intl = useIntl() const oidcNodeCount = ui.nodes.filter((node) => node.group === "oidc").length ?? 0 @@ -69,7 +72,7 @@ export function DefaultButtonSocial({ {showLabel ? ( - {node.meta.label?.text} + {formatMessage(node.meta.label, intl)} ) : null} diff --git a/packages/elements-react/src/theme/default/utils/__tests__/__snapshots__/constructCardHeader.spec.ts.snap b/packages/elements-react/src/theme/default/utils/__tests__/__snapshots__/constructCardHeader.spec.tsx.snap similarity index 88% rename from packages/elements-react/src/theme/default/utils/__tests__/__snapshots__/constructCardHeader.spec.ts.snap rename to packages/elements-react/src/theme/default/utils/__tests__/__snapshots__/constructCardHeader.spec.tsx.snap index 726a1b826..984f29995 100644 --- a/packages/elements-react/src/theme/default/utils/__tests__/__snapshots__/constructCardHeader.spec.ts.snap +++ b/packages/elements-react/src/theme/default/utils/__tests__/__snapshots__/constructCardHeader.spec.tsx.snap @@ -2,42 +2,42 @@ exports[`flowType=login refresh=false combination=code constructCardHeaderText 1`] = ` { - "description": "Sign in with a code sent to your email.", + "description": "Sign in with a code sent to your email", "title": "Sign in", } `; exports[`flowType=login refresh=false combination=default_and_code constructCardHeaderText 1`] = ` { - "description": "Sign in with a code sent to your email.", + "description": "Sign in with a code sent to your email", "title": "Sign in", } `; exports[`flowType=login refresh=false combination=default_and_oidc constructCardHeaderText 1`] = ` { - "description": "Sign in with a social provider.", + "description": "Sign in with a social provider", "title": "Sign in", } `; exports[`flowType=login refresh=false combination=default_and_passkeys constructCardHeaderText 1`] = ` { - "description": "Sign in with a Passkey.", + "description": "Sign in with a Passkey", "title": "Sign in", } `; exports[`flowType=login refresh=false combination=default_and_password constructCardHeaderText 1`] = ` { - "description": "Sign in with your email and password.", + "description": "Sign in with your email and password", "title": "Sign in", } `; exports[`flowType=login refresh=false combination=default_and_webauthn constructCardHeaderText 1`] = ` { - "description": "Sign in with a security key.", + "description": "Sign in with a security key", "title": "Sign in", } `; @@ -51,91 +51,91 @@ exports[`flowType=login refresh=false combination=defaultGroup constructCardHead exports[`flowType=login refresh=false combination=idenfier_first_and_oidc constructCardHeaderText 1`] = ` { - "description": "Sign in with a social provider or your undefined.", + "description": "Sign in with a social provider or your Email", "title": "Sign in", } `; exports[`flowType=login refresh=false combination=identifierFirst constructCardHeaderText 1`] = ` { - "description": "Sign in with your undefined.", + "description": "Sign in with your Email", "title": "Sign in", } `; exports[`flowType=login refresh=false combination=identifierFirstPhone constructCardHeaderText 1`] = ` { - "description": "Sign in with your undefined.", + "description": "Sign in with your Phone number", "title": "Sign in", } `; exports[`flowType=login refresh=false combination=oidc constructCardHeaderText 1`] = ` { - "description": "Sign in with a social provider.", + "description": "Sign in with a social provider", "title": "Sign in", } `; exports[`flowType=login refresh=false combination=passkey constructCardHeaderText 1`] = ` { - "description": "Sign in with a Passkey.", + "description": "Sign in with a Passkey", "title": "Sign in", } `; exports[`flowType=login refresh=false combination=password constructCardHeaderText 1`] = ` { - "description": "Sign in with your email and password.", + "description": "Sign in with your email and password", "title": "Sign in", } `; exports[`flowType=login refresh=false combination=webauthn constructCardHeaderText 1`] = ` { - "description": "Sign in with a security key.", + "description": "Sign in with a security key", "title": "Sign in", } `; exports[`flowType=login refresh=true combination=code constructCardHeaderText 1`] = ` { - "description": "Confirm your identity with a code sent to your email.", + "description": "Confirm your identity with a code sent to your email", "title": "Reauthenticate", } `; exports[`flowType=login refresh=true combination=default_and_code constructCardHeaderText 1`] = ` { - "description": "Confirm your identity with a code sent to your email.", + "description": "Confirm your identity with a code sent to your email", "title": "Reauthenticate", } `; exports[`flowType=login refresh=true combination=default_and_oidc constructCardHeaderText 1`] = ` { - "description": "Confirm your identity with a social provider.", + "description": "Confirm your identity with a social provider", "title": "Reauthenticate", } `; exports[`flowType=login refresh=true combination=default_and_passkeys constructCardHeaderText 1`] = ` { - "description": "Confirm your identity with a Passkey.", + "description": "Confirm your identity with a Passkey", "title": "Reauthenticate", } `; exports[`flowType=login refresh=true combination=default_and_password constructCardHeaderText 1`] = ` { - "description": "Confirm your identity with your email and password.", + "description": "Confirm your identity with your email and password", "title": "Reauthenticate", } `; exports[`flowType=login refresh=true combination=default_and_webauthn constructCardHeaderText 1`] = ` { - "description": "Confirm your identity with a security key.", + "description": "Confirm your identity with a security key", "title": "Reauthenticate", } `; @@ -149,49 +149,49 @@ exports[`flowType=login refresh=true combination=defaultGroup constructCardHeade exports[`flowType=login refresh=true combination=idenfier_first_and_oidc constructCardHeaderText 1`] = ` { - "description": "Confirm your identity with a social provider or your undefined.", + "description": "Confirm your identity with a social provider or your Email", "title": "Reauthenticate", } `; exports[`flowType=login refresh=true combination=identifierFirst constructCardHeaderText 1`] = ` { - "description": "Confirm your identity with your undefined.", + "description": "Confirm your identity with your Email", "title": "Reauthenticate", } `; exports[`flowType=login refresh=true combination=identifierFirstPhone constructCardHeaderText 1`] = ` { - "description": "Confirm your identity with your undefined.", + "description": "Confirm your identity with your Phone number", "title": "Reauthenticate", } `; exports[`flowType=login refresh=true combination=oidc constructCardHeaderText 1`] = ` { - "description": "Confirm your identity with a social provider.", + "description": "Confirm your identity with a social provider", "title": "Reauthenticate", } `; exports[`flowType=login refresh=true combination=passkey constructCardHeaderText 1`] = ` { - "description": "Confirm your identity with a Passkey.", + "description": "Confirm your identity with a Passkey", "title": "Reauthenticate", } `; exports[`flowType=login refresh=true combination=password constructCardHeaderText 1`] = ` { - "description": "Confirm your identity with your email and password.", + "description": "Confirm your identity with your email and password", "title": "Reauthenticate", } `; exports[`flowType=login refresh=true combination=webauthn constructCardHeaderText 1`] = ` { - "description": "Confirm your identity with a security key.", + "description": "Confirm your identity with a security key", "title": "Reauthenticate", } `; @@ -394,197 +394,197 @@ exports[`flowType=recovery refresh=true combination=webauthn constructCardHeader exports[`flowType=registration refresh=false combination=code constructCardHeaderText 1`] = ` { - "description": "Sign up with a code sent to your email.", - "title": "Sign up", + "description": "Sign up with a code sent to your email", + "title": "Register an account", } `; exports[`flowType=registration refresh=false combination=default_and_code constructCardHeaderText 1`] = ` { - "description": "Sign up with a code sent to your email.", - "title": "Sign up", + "description": "Sign up with a code sent to your email", + "title": "Register an account", } `; exports[`flowType=registration refresh=false combination=default_and_oidc constructCardHeaderText 1`] = ` { - "description": "Sign up with a social provider.", - "title": "Sign up", + "description": "Sign up with a social provider", + "title": "Register an account", } `; exports[`flowType=registration refresh=false combination=default_and_passkeys constructCardHeaderText 1`] = ` { - "description": "Sign up with a Passkey.", - "title": "Sign up", + "description": "Sign up with a Passkey", + "title": "Register an account", } `; exports[`flowType=registration refresh=false combination=default_and_password constructCardHeaderText 1`] = ` { - "description": "Sign up with your email and a password.", - "title": "Sign up", + "description": "Sign up with your email and a password", + "title": "Register an account", } `; exports[`flowType=registration refresh=false combination=default_and_webauthn constructCardHeaderText 1`] = ` { - "description": "Sign up with a security key.", - "title": "Sign up", + "description": "Sign up with a security key", + "title": "Register an account", } `; exports[`flowType=registration refresh=false combination=defaultGroup constructCardHeaderText 1`] = ` { "description": "", - "title": "Sign up", + "title": "Register an account", } `; exports[`flowType=registration refresh=false combination=idenfier_first_and_oidc constructCardHeaderText 1`] = ` { - "description": "Sign up with a social provider or your undefined.", - "title": "Sign up", + "description": "Sign up with a social provider or your Email", + "title": "Register an account", } `; exports[`flowType=registration refresh=false combination=identifierFirst constructCardHeaderText 1`] = ` { - "description": "Sign up with your undefined.", - "title": "Sign up", + "description": "Sign up with your Email", + "title": "Register an account", } `; exports[`flowType=registration refresh=false combination=identifierFirstPhone constructCardHeaderText 1`] = ` { - "description": "Sign up with your undefined.", - "title": "Sign up", + "description": "Sign up with your Phone number", + "title": "Register an account", } `; exports[`flowType=registration refresh=false combination=oidc constructCardHeaderText 1`] = ` { - "description": "Sign up with a social provider.", - "title": "Sign up", + "description": "Sign up with a social provider", + "title": "Register an account", } `; exports[`flowType=registration refresh=false combination=passkey constructCardHeaderText 1`] = ` { - "description": "Sign up with a Passkey.", - "title": "Sign up", + "description": "Sign up with a Passkey", + "title": "Register an account", } `; exports[`flowType=registration refresh=false combination=password constructCardHeaderText 1`] = ` { - "description": "Sign up with your email and a password.", - "title": "Sign up", + "description": "Sign up with your email and a password", + "title": "Register an account", } `; exports[`flowType=registration refresh=false combination=webauthn constructCardHeaderText 1`] = ` { - "description": "Sign up with a security key.", - "title": "Sign up", + "description": "Sign up with a security key", + "title": "Register an account", } `; exports[`flowType=registration refresh=true combination=code constructCardHeaderText 1`] = ` { - "description": "Sign up with a code sent to your email.", - "title": "Sign up", + "description": "Sign up with a code sent to your email", + "title": "Register an account", } `; exports[`flowType=registration refresh=true combination=default_and_code constructCardHeaderText 1`] = ` { - "description": "Sign up with a code sent to your email.", - "title": "Sign up", + "description": "Sign up with a code sent to your email", + "title": "Register an account", } `; exports[`flowType=registration refresh=true combination=default_and_oidc constructCardHeaderText 1`] = ` { - "description": "Sign up with a social provider.", - "title": "Sign up", + "description": "Sign up with a social provider", + "title": "Register an account", } `; exports[`flowType=registration refresh=true combination=default_and_passkeys constructCardHeaderText 1`] = ` { - "description": "Sign up with a Passkey.", - "title": "Sign up", + "description": "Sign up with a Passkey", + "title": "Register an account", } `; exports[`flowType=registration refresh=true combination=default_and_password constructCardHeaderText 1`] = ` { - "description": "Sign up with your email and a password.", - "title": "Sign up", + "description": "Sign up with your email and a password", + "title": "Register an account", } `; exports[`flowType=registration refresh=true combination=default_and_webauthn constructCardHeaderText 1`] = ` { - "description": "Sign up with a security key.", - "title": "Sign up", + "description": "Sign up with a security key", + "title": "Register an account", } `; exports[`flowType=registration refresh=true combination=defaultGroup constructCardHeaderText 1`] = ` { "description": "", - "title": "Sign up", + "title": "Register an account", } `; exports[`flowType=registration refresh=true combination=idenfier_first_and_oidc constructCardHeaderText 1`] = ` { - "description": "Sign up with a social provider or your undefined.", - "title": "Sign up", + "description": "Sign up with a social provider or your Email", + "title": "Register an account", } `; exports[`flowType=registration refresh=true combination=identifierFirst constructCardHeaderText 1`] = ` { - "description": "Sign up with your undefined.", - "title": "Sign up", + "description": "Sign up with your Email", + "title": "Register an account", } `; exports[`flowType=registration refresh=true combination=identifierFirstPhone constructCardHeaderText 1`] = ` { - "description": "Sign up with your undefined.", - "title": "Sign up", + "description": "Sign up with your Phone number", + "title": "Register an account", } `; exports[`flowType=registration refresh=true combination=oidc constructCardHeaderText 1`] = ` { - "description": "Sign up with a social provider.", - "title": "Sign up", + "description": "Sign up with a social provider", + "title": "Register an account", } `; exports[`flowType=registration refresh=true combination=passkey constructCardHeaderText 1`] = ` { - "description": "Sign up with a Passkey.", - "title": "Sign up", + "description": "Sign up with a Passkey", + "title": "Register an account", } `; exports[`flowType=registration refresh=true combination=password constructCardHeaderText 1`] = ` { - "description": "Sign up with your email and a password.", - "title": "Sign up", + "description": "Sign up with your email and a password", + "title": "Register an account", } `; exports[`flowType=registration refresh=true combination=webauthn constructCardHeaderText 1`] = ` { - "description": "Sign up with a security key.", - "title": "Sign up", + "description": "Sign up with a security key", + "title": "Register an account", } `; diff --git a/packages/elements-react/src/theme/default/utils/__tests__/constructCardHeader.spec.ts b/packages/elements-react/src/theme/default/utils/__tests__/constructCardHeader.spec.tsx similarity index 84% rename from packages/elements-react/src/theme/default/utils/__tests__/constructCardHeader.spec.ts rename to packages/elements-react/src/theme/default/utils/__tests__/constructCardHeader.spec.tsx index 04fdb817b..1e8127d00 100644 --- a/packages/elements-react/src/theme/default/utils/__tests__/constructCardHeader.spec.ts +++ b/packages/elements-react/src/theme/default/utils/__tests__/constructCardHeader.spec.tsx @@ -2,7 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 import { FlowType, UiNode, UiTextTypeEnum } from "@ory/client-fetch" -import { constructCardHeaderText } from "../constructCardHeader" +import { useCardHeaderText } from "../constructCardHeader" +import { renderHook } from "@testing-library/react" +import { PropsWithChildren } from "react" +import { IntlProvider } from "../../../../context" const password: UiNode = { group: "password", @@ -108,14 +111,15 @@ const identifierFirst: UiNode = { type: "text", value: "", disabled: false, + }, + messages: [], + meta: { label: { text: "Email", type: UiTextTypeEnum.Info, id: 9999, }, }, - messages: [], - meta: {}, } const identifierFirstPhone: UiNode = { @@ -127,14 +131,15 @@ const identifierFirstPhone: UiNode = { type: "text", value: "", disabled: false, + }, + messages: [], + meta: { label: { text: "Phone number", type: UiTextTypeEnum.Info, id: 9999, }, }, - messages: [], - meta: {}, } const combinations = { @@ -154,6 +159,10 @@ const combinations = { idenfier_first_and_oidc: [identifierFirst, oidc], } +const wrapper = ({ children }: PropsWithChildren) => ( + {children} +) + for (const flowType of [ FlowType.Login, FlowType.Registration, @@ -172,11 +181,15 @@ for (const flowType of [ for (const [key, value] of Object.entries(combinations)) { describe("combination=" + key, () => { test("constructCardHeaderText", () => { - const res = constructCardHeaderText( - Array.isArray(value) ? value : [value], - opts, + const res = renderHook( + () => + useCardHeaderText( + Array.isArray(value) ? value : [value], + opts, + ), + { wrapper }, ) - expect(res).toMatchSnapshot() + expect(res.result.current).toMatchSnapshot() }) }) } diff --git a/packages/elements-react/src/theme/default/utils/constructCardHeader.ts b/packages/elements-react/src/theme/default/utils/constructCardHeader.ts index 5f07b1e7e..94059f367 100644 --- a/packages/elements-react/src/theme/default/utils/constructCardHeader.ts +++ b/packages/elements-react/src/theme/default/utils/constructCardHeader.ts @@ -2,15 +2,16 @@ // SPDX-License-Identifier: Apache-2.0 import { FlowType, isUiNodeInputAttributes, UiNode } from "@ory/client-fetch" +import { useIntl } from "react-intl" -function joinWithCommaOr(list: string[]): string { +function joinWithCommaOr(list: string[], orText = "or"): string { if (list.length === 0) { return "." } else if (list.length === 1) { - return list[0] + "." + return list[0] } else { const last = list.pop() - return `${list.join(", ")} or ${last}.` + return `${list.join(", ")} ${orText} ${last}` } } @@ -46,27 +47,38 @@ type opts = * @param opts - can be a flow object, only needed for the refresh login flow * @returns a title and a description for the card header */ -export function constructCardHeaderText( +export function useCardHeaderText( nodes: UiNode[], opts: opts, ): { title: string; description: string } { + const intl = useIntl() switch (opts.flowType) { case FlowType.Recovery: return { - title: "Recover your account", - description: - "Enter the email address associated with your account to receive a one-time access code", + title: intl.formatMessage({ + id: "recovery.title", + }), + description: intl.formatMessage({ + id: "recovery.subtitle", + }), } case FlowType.Settings: return { - title: "Update your account", - description: "Update your account settings", + title: intl.formatMessage({ + id: "settings.title", + }), + description: intl.formatMessage({ + id: "settings.subtitle", + }), } case FlowType.Verification: return { - title: "Verify your account", - description: - "Enter the email address associated with your account to verify it", + title: intl.formatMessage({ + id: "verification.title", + }), + description: intl.formatMessage({ + id: "verification.subtitle", + }), } } @@ -75,26 +87,43 @@ export function constructCardHeaderText( if (nodes.find((node) => node.group === "password")) { switch (opts.flowType) { case FlowType.Registration: - parts.push(`your email and a password`) + parts.push( + intl.formatMessage( + { id: "card.header.parts.password.registration" }, + // TODO: make this generic for other labels + { identifierLabel: "email" }, + ), + ) break default: - parts.push(`your email and password`) + parts.push( + intl.formatMessage( + { id: "card.header.parts.password.login" }, + // TODO: make this generic for other labels + { identifierLabel: "email" }, + ), + ) } } + if (nodes.find((node) => node.group === "oidc")) { - parts.push(`a social provider`) + parts.push( + intl.formatMessage({ + id: "card.header.parts.oidc", + }), + ) } if (nodes.find((node) => node.group === "code")) { - parts.push(`a code sent to your email`) + parts.push(intl.formatMessage({ id: "card.header.parts.code" })) } if (nodes.find((node) => node.group === "passkey")) { - parts.push(`a Passkey`) + parts.push(intl.formatMessage({ id: "card.header.parts.passkey" })) } if (nodes.find((node) => node.group === "webauthn")) { - parts.push(`a security key`) + parts.push(intl.formatMessage({ id: "card.header.parts.webauthn" })) } if (nodes.find((node) => node.group === "identifier_first")) { @@ -106,7 +135,16 @@ export function constructCardHeaderText( ) if (identifier) { - parts.push(`your ${identifier.meta.label?.text}`) + parts.push( + intl.formatMessage( + { + id: "card.header.parts.identifier-first", + }, + { + identifierLabel: identifier.meta.label?.text, + }, + ), + ) } } @@ -114,20 +152,57 @@ export function constructCardHeaderText( case FlowType.Login: if (opts.flow.refresh) { return { - title: "Reauthenticate", - description: "Confirm your identity with " + joinWithCommaOr(parts), + title: intl.formatMessage({ + id: "login.title-refresh", + }), + description: intl.formatMessage( + { + id: "login.subtitle-refresh", + }, + { + parts: joinWithCommaOr(parts), + }, + ), } } return { - title: "Sign in", + title: intl.formatMessage({ + id: "login.title", + }), description: - parts.length > 0 ? "Sign in with " + joinWithCommaOr(parts) : "", + parts.length > 0 + ? intl.formatMessage( + { + id: "login.subtitle", + }, + { + parts: joinWithCommaOr( + parts, + intl.formatMessage({ id: "misc.or" }), + ), + }, + ) + : "", } case FlowType.Registration: return { - title: "Sign up", + title: intl.formatMessage({ + id: "registration.title", + }), description: - parts.length > 0 ? "Sign up with " + joinWithCommaOr(parts) : "", + parts.length > 0 + ? intl.formatMessage( + { + id: "registration.subtitle", + }, + { + parts: joinWithCommaOr( + parts, + intl.formatMessage({ id: "misc.or" }), + ), + }, + ) + : "", } }