Languages
The eight built-in languages, how missing translations fall back to English, adding a ninth language, and asking the agent to translate.
The site ships in eight languages. English is the default and the source every other language is checked against.
| Code | Language | URL |
|---|---|---|
en |
English | /pricing (no prefix) |
zh |
中文 | /zh/pricing |
ja |
日本語 | /ja/pricing |
fr |
Français | /fr/pricing |
de |
Deutsch | /de/pricing |
ko |
한국어 | /ko/pricing |
es |
Español | /es/pricing |
it |
Italiano | /it/pricing |
The list lives in i18n/routing.ts (LOCALES). English has no prefix in the URL; every other language has its code. The sitemap and the hreflang tags on each page list every language automatically.
Visitors pick a language with the switcher in the header. If you set NEXT_PUBLIC_LOCALE_DETECTION=true, first-time visitors are also sent to the language their browser asks for.
Where the text lives
| What | Where |
|---|---|
| Page and interface text | i18n/messages/<code>/, one JSON file per page or area (Landing.json, Pricing.json, Dashboard/Admin/Blogs.json …); common.json holds the header, footer, login and shared text |
| Plan names, descriptions and features | config/pricing.ts, inside each plan's copy |
| The About page | content/about/<code>.mdx |
| Blog posts written as files | blogs/<code>/*.mdx |
| Blog and glossary posts written in the admin | the database, one post per language (see Blog and glossary) |
| Social preview images | public/og.png for English, public/og_<code>.png for the others |
Missing text falls back to English, key by key
i18n/request.ts loads the English file first and lays the translation over it. Any key the translation lacks, or leaves empty, shows the English text. A whole missing file shows English for that page. A list (for example the features on the landing page) is taken from the translation only when it has as many items as the English one; otherwise the English list shows.
So a half-translated language never breaks a page; it shows some English. The same holds for plan text: a plan without copy.<code> shows its English text.
Asking the agent to translate
The agent writes every language itself. Useful requests:
I changed the hero text in i18n/messages/en/Landing.json.
Update the same keys in the other seven languages, each written in its own language.
Check every language folder against English and translate any key that is missing or still in English.
Translate the card text of the new "pro-monthly" plan in config/pricing.ts into every language in LOCALES.
Name the source of truth (usually the English file) and ask for "written in its own language", not word-for-word translation. Have a native speaker read the pages that sell (landing, pricing) before launch.
For blog and glossary posts there is also a translate button in the admin editor; see Blog and glossary.
Adding a language
Tell the agent: "Add Portuguese (pt) as a ninth language." It works through this list:
i18n/routing.ts: add the code toLOCALESand one entry to each table below it:LOCALE_NAMES(the language's own name, shown in the switcher),SHORT_LOCALE_NAMES,LOCALE_ENGLISH_NAMES(used when telling an AI model which language to write),LOCALE_TO_HREFLANG(for examplept-BR).proxy.ts: the locale list in thematcher(the line'/(en|zh|ja)/:path*'); the file's header asks to keep it in step withLOCALES.i18n/messages/pt/: a copy of theen/folder, translated. Anything left out shows English.config/pricing.ts: aptentry in each plan'scopy.content/about/pt.mdx: the About page.public/og_pt.png: the social preview image for that language. The starter shipsog.png,og_zh.pngandog_ja.pngonly, so the other languages point to an image that does not exist until you add it.- Optional:
blogs/pt/for file-based posts.
Then it runs pnpm lint and opens a few pages under /pt/.
To remove a language, do the same in reverse. Pages already indexed by search engines in that language will answer 404.
When it fails
| What you see | Cause | Fix |
|---|---|---|
| A page shows English in another language | the key or the file is missing in that language | ask the agent to translate the missing keys |
| A list shows in English while the rest is translated | the translated list has a different number of items | make the lists the same length |
/pt/… answers 404 for every page |
the code is missing from LOCALES |
add it in i18n/routing.ts |
A new page's text shows as raw keys (Landing.title) in every language |
its message file is not registered in the FILES table of i18n/request.ts |
the agent registers it |
| The About page is empty in one language | content/about/<code>.mdx is missing |
add the file |
| Shared links in one language show no preview image | public/og_<code>.png is missing |
add the image |