Adding new translations | Next.js Supabase SaaS Kit
Learn how to add new languages, create translation files, and organize namespaces in your Next.js Supabase SaaS application.
This guide covers adding new languages, creating translation files, and organizing your translations into namespaces.
Steps to add new translations
Learn how to add new translations to your Next.js Supabase SaaS project.
1. Create Language Files
Translation files live in apps/web/public/locales/[language]/. Each language needs its own folder with JSON files matching your namespaces.
Create the Language Folder
Create a new folder using the ISO 639-1 language code:
mkdir apps/web/public/locales/esCommon language codes:
de- Germanes- Spanishfr- Frenchit- Italianja- Japanesept- Portuguesezh- Chinese
Regional Language Codes
For regional variants like es-ES (Spanish - Spain) or pt-BR (Portuguese - Brazil), use lowercase with a hyphen:
# Correctmkdir apps/web/public/locales/es-esmkdir apps/web/public/locales/pt-br# Incorrect - will not workmkdir apps/web/public/locales/es-ESThe system normalizes language codes to lowercase internally.
Copy and Translate Files
Copy the English files as a starting point:
cp apps/web/public/locales/en/*.json apps/web/public/locales/es/Then translate each JSON file. Here's an example for common.json:
{ "homeTabLabel": "Inicio", "cancel": "Cancelar", "clear": "Limpiar", "goBack": "Volver", "tryAgain": "Intentar de nuevo", "loading": "Cargando. Por favor espere...", "routes": { "home": "Inicio", "account": "Cuenta", "billing": "Facturacion" }}Keep the same key structure as the English files. Only translate the values.
2. Register the Language
Add your new language to the settings file:
/** * The default language of the application. */const defaultLanguage = process.env.NEXT_PUBLIC_DEFAULT_LOCALE ?? 'en';/** * The list of supported languages. * Add more languages here as needed. */export const languages: string[] = [defaultLanguage, 'es', 'de', 'fr'];The order matters for fallback behavior:
- First language is the default fallback
- When a translation is missing, the system falls back through this list
Verify the Registration
After adding a language, verify it works:
- Restart the development server
- Open browser DevTools > Application > Cookies
- Set the
langcookie to your new language code (e.g.,es) - Refresh the page
You should see your translations appear.
3. Add Custom Namespaces
Namespaces organize translations by feature or domain. The default namespaces are:
export const defaultI18nNamespaces = [ 'common', // Shared UI elements 'auth', // Authentication flows 'account', // Account settings 'teams', // Team management 'billing', // Billing and subscriptions 'marketing', // Marketing pages];Create a New Namespace
Create the JSON file for each language:
# Create for Englishtouch apps/web/public/locales/en/projects.json# Create for other languagestouch apps/web/public/locales/es/projects.jsonAdd your translations
{ "title": "Projects", "createProject": "Create Project", "projectName": "Project Name", "projectDescription": "Description", "deleteProject": "Delete Project", "confirmDelete": "Are you sure you want to delete this project?", "status": { "active": "Active", "archived": "Archived", "draft": "Draft" }}Register the namespace:
export const defaultI18nNamespaces = [ 'common', 'auth', 'account', 'teams', 'billing', 'marketing', 'projects', // Your new namespace];Use the namespace in your components:
import { Trans } from '@kit/ui/trans';function ProjectsPage() { return ( <div> <h1> <Trans i18nKey="projects:title" /> </h1> <button> <Trans i18nKey="projects:createProject" /> </button> </div> );}Namespace Best Practices
Keep namespaces focused: Each namespace should cover a single feature or domain.
Good:- projects.json (project management)- invoices.json (invoicing feature)- notifications.json (notification system)Avoid:- misc.json (too vague)- page1.json (not semantic)Use consistent key naming:
{ "title": "Page title", "description": "Page description", "actions": { "create": "Create", "edit": "Edit", "delete": "Delete" }, "status": { "loading": "Loading...", "error": "An error occurred", "success": "Success!" }}Avoid duplicating common strings: Use the common namespace for shared strings like "Cancel", "Save", "Loading".
4. Translate Email Templates
Email templates have their own translation system in packages/email-templates/src/locales/.
Email Translation Structure
packages/email-templates/src/locales/└── en/ ├── account-delete-email.json ├── invite-email.json └── otp-email.jsonAdd Email Translations for a New Language
Create the language folder:
mkdir packages/email-templates/src/locales/esCopy and translate the email files:
cp packages/email-templates/src/locales/en/*.json packages/email-templates/src/locales/es/Translate the content:
{ "subject": "Has sido invitado a unirte a un equipo", "heading": "Unete a {{teamName}} en {{productName}}", "hello": "Hola {{invitedUserEmail}},", "mainText": "<strong>{{inviter}}</strong> te ha invitado al equipo <strong>{{teamName}}</strong> en <strong>{{productName}}</strong>.", "joinTeam": "Unirse a {{teamName}}", "copyPasteLink": "o copia y pega esta URL en tu navegador:", "invitationIntendedFor": "Esta invitacion es para {{invitedUserEmail}}."}Email templates support interpolation with {{variable}} syntax and basic HTML tags.
Organizing Large Translation Files
For applications with many translations, consider splitting by feature:
apps/web/public/locales/en/├── common.json # 50-100 keys max├── auth.json├── account.json├── billing/│ ├── subscriptions.json│ ├── invoices.json│ └── checkout.json└── features/ ├── projects.json ├── analytics.json └── integrations.jsonUpdate your namespace registration accordingly:
export const defaultI18nNamespaces = [ 'common', 'auth', 'account', 'billing/subscriptions', 'billing/invoices', 'features/projects',];Translation Workflow Tips
Use Placeholders During Development
When adding new features, start with English placeholders:
{ "newFeature": "[TODO] New feature title", "newFeatureDescription": "[TODO] Description of the new feature"}This makes untranslated strings visible and searchable.
Maintain Translation Parity
Keep all language files in sync. When adding a key to one language, add it to all:
# Check for missing keys (example script)diff <(jq -r 'keys[]' locales/en/common.json | sort) \ <(jq -r 'keys[]' locales/es/common.json | sort)Consider Translation Services
For production applications, integrate with translation services:
These services can:
- Sync with your JSON files via CLI or CI/CD
- Provide translator interfaces
- Handle pluralization rules per language
- Track translation coverage
RTL Language Support
For right-to-left languages like Arabic (ar) or Hebrew (he):
- Add the language as normal to
i18n.settings.ts - Create a client component to detect the current language and set the
dirattribute:
'use client';import { useEffect } from 'react';import { useTranslation } from 'react-i18next';const rtlLanguages = ['ar', 'he', 'fa', 'ur'];export function RtlProvider({ children }: { children: React.ReactNode }) { const { i18n } = useTranslation(); useEffect(() => { const isRtl = rtlLanguages.includes(i18n.language); document.documentElement.dir = isRtl ? 'rtl' : 'ltr'; document.documentElement.lang = i18n.language; }, [i18n.language]); return children;}- Wrap your app with the provider in
RootProviders:
import { RtlProvider } from './rtl-provider';export function RootProviders({ children }: { children: React.ReactNode }) { return ( <I18nProvider settings={i18nSettings} resolver={i18nResolver}> <RtlProvider> {children} </RtlProvider> </I18nProvider> );}- Use Tailwind's RTL utilities (
rtl:prefix) for layout adjustments:
<div className="ml-4 rtl:ml-0 rtl:mr-4"> {/* Content flows correctly in both directions */}</div>Frequently Asked Questions
How do I verify my translations are working?
Do I need to translate every single key?
Can I use nested folders for namespaces?
How do I handle pluralization in different languages?
Should translation files be committed to git?
Related Documentation
- Using Translations - Use translations in your components
- Language Selector - Add a language switcher
- Email Translations - Translate email templates