From ec3d744890aa117d9f212306c27ad336d28c31ce Mon Sep 17 00:00:00 2001 From: Alano Terblanche <18033717+Benehiko@users.noreply.github.com> Date: Tue, 10 Oct 2023 17:52:57 +0200 Subject: [PATCH 1/5] feat: allow custom languages in `IntlProvider` --- README.md | 53 ++++ examples/nextjs-spa/src/components/layout.tsx | 43 +++- examples/preact-spa/src/main.tsx | 42 +++- examples/react-spa/src/Login.tsx | 1 - examples/react-spa/src/Recovery.tsx | 1 - examples/react-spa/src/Registration.tsx | 1 - examples/react-spa/src/main.tsx | 43 +++- src/react-components/provider.tsx | 227 ++++++++++++++++-- 8 files changed, 386 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index f6fc60e7f..6159fb726 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,59 @@ have explicitly told our React app to use through the `VITE_ORY_SDK_URL` export. Now you can see Ory Elements in action by opening in your browser! +## Adding Translations + +Ory Elements supports translations out-of-the-box with the `IntlProvider`. The +`IntlProvider` is required by Ory Elements so that the default languages can be +mapped correctly, specifically English. + +The `IntlProvider` has the ability to accept custom translations through a +`CustomLanguageFormats` object. You can specify to the `` that you +would like to use a `CustomTranslations` type instead of the +`SupportedLanguages` which will require providing the `customTranslations` prop. + +When providing a language, it is important to note that it will be merged with +an existing supported language, with your provided values taking precedent. This +is to reduce the work needed to get up and running and provide the ability to +just modify one key from an already supported language, rather than modifying +the entire translation file :) + +For example, I want to adjust the English translation to say `Email` instead of +`ID` when a Login card is shown. So I provide the key-value pair +`"identities.messages.1070004": "Email"`. By Default this value is `ID`. Ory +Elements will now use the updated value `Email` instead of `ID` for this +specific label, but will still keep the other defaults in-tact. + +Another scenario is when we add partial keys to an unsupported language such as +`af` (Afrikaans). I add my key-value only for one entry +`"identities.messages.1070004": "E-posadres"`, however, the language has no +default inside Ory Elements. As a safe-guard we fall-back to English for the +rest of the labels. + +```tsx +import { ThemeProvider, IntlProvider, CustomTranslations } from "@ory/elements" + +const RootComponent = () => { + const myCustomTranslations: CustomLanguageFormats = { + en: { + "login.title": "Login", + }, + } + + return ( + + + customTranslations={myCustomTranslations} + locale="en" + defaultLocale="en" + > + // children + + + ) +} +``` + ## End-to-end Testing with Playwright Ory Elements provides an end-to-end library based on diff --git a/examples/nextjs-spa/src/components/layout.tsx b/examples/nextjs-spa/src/components/layout.tsx index 98f85ffc4..ae82a8ca2 100644 --- a/examples/nextjs-spa/src/components/layout.tsx +++ b/examples/nextjs-spa/src/components/layout.tsx @@ -1,4 +1,10 @@ -import { IntlProvider, Nav, ThemeProvider } from "@ory/elements" +import { + CustomLanguageFormats, + CustomTranslations, + IntlProvider, + Nav, + ThemeProvider, +} from "@ory/elements" import Head from "next/head" interface LayoutProps { @@ -6,9 +12,42 @@ interface LayoutProps { } export default function Layout({ children }: LayoutProps) { + // adds custom translations labels to the default translations + // this merges the custom translations with the default translations + // if a custom language is provided, but no standard translation + // exists, the english translation will be merged instead for missing values. + // + // For example, if you provide a custom translation for the "login.title" label + // in the "af" language (Afrikaans), but no standard translation exists for "af", + // the english translation will be used for the remaining labels. + // + // You can also contribute your custom translations to the Ory Elements project + // by submitting a pull request to the following repository: + // https://github.com/ory/elements + const customTranslations: CustomLanguageFormats = { + en: { + "login.title": "Login", + "identities.messages.1070004": "Email", + }, + nl: { + "login.title": "Inloggen", + "identities.messages.1070004": "E-mail", + }, + af: { + "login.title": "Meld aan", + "identities.messages.1070004": "E-posadres", + }, + } return ( - + {/* We dont need to pass any custom translations */} + {/* */} + {/* We pass custom translations */} + + customTranslations={customTranslations} + locale="af" + defaultLocale="en" + > Next.js w/ Elements diff --git a/examples/preact-spa/src/main.tsx b/examples/preact-spa/src/main.tsx index 895f93a66..8e785e918 100644 --- a/examples/preact-spa/src/main.tsx +++ b/examples/preact-spa/src/main.tsx @@ -11,7 +11,12 @@ import { Recovery } from "./recovery" import { Register } from "./register" import { Settings } from "./settings" import { Verification } from "./verification" -import { IntlProvider, ThemeProvider } from "@ory/elements-preact" +import { + CustomLanguageFormats, + CustomTranslations, + IntlProvider, + ThemeProvider, +} from "@ory/elements-preact" // Ory Elements // optional fontawesome icons @@ -27,9 +32,42 @@ import "@ory/elements-preact/assets/jetbrains-mono-font.css" import "@ory/elements-preact/style.css" const Main = () => { + // adds custom translations labels to the default translations + // this merges the custom translations with the default translations + // if a custom language is provided, but no standard translation + // exists, the english translation will be merged instead for missing values. + // + // For example, if you provide a custom translation for the "login.title" label + // in the "af" language (Afrikaans), but no standard translation exists for "af", + // the english translation will be used for the remaining labels. + // + // You can also contribute your custom translations to the Ory Elements project + // by submitting a pull request to the following repository: + // https://github.com/ory/elements + const customTranslations: CustomLanguageFormats = { + en: { + "login.title": "Login", + "identities.messages.1070004": "Email", + }, + nl: { + "login.title": "Inloggen", + "identities.messages.1070004": "E-mail", + }, + af: { + "login.title": "Meld aan", + "identities.messages.1070004": "E-posadres", + }, + } return ( - + {/* We dont need to pass any custom translations */} + {/* */} + {/* We pass custom translations */} + + customTranslations={customTranslations} + locale="af" + defaultLocale="en" + > diff --git a/examples/react-spa/src/Login.tsx b/examples/react-spa/src/Login.tsx index 9da5e0ff0..34f3c19a9 100644 --- a/examples/react-spa/src/Login.tsx +++ b/examples/react-spa/src/Login.tsx @@ -107,7 +107,6 @@ export const Login = (): JSX.Element => { return flow ? ( // we render the login form using Ory Elements { return flow ? ( // We create a dynamic Recovery form based on the flow using Ory Elements { return flow ? ( // create a registration form that dynamically renders based on the flow data using Ory Elements {/* We add the Ory themes here */} - + {/* We dont need to pass any custom translations */} + {/* */} + {/* We pass custom translations */} + + locale="af" + defaultLocale="en" + customTranslations={customTranslations} + > } /> } /> diff --git a/src/react-components/provider.tsx b/src/react-components/provider.tsx index 3b3d4073b..d4244159a 100644 --- a/src/react-components/provider.tsx +++ b/src/react-components/provider.tsx @@ -9,6 +9,7 @@ import { themeProviderStyle, } from "../theme/theme-provider.css" import * as locales from "./../locales" +import { merge } from "lodash" export interface ThemeProviderProps { theme?: "light" | "dark" @@ -37,20 +38,214 @@ export const ThemeProvider = ({ ) -export const IntlProvider = ({ - locale = "en", +export type TranslationFile = { + [K in keyof typeof locales.en]: string +} + +export const CountryCodes = [ + "ab", + "aa", + "af", + "sq", + "am", + "ar", + "hy", + "as", + "ay", + "az", + "ba", + "eu", + "bn", + "dz", + "bh", + "bi", + "br", + "bg", + "my", + "be", + "km", + "ca", + "zh", + "co", + "hr", + "cs", + "da", + "nl", + "en", + "eo", + "et", + "fo", + "fj", + "fi", + "fr", + "fy", + "gd", + "gl", + "ka", + "de", + "el", + "kl", + "gn", + "gu", + "ha", + "iw", + "hi", + "hu", + "is", + "in", + "ia", + "ie", + "ik", + "ga", + "it", + "ja", + "jw", + "kn", + "ks", + "kk", + "rw", + "ky", + "rn", + "ko", + "ku", + "lo", + "la", + "lv", + "ln", + "lt", + "mk", + "mg", + "ms", + "ml", + "mt", + "mi", + "mr", + "mo", + "mn", + "na", + "ne", + "no", + "oc", + "or", + "om", + "ps", + "fa", + "pl", + "pt", + "pa", + "qu", + "rm", + "ro", + "ru", + "sm", + "sg", + "sa", + "sr", + "sh", + "st", + "tn", + "sn", + "sd", + "si", + "ss", + "sk", + "sl", + "so", + "es", + "su", + "sw", + "sv", + "tl", + "tg", + "ta", + "tt", + "te", + "th", + "bo", + "ti", + "to", + "ts", + "tr", + "tk", + "tw", + "uk", + "ur", + "uz", + "vi", + "vo", + "cy", + "wo", + "xh", + "ji", + "yo", + "zu", +] as const + +export type CustomLanguageFormats = { + [k in (typeof CountryCodes)[number]]?: Partial +} + +export interface CustomTranslations { + customTranslations: Partial + locale?: (typeof CountryCodes)[number] + defaultLocale?: (typeof CountryCodes)[number] +} + +const isCustomTranslations = (o: unknown): o is CustomTranslations => { + return (o as CustomTranslations).customTranslations !== undefined +} + +export type SupportedLanguageFormats = { + [k in keyof typeof locales]: TranslationFile +} + +type locale = keyof typeof locales + +export interface SupportedTranslations { + locale?: locale + defaultLocale?: locale +} + +export type IntlProviderProps = Type extends CustomTranslations + ? PropsWithChildren + : PropsWithChildren + +export const IntlProvider = < + T extends SupportedTranslations | CustomTranslations = SupportedTranslations, +>({ children, -}: PropsWithChildren<{ - locale?: keyof typeof locales -}>) => ( - {chunks}, - }} - > - {children} - -) + ...props +}: IntlProviderProps) => { + let translation = locales.en + + if (props.locale && props.locale in locales) { + translation = locales[props.locale as locale] + } + + const intlProps = isCustomTranslations(props) + ? { + locale: props.locale ?? "en", + defaultLocale: props.defaultLocale, + messages: merge( + {}, + translation, + props.customTranslations[props.locale ?? "en"], + ), + } + : { + locale: props.locale ?? "en", + defaultLocale: props.defaultLocale, + messages: translation, + } + + return ( + {chunks}, + }} + > + {children} + + ) +} From fee54ee51b0fa242156d532da9642da01c5c0212 Mon Sep 17 00:00:00 2001 From: Alano Terblanche <18033717+Benehiko@users.noreply.github.com> Date: Tue, 10 Oct 2023 17:56:48 +0200 Subject: [PATCH 2/5] chore: remove hard-coded title in examples --- examples/nextjs-spa/src/pages/registration.tsx | 1 - examples/preact-spa/src/login.tsx | 1 - examples/preact-spa/src/recovery.tsx | 1 - examples/preact-spa/src/register.tsx | 1 - examples/preact-spa/src/verification.tsx | 1 - 5 files changed, 5 deletions(-) diff --git a/examples/nextjs-spa/src/pages/registration.tsx b/examples/nextjs-spa/src/pages/registration.tsx index 81bed7bd2..54d22e42d 100644 --- a/examples/nextjs-spa/src/pages/registration.tsx +++ b/examples/nextjs-spa/src/pages/registration.tsx @@ -98,7 +98,6 @@ const Registration: NextPageWithLayout = () => { // create a registration form that dynamically renders based on the flow data using Ory Elements { forgotPasswordURL: "/recovery", signupURL: "/registration", }} - title={"Login"} includeScripts={true} onSubmit={({ body }) => submitFlow(body as UpdateLoginFlowBody)} /> diff --git a/examples/preact-spa/src/recovery.tsx b/examples/preact-spa/src/recovery.tsx index 06dc1cda3..d48180ae1 100644 --- a/examples/preact-spa/src/recovery.tsx +++ b/examples/preact-spa/src/recovery.tsx @@ -64,7 +64,6 @@ export const Recovery = () => { return flow ? ( { { additionalProps={{ signupURL: "/registration", }} - title="Verification" // submit the verification form data to Ory onSubmit={({ body }) => submitFlow(body as UpdateVerificationFlowBody)} /> From 4390ef31d09963513c3831304230c8394eafdddc Mon Sep 17 00:00:00 2001 From: Alano Terblanche <18033717+Benehiko@users.noreply.github.com> Date: Wed, 11 Oct 2023 10:25:45 +0200 Subject: [PATCH 3/5] chore: update language codes variable --- src/react-components/provider.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/react-components/provider.tsx b/src/react-components/provider.tsx index d4244159a..73d2138fa 100644 --- a/src/react-components/provider.tsx +++ b/src/react-components/provider.tsx @@ -42,7 +42,9 @@ export type TranslationFile = { [K in keyof typeof locales.en]: string } -export const CountryCodes = [ +// ISO 639-1 language codes +// https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes +export const LanguageCodes = [ "ab", "aa", "af", @@ -182,13 +184,13 @@ export const CountryCodes = [ ] as const export type CustomLanguageFormats = { - [k in (typeof CountryCodes)[number]]?: Partial + [k in (typeof LanguageCodes)[number]]?: Partial } export interface CustomTranslations { customTranslations: Partial - locale?: (typeof CountryCodes)[number] - defaultLocale?: (typeof CountryCodes)[number] + locale?: (typeof LanguageCodes)[number] + defaultLocale?: (typeof LanguageCodes)[number] } const isCustomTranslations = (o: unknown): o is CustomTranslations => { From 999d392937a2b2affaa700ec406bdad803ca5dc5 Mon Sep 17 00:00:00 2001 From: Alano Terblanche <18033717+Benehiko@users.noreply.github.com> Date: Wed, 11 Oct 2023 10:38:26 +0200 Subject: [PATCH 4/5] chore: update README --- README.md | 105 ++++++++++++++++++++++++------------------------------ 1 file changed, 46 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index 6159fb726..b0e870e45 100644 --- a/README.md +++ b/README.md @@ -82,28 +82,28 @@ have explicitly told our React app to use through the `VITE_ORY_SDK_URL` export. Now you can see Ory Elements in action by opening in your browser! -## Adding Translations +## Internalization (i18n) Ory Elements supports translations out-of-the-box with the `IntlProvider`. The -`IntlProvider` is required by Ory Elements so that the default languages can be -mapped correctly, specifically English. +`IntlProvider` is required by Ory Elements and allows the language to be mapped +correctly, specifically American English. The `IntlProvider` has the ability to accept custom translations through a -`CustomLanguageFormats` object. You can specify to the `` that you -would like to use a `CustomTranslations` type instead of the -`SupportedLanguages` which will require providing the `customTranslations` prop. +`CustomLanguageFormats` type. You can specify to the `` that you +would like to use a `CustomTranslations` instead of the +`SupportedLanguages (default)` type which will require providing the +`customTranslations` prop. -When providing a language, it is important to note that it will be merged with -an existing supported language, with your provided values taking precedent. This -is to reduce the work needed to get up and running and provide the ability to -just modify one key from an already supported language, rather than modifying -the entire translation file :) +When providing a translation, it is important to note that it will be merged +with an existing supported language, with your provided values taking precedent. +This is to allow modifying only one key from an already supported language, +rather than modifying the entire translation file :) For example, I want to adjust the English translation to say `Email` instead of `ID` when a Login card is shown. So I provide the key-value pair -`"identities.messages.1070004": "Email"`. By Default this value is `ID`. Ory -Elements will now use the updated value `Email` instead of `ID` for this -specific label, but will still keep the other defaults in-tact. +`"identities.messages.1070004": "Email"`. Ory Elements will now use the updated +value `Email` instead of `ID` for this specific label, but will still keep the +other defaults in-tact. Another scenario is when we add partial keys to an unsupported language such as `af` (Afrikaans). I add my key-value only for one entry @@ -111,6 +111,9 @@ Another scenario is when we add partial keys to an unsupported language such as default inside Ory Elements. As a safe-guard we fall-back to English for the rest of the labels. +More information on the Ory messages can be found +[in the docs](https://www.ory.sh/docs/kratos/concepts/ui-user-interface#ui-message-codes) + ```tsx import { ThemeProvider, IntlProvider, CustomTranslations } from "@ory/elements" @@ -135,6 +138,35 @@ const RootComponent = () => { } ``` +It is of course also possible to provide the `IntlProvider` directly from the +[react-intl](https://formatjs.io/docs/react-intl/) library to format messages +and provide translations. The default translations of Ory Elements are located +in the `src/locales` directory. + +```tsx +import { IntlProvider } from "react-intl" +import { locales } from "@ory/elements" + +const customMessages = { + ...locales, + de: { + ...locales.de, + "login.title": "Login", + }, +} + +const Main = () => { + return ( + + + + {/* ... */} + + + ) +} +``` + ## End-to-end Testing with Playwright Ory Elements provides an end-to-end library based on @@ -384,51 +416,6 @@ const Main = () => { } ``` -### Internalization (i18n) - -Ory Elements uses [react-intl](https://formatjs.io/docs/react-intl/) to format -messages and provide translations. The default language is american English, but -you can provide your own translations by using the `IntlProvider` component. The -default translations of Ory Elements are located in the `src/locales` directory. -They can be loaded using the `IntlProvider` from Ory Elements. Please note that -it is necessary to wrap all Ory Element components either in the `IntlProvider` -from `react-intl` or Ory Elements. - -```tsx -import { IntlProvider } from "@ory/elements" - -const Main = () => { - return ( - - - - {/* ... */} - - - ) -} -``` - -Custom translations can be provided using the `IntlProvider` from `react-intl`. -For reference, it is best to start with the auto-generated English defaults, as -they include all keys. More information on the Kratos messages can be found -[in the docs](https://www.ory.sh/docs/kratos/concepts/ui-user-interface#ui-message-codes). - -```tsx -import { IntlProvider } from "react-intl" - -const Main = () => { - return ( - - - - {/* ... */} - - - ) -} -``` - ### Theme CSS in Express.js For Express.js the library also exports a helper function which registers all From 005c5a6fd3ddce8a21dc4b33e801f51bd0453b33 Mon Sep 17 00:00:00 2001 From: Alano Terblanche <18033717+Benehiko@users.noreply.github.com> Date: Wed, 11 Oct 2023 11:46:28 +0200 Subject: [PATCH 5/5] refactor: don't auto-merge translations --- README.md | 77 ++++++++++++++----- examples/nextjs-spa/src/components/layout.tsx | 12 ++- examples/preact-spa/src/main.tsx | 12 ++- examples/react-spa/src/main.tsx | 12 ++- src/react-components/provider.tsx | 14 ++-- 5 files changed, 76 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index b0e870e45..1765a88de 100644 --- a/README.md +++ b/README.md @@ -85,8 +85,37 @@ your browser! ## Internalization (i18n) Ory Elements supports translations out-of-the-box with the `IntlProvider`. The -`IntlProvider` is required by Ory Elements and allows the language to be mapped -correctly, specifically American English. +`IntlProvider` is required by Ory Elements and maps American English as the +default language. + +```tsx +import { ThemeProvider } from "@ory/elements" + +const RootComponent = () => { + return ( + + // children + + ) +} +``` + +To switch the language the UI should use, you can pass in the language code +through the `locale` prop. + +```tsx +import { ThemeProvider } from "@ory/elements" + +const RootComponent = () => { + return ( + + + // children + + + ) +} +``` The `IntlProvider` has the ability to accept custom translations through a `CustomLanguageFormats` type. You can specify to the `` that you @@ -94,33 +123,39 @@ would like to use a `CustomTranslations` instead of the `SupportedLanguages (default)` type which will require providing the `customTranslations` prop. -When providing a translation, it is important to note that it will be merged -with an existing supported language, with your provided values taking precedent. -This is to allow modifying only one key from an already supported language, -rather than modifying the entire translation file :) +More information on the Ory messages can be found +[in the docs](https://www.ory.sh/docs/kratos/concepts/ui-user-interface#ui-message-codes) + +When providing a translation, you can merge an existing supported locale from +Ory Elements so that you do not need to provide all keys for the entire +tranlsation file :) For example, I want to adjust the English translation to say `Email` instead of `ID` when a Login card is shown. So I provide the key-value pair -`"identities.messages.1070004": "Email"`. Ory Elements will now use the updated -value `Email` instead of `ID` for this specific label, but will still keep the -other defaults in-tact. - -Another scenario is when we add partial keys to an unsupported language such as -`af` (Afrikaans). I add my key-value only for one entry -`"identities.messages.1070004": "E-posadres"`, however, the language has no -default inside Ory Elements. As a safe-guard we fall-back to English for the -rest of the labels. - -More information on the Ory messages can be found -[in the docs](https://www.ory.sh/docs/kratos/concepts/ui-user-interface#ui-message-codes) +`"identities.messages.1070004": "Email"`. Another unsupported language such as +`af` (Afrikaans) is also added only for one entry +`"identities.messages.1070004": "E-posadres"`. We merge the supported `en` +locale from Ory Elements so that we don't need to provide all key-value pairs. ```tsx -import { ThemeProvider, IntlProvider, CustomTranslations } from "@ory/elements" +import { + ThemeProvider, + IntlProvider, + CustomTranslations, + locales, +} from "@ory/elements" const RootComponent = () => { const myCustomTranslations: CustomLanguageFormats = { + ...locales, en: { - "login.title": "Login", + ...locales.en, + "identities.messages.1070004": "Email", + }, + af: { + // fallback to English on other labels + ...locales.en, + "identities.messages.1070004": "E-posadres", }, } @@ -128,7 +163,7 @@ const RootComponent = () => { customTranslations={myCustomTranslations} - locale="en" + locale="af" defaultLocale="en" > // children diff --git a/examples/nextjs-spa/src/components/layout.tsx b/examples/nextjs-spa/src/components/layout.tsx index ae82a8ca2..3e16dd641 100644 --- a/examples/nextjs-spa/src/components/layout.tsx +++ b/examples/nextjs-spa/src/components/layout.tsx @@ -4,6 +4,7 @@ import { IntlProvider, Nav, ThemeProvider, + locales, } from "@ory/elements" import Head from "next/head" @@ -13,27 +14,24 @@ interface LayoutProps { export default function Layout({ children }: LayoutProps) { // adds custom translations labels to the default translations - // this merges the custom translations with the default translations - // if a custom language is provided, but no standard translation - // exists, the english translation will be merged instead for missing values. - // - // For example, if you provide a custom translation for the "login.title" label - // in the "af" language (Afrikaans), but no standard translation exists for "af", - // the english translation will be used for the remaining labels. // // You can also contribute your custom translations to the Ory Elements project // by submitting a pull request to the following repository: // https://github.com/ory/elements const customTranslations: CustomLanguageFormats = { en: { + ...locales.en, "login.title": "Login", "identities.messages.1070004": "Email", }, nl: { + ...locales.nl, "login.title": "Inloggen", "identities.messages.1070004": "E-mail", }, af: { + // merging English since no default Afrikaans translations are available + ...locales.en, "login.title": "Meld aan", "identities.messages.1070004": "E-posadres", }, diff --git a/examples/preact-spa/src/main.tsx b/examples/preact-spa/src/main.tsx index 8e785e918..514cf80a6 100644 --- a/examples/preact-spa/src/main.tsx +++ b/examples/preact-spa/src/main.tsx @@ -16,6 +16,7 @@ import { CustomTranslations, IntlProvider, ThemeProvider, + locales, } from "@ory/elements-preact" // Ory Elements @@ -33,27 +34,24 @@ import "@ory/elements-preact/style.css" const Main = () => { // adds custom translations labels to the default translations - // this merges the custom translations with the default translations - // if a custom language is provided, but no standard translation - // exists, the english translation will be merged instead for missing values. - // - // For example, if you provide a custom translation for the "login.title" label - // in the "af" language (Afrikaans), but no standard translation exists for "af", - // the english translation will be used for the remaining labels. // // You can also contribute your custom translations to the Ory Elements project // by submitting a pull request to the following repository: // https://github.com/ory/elements const customTranslations: CustomLanguageFormats = { en: { + ...locales.en, "login.title": "Login", "identities.messages.1070004": "Email", }, nl: { + ...locales.nl, "login.title": "Inloggen", "identities.messages.1070004": "E-mail", }, af: { + // merging English since no default Afrikaans translations are available + ...locales.en, "login.title": "Meld aan", "identities.messages.1070004": "E-posadres", }, diff --git a/examples/react-spa/src/main.tsx b/examples/react-spa/src/main.tsx index fc41e217a..38f325296 100644 --- a/examples/react-spa/src/main.tsx +++ b/examples/react-spa/src/main.tsx @@ -3,6 +3,7 @@ import { IntlProvider, CustomTranslations, CustomLanguageFormats, + locales, } from "@ory/elements" // optional global css reset @@ -34,27 +35,24 @@ import "@ory/elements/assets/jetbrains-mono-font.css" import "@ory/elements/style.css" // adds custom translations labels to the default translations -// this merges the custom translations with the default translations -// if a custom language is provided, but no standard translation -// exists, the english translation will be merged instead for missing values. -// -// For example, if you provide a custom translation for the "login.title" label -// in the "af" language (Afrikaans), but no standard translation exists for "af", -// the english translation will be used for the remaining labels. // // You can also contribute your custom translations to the Ory Elements project // by submitting a pull request to the following repository: // https://github.com/ory/elements const customTranslations: CustomLanguageFormats = { en: { + ...locales.en, "login.title": "Login", "identities.messages.1070004": "Email", }, nl: { + ...locales.nl, "login.title": "Inloggen", "identities.messages.1070004": "E-mail", }, af: { + // merging English since no default Afrikaans translations are available + ...locales.en, "login.title": "Meld aan", "identities.messages.1070004": "E-posadres", }, diff --git a/src/react-components/provider.tsx b/src/react-components/provider.tsx index 73d2138fa..edc109eee 100644 --- a/src/react-components/provider.tsx +++ b/src/react-components/provider.tsx @@ -189,8 +189,8 @@ export type CustomLanguageFormats = { export interface CustomTranslations { customTranslations: Partial - locale?: (typeof LanguageCodes)[number] - defaultLocale?: (typeof LanguageCodes)[number] + locale: (typeof LanguageCodes)[number] + defaultLocale: (typeof LanguageCodes)[number] } const isCustomTranslations = (o: unknown): o is CustomTranslations => { @@ -226,17 +226,13 @@ export const IntlProvider = < const intlProps = isCustomTranslations(props) ? { - locale: props.locale ?? "en", + locale: props.locale, defaultLocale: props.defaultLocale, - messages: merge( - {}, - translation, - props.customTranslations[props.locale ?? "en"], - ), + messages: props.customTranslations[props.locale], } : { locale: props.locale ?? "en", - defaultLocale: props.defaultLocale, + defaultLocale: props.defaultLocale ?? "en", messages: translation, }