Deterministic Markdown i18n preprocessor for maintaining one multilingual Markdown source and compiling localized output files.
You write a single source file such as README.i18n.md and compile it into multiple localized files:
README.mdREADME.en.mdREADME.es.mdREADME.pt.md
This keeps documentation in one place while still producing language-specific Markdown files for publishing.
- simple
:::lang <code>block syntax - shared content outside language blocks
- multiple languages per block
- deterministic output
- fenced code block awareness
- missing translation detection
- optional default output file such as
README.md - watch mode for automatic rebuilds
This project currently uses plain Node.js with no external runtime dependencies.
Requirements:
- Node.js installed and available in
PATH
For local development:
npm installscripts/build-readme.js
src/README.i18n.md
dist/
test/build-readme.test.js
Any Markdown outside :::lang blocks is copied to every generated language file.
# My Project
This paragraph appears in all outputs.:::lang en
Hello world
:::
:::lang es
Hola mundo
::::::lang en,es
This content is shared by English and Spanish.
:::Language markers inside fenced code blocks are ignored by the parser.
```js
const marker = ":::lang en";
```Ignore sections are preserved as-is.
<!-- i18n-ignore-start -->
```bash
npm install
```
<!-- i18n-ignore-end -->Run:
node .\scripts\build-readme.js build .\src\README.i18n.md --langs en,es,pt --out-dir .\dist --default enThis generates:
dist/README.en.mddist/README.es.mddist/README.pt.mddist/README.md
Validate structure and missing translations without writing output files:
node .\scripts\build-readme.js check .\src\README.i18n.md --langs en,es,ptIf validation passes, the command prints:
md-i18n: check passed
Rebuild automatically when *.i18n.md files change.
Watch one file:
node .\scripts\build-readme.js watch .\src\README.i18n.md --langs en,es,pt --out-dir .\dist --default enWatch a directory:
node .\scripts\build-readme.js watch .\src --langs en,es,pt --out-dir .\dist --default enBehavior:
- builds all matching
.i18n.mdfiles once at startup - watches those files for changes
- starts watching new
.i18n.mdfiles created in the watched directory - stops watching
.i18n.mdfiles removed from the watched directory - recompiles automatically after each save
- writes outputs without needing a manual build command
md-i18n build <input> --langs en,es,pt [options]
md-i18n check <input> --langs en,es,pt [options]
md-i18n watch <path> --langs en,es,pt [options]
--langs <list> Comma-separated target languages
--out-dir <dir> Output directory
--default <lang> Also emit README.md using this language
--default-name <file> Default output file name
--allow-missing Do not fail when a localized group misses languages
By default, the build fails if a localized group does not cover all requested languages.
Example:
:::lang en
Hello
:::
:::lang es
Hola
:::If you build with:
node .\scripts\build-readme.js build .\src\README.i18n.md --langs en,es,ptthe compiler fails because pt is missing for that localized group.
If you want to allow partial translations:
node .\scripts\build-readme.js build .\src\README.i18n.md --langs en,es,pt --allow-missingRun the automated tests with:
node --test --test-isolation=noneOr through npm:
npm testThe current test suite covers:
- common vs localized content
- fenced code block handling
- missing translation detection
- output generation
- optional missing translation allowance
- watch-related path handling
- Edit
src/README.i18n.md - Run
check - Start
watchduring active editing, or run the build command manually - Run the tests
- Review the generated files in
dist/
Once published, a consumer project would typically install it with:
npm install -D markdown-i18nThen use it through scripts:
{
"scripts": {
"docs:check": "md-i18n check README.i18n.md --langs en,es,pt",
"docs:build": "md-i18n build README.i18n.md --langs en,es,pt --out-dir . --default en",
"docs:watch": "md-i18n watch . --langs en,es,pt --out-dir . --default en"
}
}That gives the user the workflow:
- edit
README.i18n.md - leave
docs:watchrunning during edits - run
docs:checkin CI or before commit
Before first publish:
- verify the package name you want on npm
- bump the version in
package.json - add a repository remote and then fill
repository,homepage, andbugs - create an npm access token and store it as
NPM_TOKENin GitHub Actions secrets
Manual local publish:
npm publishAutomated publish:
- the repo includes
.github/workflows/publish.yml - publishing a GitHub release, or manually dispatching the workflow, will publish to npm
prepublishOnlyruns tests andcheckbefore publish
Before enabling the publish workflow in GitHub Actions, update package.json with your real repository URLs.
Current implementation is intentionally minimal:
- no external dependencies
- no Markdown AST
- no runtime translation
- no editor integration yet
MIT