i18n.md
GitHub repositoryKeep your interface strings and their translations in a Markdown file. Edit the example below, change the language or item count, and see the result.
Use the translation
Add another language
Paste that Markdown block into any LLM and ask it to add a language. Try Hawaiian, Chinese, Esperanto, or whatever you like. Paste the result back here and click Apply changes to see the demo in Klingon, Pirate, or whatever you like.
Extract user-visible strings
Give this prompt to a coding agent in your application repository. It asks the agent to inspect the existing translation setup before changing source files. You can also use it as the instructions for an agent skill.
Inspect this application's existing translation setup and reuse it where possible. Find user-visible strings in pages, components, menus, buttons, form labels, accessibility text, validation errors, notifications, and server responses shown to users. Exclude identifiers, URLs, logs, and internal messages.
Create one translations.i18n.md file using the i18n.md format: source language, syntax: icu, and a JSON languages map in the metadata; then a ## token heading, a Context line, and a fenced language block for each message. Preserve the original wording. Use stable token names and reuse tokens only when their meanings and contexts match.
Keep complete sentences together. Replace dynamic values with named placeholders. Use ICU plural or select expressions when the message depends on a count or choice; do not build translated sentences by concatenating fragments. Include context for ambiguous terms and any tone or formality requirements.
Replace the source strings with i18nmd(token, locale, values) calls using the application's locale selection. Keep source behavior, escaping, and accessibility intact. Review dynamic messages and rich markup manually. Do not publish source code, strings, or other project information externally.
Validate the Markdown, compile it into application files, and run focused checks of the changed UI. Report unresolved strings and the files changed. Do not claim every string was found unless you verified each user-facing path.
Use the extractor for supported source files
From the tool checkout, run the extractor on a JavaScript or TypeScript source directory. It writes converted copies to the destination directory. Review those copies before integrating them into your application. Dynamic expressions and rich markup need manual review.
node bin/i18nmd.mjs extract /path/to/app/src \ --out translations.i18n.md --dest .i18n/src \ --runtime .i18n/generated/i18n --language locale
Make the application's locale variable available at each generated call. Use --merge on later runs to preserve existing tokens and translations.
Compile the translation file
The compiler checks the declared languages and placeholders, then writes files your application can import. Run these commands from the tool checkout:
npm ci --ignore-scripts node bin/i18nmd.mjs check translations.i18n.md node bin/i18nmd.mjs compile translations.i18n.md \ --target ts --out /path/to/app/src/i18n
The TypeScript output contains i18n.ts, runtime.mjs, and runtime.d.mts. Keep them together. Import the generated function in a component:
import { i18nmd } from './i18n/i18n';
function Cart({ locale, count }) {
return <p>{i18nmd('cart_items', locale, { count })}</p>;
}
Recompile after editing translations. The compiler also accepts --target js, json, or python. Python output requires a catalog with syntax: python; JSON output needs a compatible message formatter in your application.
Try the browser compiler
The demo's Download TypeScript button compiles the current Markdown into i18n.ts. Save these companion files in the same directory:
Translate this site
Copy this page’s Markdown file into an LLM and ask it to add a language. Paste the result below, apply it, and choose the language to translate the page.