Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow custom languages in IntlProvider #158

Merged
merged 5 commits into from
Oct 11, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <http://localhost:3000> 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 `<IntlProvider>` 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 (
<ThemeProvider>
<IntlProvider<CustomTranslations>
customTranslations={myCustomTranslations}
locale="en"
defaultLocale="en"
>
// children
</IntlProvider>
</ThemeProvider>
)
}
```

## End-to-end Testing with Playwright

Ory Elements provides an end-to-end library based on
Expand Down
43 changes: 41 additions & 2 deletions examples/nextjs-spa/src/components/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,53 @@
import { IntlProvider, Nav, ThemeProvider } from "@ory/elements"
import {
CustomLanguageFormats,
CustomTranslations,
IntlProvider,
Nav,
ThemeProvider,
} from "@ory/elements"
import Head from "next/head"

interface LayoutProps {
children: React.ReactNode
}

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 (
<ThemeProvider themeOverrides={{}}>
<IntlProvider>
{/* We dont need to pass any custom translations */}
{/* <IntlProvider> */}
{/* We pass custom translations */}
<IntlProvider<CustomTranslations>
customTranslations={customTranslations}
locale="af"
defaultLocale="en"
>
<Head>
<title>Next.js w/ Elements</title>
<link rel="icon" href="/ory.svg" />
Expand Down
1 change: 0 additions & 1 deletion examples/nextjs-spa/src/pages/registration.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@ const Registration: NextPageWithLayout = () => {
// create a registration form that dynamically renders based on the flow data using Ory Elements
<UserAuthCard
cardImage="/ory.svg"
title={"Registration"}
// This defines what kind of card we want to render.
flowType={"registration"}
// we always need to pass the flow to the card since it contains the form fields, error messages and csrf token
Expand Down
1 change: 0 additions & 1 deletion examples/preact-spa/src/login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ export const Login = () => {
forgotPasswordURL: "/recovery",
signupURL: "/registration",
}}
title={"Login"}
includeScripts={true}
onSubmit={({ body }) => submitFlow(body as UpdateLoginFlowBody)}
/>
Expand Down
42 changes: 40 additions & 2 deletions examples/preact-spa/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 (
<ThemeProvider>
<IntlProvider>
{/* We dont need to pass any custom translations */}
{/* <IntlProvider> */}
{/* We pass custom translations */}
<IntlProvider<CustomTranslations>
customTranslations={customTranslations}
locale="af"
defaultLocale="en"
>
<Router>
<Route path="/" component={Dashboard} />
<Route path="/login" component={Login} />
Expand Down
1 change: 0 additions & 1 deletion examples/preact-spa/src/recovery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ export const Recovery = () => {

return flow ? (
<UserAuthCard
title="Recovery"
flow={flow}
flowType={"recovery"}
additionalProps={{ loginURL: "/login" }}
Expand Down
1 change: 0 additions & 1 deletion examples/preact-spa/src/register.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ export const Register = () => {
<UserAuthCard
flow={flow}
flowType={"registration"}
title={"Registration"}
additionalProps={{
loginURL: "/login",
}}
Expand Down
1 change: 0 additions & 1 deletion examples/preact-spa/src/verification.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ export const Verification = () => {
additionalProps={{
signupURL: "/registration",
}}
title="Verification"
// submit the verification form data to Ory
onSubmit={({ body }) => submitFlow(body as UpdateVerificationFlowBody)}
/>
Expand Down
1 change: 0 additions & 1 deletion examples/react-spa/src/Login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@ export const Login = (): JSX.Element => {
return flow ? (
// we render the login form using Ory Elements
<UserAuthCard
title={"Login"}
flowType={"login"}
// we always need the flow data which populates the form fields and error messages dynamically
flow={flow}
Expand Down
1 change: 0 additions & 1 deletion examples/react-spa/src/Recovery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ export const Recovery = () => {
return flow ? (
// We create a dynamic Recovery form based on the flow using Ory Elements
<UserAuthCard
title="Recovery"
flowType={"recovery"}
// the flow is always required since it contains the UI form elements, UI error messages and csrf token
flow={flow}
Expand Down
1 change: 0 additions & 1 deletion examples/react-spa/src/Registration.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ export const Registration = () => {
return flow ? (
// create a registration form that dynamically renders based on the flow data using Ory Elements
<UserAuthCard
title={"Registration"}
flowType={"registration"}
// we always need to pass the flow to the card since it contains the form fields, error messages and csrf token
flow={flow}
Expand Down
43 changes: 41 additions & 2 deletions examples/react-spa/src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { ThemeProvider, IntlProvider } from "@ory/elements"
import {
ThemeProvider,
IntlProvider,
CustomTranslations,
CustomLanguageFormats,
} from "@ory/elements"

// optional global css reset
import "@ory/elements/assets/normalize.css"
Expand Down Expand Up @@ -28,12 +33,46 @@ import "@ory/elements/assets/jetbrains-mono-font.css"
// required styles for Ory Elements
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: {
"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",
},
}

ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
<React.StrictMode>
<BrowserRouter>
{/* We add the Ory themes here */}
<ThemeProvider themeOverrides={{}}>
<IntlProvider>
{/* We dont need to pass any custom translations */}
{/* <IntlProvider> */}
{/* We pass custom translations */}
<IntlProvider<CustomTranslations>
locale="af"
defaultLocale="en"
customTranslations={customTranslations}
>
<Routes>
<Route path="/" element={<Dashboard />} />
<Route path="/login" element={<Login />} />
Expand Down
Loading
Loading