Internationalization (i18n)
SaaS Box uses next-intl for its internationalization layer. I integrated it deeply into the Next.js App Router to support automatic language detection, URL-based routing, and full Server Component compatibility without sacrificing performance.
Architecture Philosophy
I chose next-intl because it is incredibly lightweight and explicitly built for the modern Next.js 16 paradigm. Instead of loading massive translation dictionaries on the client, translations are resolved on the server and only the required strings are sent to the browser.
URL-Based Locales
Every route in the boilerplate is wrapped in a [locale]folder segment. This ensures SEO-friendly URLs like /fr/dashboardand strict state management across the application.
Flat Dictionary
Unlike highly fragmented setups, I keep all translations within a singlemessages folder at the root. This makes handing files off to translators or AI tools incredibly straightforward.
Core Configuration
The engine is controlled by two specific files in the src/i18n directory.
This file defines the supported languages and the default fallback. It also exports locale-aware navigation helpers (Link,useRouter) that you should use instead of the standard Next.js imports to ensure the locale is preserved across clicks.
This file executes on the server for every incoming request. It determines which JSON dictionary to load based on the URL parameter.
Translation Files
I keep all translations inside the messages/directory at the root of the project. Out of the box, the boilerplate supports English (en.json), French (fr.json), Spanish (es.json), and German (de.json).
You can nest keys deeply within these JSON files to keep things organized by page or component.
Usage in Server Components
Because server components execute on the backend, fetching translations is an asynchronous operation. You must use the getTranslations method.
Usage in Client Components
When you need interactivity (e.g., inside forms or buttons), you use theuseTranslations hook. The Next.js provider automatically injects the JSON dictionary into the client tree, making this synchronous.
Adding a New Locale
Extending the boilerplate to support another language is a simple two-step process.
Create the Dictionary
Create a new JSON file inside the messages/ folder matching your target language code (e.g., it.json for Italian). Copy the structure from en.json and translate the values.
Update the Routing Config
Open src/i18n/routing.ts and append your new language code to the locales array. The middleware will now automatically intercept and route requests for that language.
Frequently Asked Questions
Can I use Markdown or rich text in translations?
Yes. next-intl supports rich text formatting. You can embed tags like <b>bold</b> in your JSON values and resolve them in your components by passing rendering functions to the translation hook.
How do I translate metadata (SEO)?
In your page.tsx or layout.tsx, you can generate dynamic metadata. Use the getTranslationsfunction inside the exported generateMetadata function to assign translated titles and descriptions based on the requested locale.
Official Documentation
Explore advanced formatting, date/time localization, and dynamic variable injection.