Building an i18n Pipeline for API Docs That Doesn't Break Every Release
The problem nobody budgets for If you've ever maintained a developer portal, you know the docs change faster than anyone wants to admit. New endpoint, new param, deprecation notice, a fixed typo in a code sample β it n
The problem nobody budgets for
If you've ever maintained a developer portal, you know the docs change faster than anyone wants to admit. New endpoint, new param, deprecation notice, a fixed typo in a code sample β it never stops. Now add translation into that mix and most teams panic and either give up on localisation entirely or bolt on a manual process that breaks within two releases.
There's a good write-up on the content and terminology side of this problem in this article on API documentation localisation. It covers why API docs need different handling than a user manual: code should never be translated, terminology has to stay locked across every page, and the whole thing is a moving target instead of a static document.
That's the editorial side. This post is about the plumbing: how to actually wire translation into a docs pipeline that's built on Git, Markdown, and CI, without it turning into a manual copy-paste nightmare every sprint.
Don't translate the whole file, translate the diff
The single biggest mistake teams make is sending the entire docs repo to translation every time something changes. If you have 300 pages of OpenAPI-generated reference and you change one endpoint, you don't need 299 pages retranslated β you need a diff-based workflow.
A reasonable setup looks like this:
# Generate a diff of changed docs since last translated commit
git diff last-translated-tag HEAD -- docs/ > changes.diff
# Extract only the files with actual content changes (not formatting)
git diff --name-only last-translated-tag HEAD -- docs/*.md docs/*.mdx
Feed only the changed files into your translation step, then re-tag once the translated version comes back:
git tag last-translated-tag HEAD
This is the same mental model as incremental builds. Translating everything every time is like rebuilding your entire project instead of just the changed modules β it works, but it doesn't scale and it reintroduces risk into sections that were already reviewed.
Keep translatable strings out of your code blocks
If your docs are Markdown/MDX with embedded code fences, you need a convention that strictly separates translatable prose from non-translatable code. Most localisation tools (and humans) will happily try to "helpfully" translate a JSON key if you don't fence it off clearly.
<!-- GOOD: comment is translatable, code stays untouched -->
json
{
"access_token": "ya29.a0AfH6...", // short-lived token, expires in 1h
"token_type": "Bearer"
}
markdown
A few rules worth enforcing with a linter or pre-commit hook:
- Code fences (
```) are never sent to translation as a whole block. Only inline comments inside them are candidates, and only if your team has explicitly approved translating comments. - JSON field names, HTTP headers, and status codes are never translated, full stop. This is a technical convention most API consumers expect, even in localised docs.
- Markdown front matter (YAML metadata) needs its own extraction rule, since it often contains both translatable text (
title:) and non-translatable config (slug:,api_version:).
Tools like gettext, or frameworks with first-class i18n support (Docusaurus i18n, Next.js with next-intl), handle this separation well if you structure content correctly from the start. Retrofitting it onto a repo that mixes everything into prose paragraphs is painful.
Structuring resource files vs. prose content
API portals typically have two very different kinds of translatable content, and they shouldn't go through the same pipeline:
UI strings (buttons, tooltips, error banners) belong in structured resource files:
// en/portal.json
{
"button.tryItOut": "Try it out",
"error.rateLimited": "You've hit the rate limit. Try again in {seconds}s."
}
// fr/portal.json
{
"button.tryItOut": "Essayer",
"error.rateLimited": "Limite de requΓͺtes atteinte. RΓ©essayez dans {seconds}s."
}
These are small, stable, and rarely change β a good candidate for full human review since a bad translation here shows up directly in the product UI.
Long-form docs (guides, quickstarts, endpoint descriptions) live in Markdown and change constantly. These need the diff-based workflow above, plus a translation memory (TM) so repeated phrases ("Returns a 401 if the token is invalid") don't get retranslated β and potentially re-worded β every single time they appear on a new page.
A minimal glossary enforcement script
Terminology drift is the real failure mode, not bad grammar. If "rate limit" gets translated three different ways across your docs, that's worse than leaving it in English everywhere. A cheap way to catch this in CI before it ships:
import re
import sys
GLOSSARY = {
"limite de dΓ©bit": "limite de requΓͺtes", # banned -> approved
"jeton d'accès": "token d'accès",
}
def check_file(path):
with open(path, encoding="utf-8") as f:
text = f.read()
errors = []
for banned, approved in GLOSSARY.items():
if re.search(banned, text, re.IGNORECASE):
errors.append(f"{path}: found '{banned}', use '{approved}' instead")
return errors
if __name__ == "__main__":
all_errors = []
for path in sys.argv[1:]:
all_errors.extend(check_file(path))
if all_errors:
print("\n".join(all_errors))
sys.exit(1)
Run this as a pre-merge check on any translated Markdown file. It won't catch everything, but it stops the most common regressions: a translator or MT engine reintroducing a term that was already standardized months ago.
Automated vs. human review, mapped to risk
Not every page deserves the same review budget, and this maps surprisingly well to standard software risk triage:
- Auto-generated reference pages (hundreds of near-identical endpoint descriptions from your OpenAPI spec): machine translation with spot checks is usually fine. The repetitive structure means errors are easy to catch with the glossary script above.
- Guides that directly affect integration success (auth flows, webhooks setup, error handling): these need a human reviewing full text, because a misunderstood paragraph here generates support tickets, not just an awkward sentence.
- Anything customer-facing in a sales context (docs an enterprise prospect reads before signing): treat it like marketing copy β full review pass, consistent tone, the works.
Testing localised docs like you'd test a UI
Once translated content is merged, don't assume it's done. German and Finnish strings run 20-35% longer than English on average, and that breaks fixed-width code sample containers, nav sidebars, and button layouts. Run your localised build through the same visual regression tests you'd use for any UI change:
# Example with Percy or a similar visual diff tool
npx percy exec -- npm run build:docs -- --locale=de
If you don't have visual regression testing set up for your docs site, this alone is worth adding before you scale to a third or fourth language.
Wrapping up
Localising API docs isn't a translation problem bolted onto a docs problem β it's a pipeline problem. The teams that get it right treat translated content the same way they treat code: versioned, diffed, linted, and tested before it ships. The source article does a good job covering the terminology and tiering side of this; the engineering side is just as much work, and it's the part that determines whether your localised docs stay correct past the first release.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes β full credit and traffic to the original publisher.