Spacerr uses next-intl for localization. The app ships with English and German by default, and it is designed to be easy to extend with more languages when your product needs them.
What It Includes
The localization layer includes:
- English as the default locale.
- German as an included additional locale.
- Clean default English routes without an
/enprefix for optimal SEO. - Locale prefixed routes for non default languages.
- Localized public pages.
- Localized dashboard routes.
- Localized legal pages.
- Language switching.
- Namespaced message files.
- Locale aware route helpers.
- Canonical URLs and language alternates for public pages.
Route Rules
English is the default locale and uses clean URLs.
/pricing
/dashboard
/blogGerman uses a locale prefix.
/de/pricing
/de/dashboard
/de/blogDo not create /en routes. If a URL does not include a locale segment, it is treated as English.
API routes are not localized. Keep all src/app/api/** routes stable and non localized.
Important Files
Localization files live under src/i18n.
src/i18n
en
de
locales.ts
locale-routing.tsStart with these files:
src/i18n/locales.tsfor supported locale IDs and the default locale.src/i18n/locale-routing.tsfor locale aware routing behavior.src/i18n/en/common.jsonfor shared actions, labels, placeholders, states, and confirmations.src/i18n/en/errors.jsonfor shared error messages.src/i18n/en/product.jsonfor product and landing page copy.src/i18n/en/seo.jsonfor route metadata copy.src/i18n/en/legal.jsonfor legal page copy.- Feature files such as
auth.json,billing.json,ai-chat.json,settings.json,library.json,blog.json, andcontact.jsonfor feature specific copy.
German files mirror the same structure under src/i18n/de.
Message Placement
Put copy in the file that owns the surface.
- Reused actions and labels go in
common.json. - Error messages go in
errors.json. - Landing and pricing copy go in
product.json. - SEO titles, descriptions, and keywords go in
seo.json. - Legal copy goes in
legal.json. - Feature specific UI copy goes in that feature message file.
If the same string appears in multiple places, move it to common.json instead of duplicating it.
Adding A Locale
To add another language:
- Add the locale ID to
src/i18n/locales.ts. - Add a matching message folder under
src/i18n. - Copy the English message file structure.
- Translate each message file.
- Confirm route helpers generate the new locale prefix.
- Check public pages, dashboard pages, legal pages, and blog pages in the new locale.
Keep the default English URLs non prefixed even after adding more locales.
Blog Localization
Blog posts store a locale on each post. The same slug can exist in different languages because the database keeps slug uniqueness scoped to each locale.
Use the same slug for translated versions when it makes sense. That gives search engines and users cleaner matching URLs across languages.
SEO Alternates
Public localized pages should expose language alternates and x-default.
alternates: {
canonical,
languages,
}The default English canonical URL should stay non prefixed. x-default should also point to the clean English URL.
Checklist
When changing localization, verify:
- English routes do not use
/en. - Non default locales use their locale prefix.
- The language switcher points to the matching route.
- Legal pages render translated policy copy.
- Blog lists and posts show the correct language.
- Metadata includes the correct canonical URL and alternates.
- New strings are in the right message namespace.