From 0a3a962780ab25b7750282fb605288e8d3a4e905 Mon Sep 17 00:00:00 2001 From: Konstantin Wohlwend Date: Tue, 19 Nov 2024 00:15:58 +0100 Subject: [PATCH] Translation overrides --- .../fern/docs/pages/components/stack-provider.mdx | 12 ++++++++++-- examples/demo/src/app/layout.tsx | 8 ++++---- .../src/components-page/account-settings.tsx | 6 +++--- .../stack/src/components-page/password-reset.tsx | 4 ++-- .../stack/src/components/credential-sign-in.tsx | 2 +- .../stack/src/components/credential-sign-up.tsx | 2 +- packages/stack/src/providers/stack-provider.tsx | 10 +++++++++- .../stack/src/providers/translation-provider.tsx | 15 ++++++++++++--- 8 files changed, 42 insertions(+), 17 deletions(-) diff --git a/docs/fern/docs/pages/components/stack-provider.mdx b/docs/fern/docs/pages/components/stack-provider.mdx index 11d35acf5..dbb93668d 100644 --- a/docs/fern/docs/pages/components/stack-provider.mdx +++ b/docs/fern/docs/pages/components/stack-provider.mdx @@ -13,6 +13,7 @@ For detailed usage instructions, please refer to the manual section of the [setu - `children`: `React.ReactNode` - The child components to be wrapped by the StackProvider. - `app`: `StackClientApp | StackServerApp` - The Stack app instance to be used. - `lang` (optional): `"en-US" | "de-DE" | "es-419" | "es-ES" | "fr-CA" | "fr-FR" | "it-IT" | "pt-BR" | "pt-PT"` - The language to be used for translations. +- `translationOverrides` (optional): `Record` - A mapping of English translations to translated equivalents. These will take priority over the translations from the language specified in the `lang` property. Note that the keys are case-sensitive. ## Example @@ -22,9 +23,16 @@ import { stackServerApp } from '@/stack'; function App() { return ( - + {/* Your app content */} ); } -``` \ No newline at end of file +``` diff --git a/examples/demo/src/app/layout.tsx b/examples/demo/src/app/layout.tsx index 4a9d6a036..d1268abda 100644 --- a/examples/demo/src/app/layout.tsx +++ b/examples/demo/src/app/layout.tsx @@ -1,9 +1,9 @@ -import React from "react"; -import { Metadata } from "next"; import { StackProvider, StackTheme } from "@stackframe/stack"; -import { stackServerApp } from "src/stack"; -import Provider from "src/components/provider"; +import { Metadata } from "next"; +import React from "react"; import Header from "src/components/header"; +import Provider from "src/components/provider"; +import { stackServerApp } from "src/stack"; import './global.css'; export const metadata: Metadata = { diff --git a/packages/stack/src/components-page/account-settings.tsx b/packages/stack/src/components-page/account-settings.tsx index 9355c79ac..20015ee33 100644 --- a/packages/stack/src/components-page/account-settings.tsx +++ b/packages/stack/src/components-page/account-settings.tsx @@ -595,7 +595,7 @@ function usePasswordSection() { @@ -605,7 +605,7 @@ function usePasswordSection() { { clearErrors('newPassword'); @@ -618,7 +618,7 @@ function usePasswordSection() { { clearErrors('newPassword'); diff --git a/packages/stack/src/components-page/password-reset.tsx b/packages/stack/src/components-page/password-reset.tsx index ef8c04118..c51b92c8d 100644 --- a/packages/stack/src/components-page/password-reset.tsx +++ b/packages/stack/src/components-page/password-reset.tsx @@ -93,7 +93,7 @@ export default function PasswordResetForm(props: { { clearErrors('password'); @@ -105,7 +105,7 @@ export default function PasswordResetForm(props: { { clearErrors('password'); diff --git a/packages/stack/src/components/credential-sign-in.tsx b/packages/stack/src/components/credential-sign-in.tsx index d0223c14e..b8d4cef39 100644 --- a/packages/stack/src/components/credential-sign-in.tsx +++ b/packages/stack/src/components/credential-sign-in.tsx @@ -59,7 +59,7 @@ export function CredentialSignIn() { diff --git a/packages/stack/src/components/credential-sign-up.tsx b/packages/stack/src/components/credential-sign-up.tsx index 3e3c23136..6aeb36621 100644 --- a/packages/stack/src/components/credential-sign-up.tsx +++ b/packages/stack/src/components/credential-sign-up.tsx @@ -68,7 +68,7 @@ export function CredentialSignUp(props: { noPasswordRepeat?: boolean }) { { clearErrors('password'); diff --git a/packages/stack/src/providers/stack-provider.tsx b/packages/stack/src/providers/stack-provider.tsx index 97948bffb..a19918311 100644 --- a/packages/stack/src/providers/stack-provider.tsx +++ b/packages/stack/src/providers/stack-provider.tsx @@ -8,8 +8,16 @@ export default function StackProvider({ children, app, lang, + translationOverrides, }: { lang?: React.ComponentProps['lang'], + /** + * A mapping of English translations to translated equivalents. + * + * These will take priority over the translations from the language specified in the `lang` property. Note that the + * keys are case-sensitive. + */ + translationOverrides?: Record, children: React.ReactNode, // list all three types of apps even though server and admin are subclasses of client so it's clear that you can pass any app: StackClientApp | StackServerApp | StackAdminApp, @@ -19,7 +27,7 @@ export default function StackProvider({ - + {children} diff --git a/packages/stack/src/providers/translation-provider.tsx b/packages/stack/src/providers/translation-provider.tsx index 1743276fe..d83d53960 100644 --- a/packages/stack/src/providers/translation-provider.tsx +++ b/packages/stack/src/providers/translation-provider.tsx @@ -1,12 +1,21 @@ -import { quetzalLocales, quetzalKeys } from "../generated/quetzal-translations"; +import { throwErr } from "@stackframe/stack-shared/dist/utils/errors"; +import { quetzalKeys, quetzalLocales } from "../generated/quetzal-translations"; import { TranslationProviderClient } from "./translation-provider-client"; -export async function TranslationProvider({ lang, children }: { +export async function TranslationProvider({ lang, translationOverrides, children }: { lang: Parameters[0] | undefined, + translationOverrides?: Record, children: React.ReactNode, }) { const locale = quetzalLocales.get(lang ?? (undefined as never)); - return + + const localeWithOverrides = new Map(locale); + for (const [orig, override] of Object.entries(translationOverrides ?? {})) { + const key = quetzalKeys.get(orig as never) ?? throwErr(new Error(`Invalid translation override: Original key ${JSON.stringify(orig)} not found. Make sure you are passing the correct values into the translationOverrides property of the component.`)); + localeWithOverrides.set(key, override); + } + + return {children} ; }