GitHub Action to translate MARKDOWN/READMEs to any language
Translate MARKDOWN files to various other languages, supports multiple files and subdirectories.
Add a workflow file to your project (e.g. .github/workflows/translate-readme.yml):
name: Translate README
on:
push:
branches:
- main
- master
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 24.x
# ISO Language Codes: https://cloud.google.com/translate/docs/languages
- name: Adding README - Chinese Simplified
uses: ikhsan3adi/markdown-translator@v3
with:
LANG: zh-CN
FILES: |-
README.md
OTHER-README.md
sub/dir/README.md
very/deep/sub/directories/FOO.md
/very/deep/sub/directories/BAR.md
docs/**/*.md
notes/*.txt
- name: Adding README translations
uses: ikhsan3adi/markdown-translator@v3
with:
LANG: |- # multiple languages
zh-CN
zh-TW
ja
FILES: |-
README.md
docs/**/*.mdGlob support:
FILESaccepts glob patterns —docs/**/*.mdtranslates every markdown file underdocs/recursively without listing them one by one, andnotes/*.txtshows plain-text files work too. Literal nested paths work the same way:sub/dir/README.mdandvery/deep/sub/directories/FOO.mdneed no globbing. Mix globs and literal paths freely; exclude paths withIGNORE(e.g.docs/external/**). Files already named like<name>.<lang>.<ext>(existing translations) are always skipped.Write permission required: the job must declare
permissions: contents: write(shown above) or the push back to your repo is rejected with a 403. Prefer a review step? SetPULL_REQUEST: trueto push a branch and open a PR instead (needspermissions: pull-requests: writeas well).Incremental by default: only files whose source changed are re-translated, saving translation quota. Changes are tracked via a
<!-- markdown-translator:<hash> -->header comment written into each translation file. Existing translations without this header are re-translated once.Markdown vs plain text: markdown files (
.md,.markdown,.mdx,.mdown,.mkd) are translated markdown-aware — code spans, fenced blocks, frontmatter and URLs stay untouched. Any other file (e.g..txt) is translated line by line, preserving blank lines and line endings.
npm run build before committing
You can configure the action further with the following options:
-
LANG: Newline-separated list of the language you want to translate your readme to. The default is Simplified Chinese. The supported languages can be found below. (default:zh-CN) (required:false) -
FILES: Newline-separated files to translate. Supports glob patterns (e.g.docs/**/*.md) and literal nested paths (e.g.sub/dir/README.md). Markdown files are translated markdown-aware (code spans, frontmatter, fenced blocks and URLs untouched); other files (e.g..txt) are translated line by line, preserving blank lines and line endings. (default:[README.md]) (required:false) -
IGNORE: Newline-separated glob patterns to exclude whenFILESuses globbing. (default: ``) (required:false) -
IGNORE_PATTERN: Newline-separated regex patterns. Any text matching a pattern is left untranslated — useful for code, command output or proper nouns. (default: ``) (required:false) -
COMMIT: Whether to commit the generated translations. Set tofalsefor a dry-run that only writes the translation files. (default:true) (required:false) -
PUSH: Whether to push the commits back to the repository. (default:true) (required:false) -
PULL_REQUEST: Push to a branch and open a pull request instead of committing to the current branch. (default:false) (required:false) -
INCREMENTAL: Only re-translate files whose source changed. The source hash is tracked in a header comment of the translation file, so existing translations are skipped when unchanged — saves translation quota. (default:true) (required:false)
FILES_CREATED: Newline-separated list of the translation files that were generated.
- name: Adding README translations as a PR
uses: ikhsan3adi/markdown-translator@master
with:
LANG: zh-CN
FILES: README.md
PULL_REQUEST: true
PUSH: true- name: Generate README translations locally
uses: ikhsan3adi/markdown-translator@master
with:
LANG: zh-CN
FILES: README.md
COMMIT: false- name: Translate all docs
uses: ikhsan3adi/markdown-translator@master
with:
LANG: ja
FILES: |-
README.md
docs/**/*.md
IGNORE: |-
docs/external/**/*.mdnpm start runs the same logic from your terminal and defaults COMMIT/PUSH to false,
so it only writes the translation files — no commit, no push. Flags mirror the action inputs
(kebab-case), separated by ,:
npm install
# translate README.md to Indonesian, dry-run only
npm start -- --lang id --files README.md
# multiple languages and glob patterns
npm start -- --lang "id,ja" --files "README.md,docs/**/*.md" --ignore "docs/external/**"
# skip text matching a regex
npm start -- --lang zh-CN --files README.md --ignore-pattern "^\|.*\|$"
# translate plain text files too
npm start -- --lang id --files notes.txt
# full local run with commit + push
npm start -- --lang id --files README.md --commit --push
npm start -- --helpLanguages supported can be found here https://cloud.google.com/translate/docs/languages
Suggestions and contributions are always welcome!