Localization prop (experimental)
Clerk offers the ability to override the strings for all of the elements in each of the Clerk components. This allows you to provide localization for your users or change the wording to suit your brand.
@clerk/localizations
The @clerk/localizations package contains predefined localizations for the Clerk components.
Languages
Clerk currently supports the following languages with English as the default:
Usage
To get started, install the @clerk/localizations package.
npm install @clerk/localizationspnpm add @clerk/localizationsyarn add @clerk/localizationsbun add @clerk/localizationsOnce the @clerk/localizations package is installed, you can import the localizations you need by removing the "-" from the locale. You can then pass the localization to the localization prop.
To apply a localization, pass the appearance prop to the <ClerkProvider> component. The appearance prop accepts the property localization, which can be set to the imported localization.
In the following example, the fr-FR locale is imported as frFR and applied to all Clerk components.
// fr-FR locale is imported as frFR
import { frFR } from '@clerk/localizations'
<ClerkProvider localization={frFR}>
{/* ... */}
</ClerkProvider>To apply a localization, pass the appearance prop to the clerk() integration. The appearance prop accepts the property localization, which can be set to the imported localization.
In the following example, the fr-FR locale is imported as frFR and applied to all Clerk components.
import { defineConfig } from 'astro/config'
import clerk from '@clerk/astro'
// fr-FR locale is imported as frFR
import { frFR } from '@clerk/localizations'
export default defineConfig({
integrations: [
clerk({
localization: frFR,
}),
],
})To apply a localization, pass the appearance prop to the clerk.load() method. The appearance prop accepts the property localization, which can be set to the imported localization.
In the following example, the fr-FR locale is imported as frFR and applied to all Clerk components.
Use the following tabs to view the code necessary for each file.
import { Clerk } from '@clerk/clerk-js'
// fr-FR locale is imported as frFR
import { frFR } from '@clerk/localizations'
const clerkPubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY
const clerk = new Clerk(clerkPubKey)
await clerk.load({
localization: frFR,
})
if (clerk.isSignedIn) {
document.getElementById('app').innerHTML = `
<div id="user-button"></div>
`
const userButtonDiv = document.getElementById('user-button')
clerk.mountUserButton(userButtonDiv)
} else {
document.getElementById('app').innerHTML = `
<div id="sign-in"></div>
`
const signInDiv = document.getElementById('sign-in')
clerk.mountSignIn(signInDiv)
}<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Clerk + JavaScript App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="main.js" async crossorigin="anonymous"></script>
</body>
</html>To apply a localization, pass the appearance prop to the clerkPlugin() integration. The appearance prop accepts the property localization, which can be set to the imported localization.
In the following example, the fr-FR locale is imported as frFR and applied to all Clerk components.
import { createApp } from 'vue'
import App from './App.vue'
import { clerkPlugin } from '@clerk/vue'
// fr-FR locale is imported as frFR
import { frFR } from '@clerk/localizations'
const app = createApp(App)
app.use(clerkPlugin, {
localization: frFR,
})
app.mount('#app')To apply a localization, pass the appearance prop to the defineNuxtConfig() integration. The appearance prop accepts the property localization, which can be set to the imported localization.
In the following example, the fr-FR locale is imported as frFR and applied to all Clerk components.
// fr-FR locale is imported as frFR
import { frFR } from '@clerk/localizations'
export default defineNuxtConfig({
modules: ['@clerk/nuxt'],
clerk: {
localization: frFR,
},
})To apply a localization, pass the appearance prop to the clerkPlugin() integration. The appearance prop accepts the property localization, which can be set to the imported localization.
In the following example, the fr-FR locale is imported as frFR and applied to all Clerk components.
import Fastify from 'fastify'
import { clerkPlugin } from '@clerk/fastify'
// fr-FR locale is imported as frFR
import { frFR } from '@clerk/localizations'
const fastify = Fastify({ logger: true })
fastify.register(clerkPlugin, {
localization: frFR,
})Adding or updating a localization
Clerk's localizations are customer-sourced and we encourage customers to add or update localizations. To do so, follow these steps:
- Fork the https://github.com/clerk/javascript/ repo.
- Clone it locally to edit it.
- Review Clerk's Contributing guide.
- If you are updating an existing localization locate the file in
packages/localizations/src - If you are adding a new language, copy the
en-US.tsfile and name it according to your language. The naming is the abbreviated language-region. For example, for French in Canada, it would befr-CA.ts. - Go through the file and edit the entries.
- If you are adding a new localization, add the language to the
packages/localizations/src/index.tsfile. - Commit your changes to git and push them to your fork. Create a Pull Request from your fork to Clerk's repo against the
mainbranch. We will review and either approve or ask for updates.
Custom localizations
You can also provide your own localizations for the Clerk components. This is useful if you want to provide limited or quick localization for a language Clerk doesn't currently support, adjust the wording to match your brand, or customize default error messages.
First, you need to find the key for the element that you want to customize. To find the key for your translation, open up Clerk's English localization file. Search the file for the term that you want to customize.
For example, say you want to change the text of the "Continue" button on the <SignIn /> component to say "LETS GO!". In this case, you'd search for "Continue". The first result that comes up is formButtonPrimary, which is the key for the "Continue" button.
Now that you know the key, you can pass it to the localization prop and set the value to the text you want to display. In this case, you'd set the value to "LETS GO!", as shown in the following example.
// Set your customizations in a `localization` object
const localization = {
formButtonPrimary: 'LETS GO!',
}
<ClerkProvider localization={localization}>
{/* ... */}
</ClerkProvider>In Astro, pass the localization prop to the clerk() integration.
import { defineConfig } from 'astro/config'
import clerk from '@clerk/astro'
export default defineConfig({
integrations: [
clerk({
localization: {
formButtonPrimary: 'LETS GO!',
},
}),
],
})Use the following tabs to view the code necessary for each file.
import { Clerk } from '@clerk/clerk-js'
const clerkPubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY
const clerk = new Clerk(clerkPubKey)
await clerk.load({
localization: {
formButtonPrimary: 'LETS GO!',
},
})
if (clerk.isSignedIn) {
document.getElementById('app').innerHTML = `
<div id="user-button"></div>
`
const userButtonDiv = document.getElementById('user-button')
clerk.mountUserButton(userButtonDiv)
} else {
document.getElementById('app').innerHTML = `
<div id="sign-in"></div>
`
const signInDiv = document.getElementById('sign-in')
clerk.mountSignIn(signInDiv)
}<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Clerk + JavaScript App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="main.js" async crossorigin="anonymous"></script>
</body>
</html>import { createApp } from 'vue'
import App from './App.vue'
import { clerkPlugin } from '@clerk/vue'
const app = createApp(App)
app.use(clerkPlugin, {
localization: {
formButtonPrimary: 'LETS GO!',
},
})
app.mount('#app')export default defineNuxtConfig({
modules: ['@clerk/nuxt'],
clerk: {
localization: {
formButtonPrimary: 'LETS GO!',
},
},
})import Fastify from 'fastify'
import { clerkPlugin } from '@clerk/fastify'
const fastify = Fastify({ logger: true })
fastify.register(clerkPlugin, {
localization: {
formButtonPrimary: 'LETS GO!',
},
})You can also customize multiple entries by passing multiple keys. The following example updates the "to continue to" subtitles on the <SignUp /> component to say "to access" instead.
const localization = {
signUp: {
start: {
subtitle: 'to access {{applicationName}}',
},
emailCode: {
subtitle: 'to access {{applicationName}}',
},
},
}
<ClerkProvider localization={localization}>
{/* ... */}
</ClerkProvider>import { defineConfig } from 'astro/config'
import clerk from '@clerk/astro'
export default defineConfig({
integrations: [
clerk({
localization: {
signUp: {
start: {
subtitle: 'to access {{applicationName}}',
},
emailCode: {
subtitle: 'to access {{applicationName}}',
},
},
},
}),
],
})Use the following tabs to view the code necessary for each file.
import { Clerk } from '@clerk/clerk-js'
const clerkPubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY
const clerk = new Clerk(clerkPubKey)
await clerk.load({
localization: {
signUp: {
start: {
subtitle: 'to access {{applicationName}}',
},
emailCode: {
subtitle: 'to access {{applicationName}}',
},
},
},
})
if (clerk.isSignedIn) {
document.getElementById('app').innerHTML = `
<div id="user-button"></div>
`
const userButtonDiv = document.getElementById('user-button')
clerk.mountUserButton(userButtonDiv)
} else {
document.getElementById('app').innerHTML = `
<div id="sign-in"></div>
`
const signInDiv = document.getElementById('sign-in')
clerk.mountSignIn(signInDiv)
}<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Clerk + JavaScript App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="main.js" async crossorigin="anonymous"></script>
</body>
</html>import { createApp } from 'vue'
import App from './App.vue'
import { clerkPlugin } from '@clerk/vue'
const app = createApp(App)
app.use(clerkPlugin, {
localization: {
signUp: {
start: {
subtitle: 'to access {{applicationName}}',
},
emailCode: {
subtitle: 'to access {{applicationName}}',
},
},
},
})
app.mount('#app')export default defineNuxtConfig({
modules: ['@clerk/nuxt'],
clerk: {
localization: {
signUp: {
start: {
subtitle: 'to access {{applicationName}}',
},
emailCode: {
subtitle: 'to access {{applicationName}}',
},
},
},
},
})import Fastify from 'fastify'
import { clerkPlugin } from '@clerk/fastify'
const fastify = Fastify({ logger: true })
fastify.register(clerkPlugin, {
localization: {
signUp: {
start: {
subtitle: 'to access {{applicationName}}',
},
emailCode: {
subtitle: 'to access {{applicationName}}',
},
},
},
})Customize error messages
You can customize Clerk's default error messages by targeting the unstable__errors key. This key lets you define specific error keys for different error types and assign them custom message strings. You can find the full list of error keys in the English localization file. Search for the unstable__errors object to find the keys you can customize.
The following example updates the not_allowed_access error message. This message appears when a user tries to sign in with an email domain that isn't allowed to access your application.
const localization = {
unstable__errors: {
not_allowed_access:
'Send us an email if you want your corporate email domain allowlisted for access',
},
}
<ClerkProvider localization={localization}>
{/* ... */}
</ClerkProvider>import { defineConfig } from 'astro/config'
import clerk from '@clerk/astro'
export default defineConfig({
integrations: [
clerk({
localization: {
unstable__errors: {
not_allowed_access:
'Send us an email if you want your corporate email domain allowlisted for access',
},
},
}),
],
})Use the following tabs to view the code necessary for each file.
import { Clerk } from '@clerk/clerk-js'
const clerkPubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY
const clerk = new Clerk(clerkPubKey)
await clerk.load({
localization: {
unstable__errors: {
not_allowed_access:
'Send us an email if you want your corporate email domain allowlisted for access',
},
},
})
if (clerk.isSignedIn) {
document.getElementById('app').innerHTML = `
<div id="user-button"></div>
`
const userButtonDiv = document.getElementById('user-button')
clerk.mountUserButton(userButtonDiv)
} else {
document.getElementById('app').innerHTML = `
<div id="sign-in"></div>
`
const signInDiv = document.getElementById('sign-in')
clerk.mountSignIn(signInDiv)
}<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Clerk + JavaScript App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="main.js" async crossorigin="anonymous"></script>
</body>
</html>import { createApp } from 'vue'
import App from './App.vue'
import { clerkPlugin } from '@clerk/vue'
const app = createApp(App)
app.use(clerkPlugin, {
localization: {
unstable__errors: {
not_allowed_access:
'Send us an email if you want your corporate email domain allowlisted for access',
},
},
})
app.mount('#app')export default defineNuxtConfig({
modules: ['@clerk/nuxt'],
clerk: {
localization: {
unstable__errors: {
not_allowed_access:
'Send us an email if you want your corporate email domain allowlisted for access',
},
},
},
})import Fastify from 'fastify'
import { clerkPlugin } from '@clerk/fastify'
const fastify = Fastify({ logger: true })
fastify.register(clerkPlugin, {
localization: {
unstable__errors: {
not_allowed_access:
'Send us an email if you want your corporate email domain allowlisted for access',
},
},
})Feedback
Last updated on