Meta description: AI-translated Django .po files drift fast without a translation style guide. Put rules in TRANSLATING.md and wire them into CI.
You run your translation command, review the diff, and the problems jump out immediately. “Submit” comes back one way in a form, another way in a modal, and your product name gets translated in a settings screen where it should have stayed untouched. Placeholders survived, HTML survived, but the wording still feels like a grab bag.
That isn't a model problem first. It's a control problem.
AI systems are very good at producing plausible language. They are not good at guessing your preferred terminology, your formality rules, or whether “Billing” is a navigation label, a legal concept, or a product area. If you feed them raw strings with no policy, you get fluent inconsistency. For Django teams, that shows up as noisy .po diffs, review churn, and translators or PMs reopening the same terminology issue every release.
If you write UI copy with localization in mind, Dokly's guide on writing for translation is a useful companion to this workflow. It helps upstream, before strings ever reach your locale files. For the downstream part, where generated translations need consistent rules, a style guide has to live in the repo and travel with the code. If you need a quick primer on the machine translation side, this overview of machine translation is a decent refresher.
Your AI Translations Are Inconsistent for a Reason
A lot of teams start here:
django-admin makemessages -l es
python manage.py translate --locale es
django-admin compilemessages
The command works. The translations are readable. Then your reviewer flags the same issue across half the file.
#: templates/billing/checkout.html:18
msgid "Submit"
msgstr "Enviar"
#: templates/reports/export.html:42
msgid "Submit"
msgstr "Presentar"
#: templates/navigation.html:12
msgid "Billing"
msgstr "Facturación"
#: templates/product/sidebar.html:7
msgid "Billing"
msgstr "Cobros"
Neither output is obviously broken in isolation. The problem is that your app isn't isolated. Users move across screens, and inconsistent labels make the product feel sloppy even when each string is technically valid.
Why prompts alone don't fix it
You can keep tuning prompts. You can add “be consistent” and “preserve brand voice” and “don't translate product names.” That helps for a while. Then a new feature lands, a different locale gets added, or a different reviewer has a different opinion about tone.
Practical rule: If a translation decision matters enough to argue about twice, it belongs in a version-controlled guide, not in someone's memory.
The root issue is missing context. A model won't infer your internal naming conventions from a bare msgid. It also won't reliably guess locale-specific formatting rules, such as dates, numbers, currency, abbreviations, or the level of plain-language phrasing your app needs.
What a Translation Style Guide Does for Code
For developers, a translation style guide should be treated like .editorconfig, Ruff config, or a formatter rule set. It exists to reduce variance. You are constraining output so the same input conditions lead to the same kinds of decisions across locales and releases.
Public guidance already frames the document this way, even if it isn't written for engineers. The City of Philadelphia's translation style guide explicitly defines rules for dates, numbers, currency, abbreviations, and grammar, and it also requires a “Plain Speech” register with everyday vocabulary and grammar in translated public content. It even calls out writing dates with a month name or standard abbreviation to avoid ambiguity in languages that commonly use day-month order, which is exactly the kind of thing that prevents subtle production bugs and user confusion in localized interfaces (Philadelphia translation style guide).

Think of it as translation configuration
A style guide for code should answer the questions your translation engine can't answer from source strings alone:
- Terminology: Which product terms are fixed?
- Voice: Are you formal, conversational, terse, or neutral?
- Formatting: How should dates, currencies, and abbreviations appear?
- Exceptions: Which terms stay in English?
- Locale split: Does
es_ESdiffer fromes_MXin tone or vocabulary?
Without that, every translation pass is a partial re-negotiation of your language policy.
What it changes in review
Once you define the rules, translation review gets narrower and faster. Reviewers stop debating style from scratch and start checking compliance against an agreed artifact.
That's the useful reframing. A translation style guide is not an editorial appendix. It's part of your localization system. If you're shipping software in multiple locales, it belongs next to your glossary, your locale/ directory, and your release process.
The Anatomy of a Developer-First Style Guide
Most style guides fail because they read like brand documentation. Your translators and your automation need something narrower. Keep the file focused on output-affecting rules, and cut everything that doesn't change a translated string.

A high-quality guide needs to define company-specific terminology and formatting constraints, including capitalization, voice, jargon, and terms that must not be translated. When translators and AI systems get that information, they can preserve brand meaning. When they don't, teams get inconsistent terminology and slower approval cycles (Blend on translation style guides and glossaries).
The parts that actually matter
Here's the structure that tends to hold up in real repos.
| Component | Why It Matters for AI | Example Rule |
|---|---|---|
| Terminology glossary | Prevents drift on product and domain terms | “Workspace” is always translated as approved locale term. Never use alternatives. |
| Do-not-translate list | Protects brand names and feature names | “TranslateBot”, “Django”, and plan names remain in English. |
| Tone and formality | Stops random shifts between formal and informal language | Use informal second-person voice in app UI for this locale. |
| Placeholder handling | Prevents broken runtime strings | Preserve %(name)s, %s, and {0} exactly. Do not reorder unless grammar requires it and the format supports it. |
| HTML and Markdown rules | Avoids output that breaks templates or rendering | Keep tags intact. Translate only visible text. |
| Date, time, number, currency rules | Removes formatting ambiguity | Use locale-standard formatting. Avoid ambiguous numeric-only dates in user-facing copy. |
| Capitalization and punctuation | Keeps UI labels uniform | Sentence case for buttons and headings unless the source term is a branded title. |
| Context notes by content type | Gives short strings meaning | In navigation, “Billing” refers to the account area, not invoice processing. |
| Inclusive language rules | Avoids phrasing that feels dated or exclusionary | Prefer clear, direct wording. Avoid terms the team has marked as exclusionary. |
| Locale exceptions | Handles regional variants cleanly | es_ES and es_MX use separate terminology for payment-related labels. |
Keep rules atomic
Don't write paragraphs like “be friendly, professional, and on-brand while respecting local expectations.” That reads fine and translates badly into action.
Write one rule per decision point. Use examples. If you need a good glossary shape for that section, this glossary format reference is the kind of thing worth mirroring inside your repo.
Use rules that can survive code review. If a reviewer can't point to a line in
TRANSLATING.md, the policy probably isn't specific enough yet.
Split by locale when needed
A generic “Spanish” guide won't carry you very far if your product spans multiple markets. The same applies to Portuguese, French, and other languages with meaningful regional variation. If your app has both marketing copy and terse operational UI, split by content type too. A support article and a destructive action button should not share the same voice rules.
A Practical Template for Your TRANSLATING.md
Put the file at the repo root and keep it close to README.md and CONTRIBUTING.md. That placement matters. Developers will find it, reviewers will link it in PR comments, and CI jobs can load it without special handling.
Guidance for AI and human translators works better when it is language-specific and written as detailed, single-topic rules with examples. Concise headings and bullet-point rules reduce ambiguity, while broad prose increases inconsistency (Lokalise on creating style guides for translators and AI).
A usable template
Copy this and cut whatever doesn't affect output quality.
# TRANSLATING.md
## Scope
- Applies to Django `.po` files in `locale/*/LC_MESSAGES/django.po`.
- Applies to model-field translations if generated from the same source content.
- Source language is English.
## Target locales
- `es_ES`
- `es_MX`
- `fr`
- `de`
## Global rules
- Preserve placeholders exactly: `%(name)s`, `%s`, `{0}`, `%(count)d`.
- Preserve HTML tags and attributes. Translate only user-visible text.
- Keep Markdown structure intact.
- Do not translate code identifiers, setting names, environment variable names, or Python paths.
- If a string is ambiguous, prefer the product UI meaning over the dictionary meaning.
## Do not translate
- TranslateBot
- Django
- Workspace
- Pro
- Starter
- API
- CSV
## Approved terminology
### English to Spanish (Spain)
| Source term | Approved translation | Notes |
|---|---|---|
| Billing | Facturación | In nav and account settings |
| Workspace | Espacio de trabajo | Never shorten |
| Sign in | Iniciar sesión | Do not use alternatives |
| Dashboard | Panel | Use in product UI |
### English to Spanish (Mexico)
| Source term | Approved translation | Notes |
|---|---|---|
| Billing | Facturación | Same as `es_ES` |
| Workspace | Espacio de trabajo | Same as `es_ES` |
| Sign in | Iniciar sesión | Same as `es_ES` |
| Dashboard | Panel | Same as `es_ES` |
## Tone and voice
### `es_ES`
- Use informal second person where natural for product UI.
- Prefer clear, direct sentences.
- Avoid literal translation if it sounds stiff.
### `de`
- Use the chosen formality consistently across the app.
- Keep buttons short.
- Prefer direct verbs over noun-heavy phrasing.
## Formatting
- Avoid ambiguous numeric-only dates in user-facing strings.
- Follow locale conventions for currency and numbers.
- Use sentence case for UI labels unless source text is a proper noun.
- Keep punctuation minimal in buttons and menu items.
## Inclusive language
- Prefer neutral, direct wording.
- Avoid phrasing marked internally as exclusionary or deficit-based.
- If grammar forces a choice, pick the form already approved by locale reviewers.
## Content-specific rules
### Buttons
- Keep labels short.
- Prefer verbs.
- Avoid adding politeness filler.
### Errors
- State the problem first.
- Then state the next action.
- Do not invent technical detail not present in the source string.
### Emails
- Slightly warmer tone than product UI.
- Preserve legal or billing terminology exactly as approved.
## Examples
### Good
- "Sign in" -> approved locale term for authentication action
- "Billing" in navigation -> approved locale term for account area
- "Try again" -> short verb phrase, not a long sentence
### Bad
- Translating a product name literally
- Using two different translations for the same nav item
- Rewriting placeholders or HTML
## Review rules
- If a new product term appears, update this file in the same PR.
- If a reviewer requests a repeated wording change, add it here as a rule.
- Locale-specific overrides beat global guidance.
Good rules and bad rules
Bad instruction:
- Be friendly and natural.
Better instruction:
- Use informal second-person voice in
es_ESproduct UI. - Keep button labels under the shortest natural phrasing for the locale.
- Do not translate “Workspace.”
Bad instruction:
- Follow brand voice.
Better instruction:
- In errors, state the problem first and the next action second.
- In billing screens, use the approved translation for “Billing” everywhere.
Why Markdown works better than a slide deck
Markdown is diffable. That's the whole point.
When a PM changes a product term, you want a normal code review trail. When a reviewer finds a recurring issue, you want one small commit that updates the rule and future runs. A shared doc outside the repo usually turns into tribal knowledge plus stale comments.
One practical way to apply this is to have your translation tool read TRANSLATING.md as context on each run. TranslateBot, for example, can use a repo-level guide to steer .po translation output while preserving placeholders and HTML. The important part isn't the specific tool. It's the pattern. The guide has to be consumed automatically, not just written once and forgotten.
Common Pitfalls and How to Avoid Them
Most style guides fail for operational reasons, not linguistic ones. Teams write them, then leave them disconnected from the work.
A recurring problem in existing guidance is that it tells you what belongs in a style guide but not how to encode it in a version-controlled workflow with review checkpoints, which is the exact problem engineering teams care about most (Oregon translation style guide).

The common breakpoints
- The guide lives in a random doc tool. Put it in the repo as
TRANSLATING.md. - Rules are vague. Write them like linter rules, not brand aspirations.
- It's too long. Keep only decisions that change output quality.
- No one updates it. Require updates when new product terms land.
- Reviewers ignore it. Add a PR checklist item for terminology and locale rules.
Inclusive language is usually missing
This shows up later than terminology drift, but it matters. A team can be perfectly consistent and still choose phrasing that feels dated, exclusionary, or too literal in a target locale.
If your team is also tuning local prompts outside the main translation pipeline, the discipline is similar to optimizing offline AI prompts. Short, explicit instructions beat abstract prose, especially when multiple people need repeatable output.
A style guide that never changes becomes fiction. Your product vocabulary changes. Your markets change. The file has to change too.
One-sentence fixes that hold up
| Pitfall | Fix |
|---|---|
| Unread document | Store it in the repo root and reference it in translation commands |
| Abstract wording | Replace adjectives with enforceable examples |
| Stale terminology | Update the guide in the same PR as the feature |
| Missing locale nuance | Add per-locale override sections |
| No enforcement | Fail CI on untranslated terms, forbidden translations, or missing guide updates |
Wiring Your Style Guide into CI/CD
Once the guide exists, stop treating it as documentation and start treating it as input. In software localization, style guides increasingly act as living references tied to translation memory, glossaries, and automated review cycles so terminology stays consistent across releases (Phrase on using style guides in localization).

A decent CI/CD pattern is boring on purpose:
- Run
makemessageswhen strings change. - Load
TRANSLATING.mdduring translation. - Write translations back to
locale/<lang>/LC_MESSAGES/django.po. - Run checks for forbidden term drift.
- Review the diff like code.
- Compile messages before release.
If you want a broader pipeline view beyond localization, CloudCops DevOps pipeline insights are useful background on where these checks fit.
Here's the shape of the workflow:
name: translations
on:
pull_request:
paths:
- "**/*.py"
- "**/*.html"
- "locale/**"
- "TRANSLATING.md"
jobs:
i18n:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install dependencies
run: pip install -r requirements.txt
- name: Extract messages
run: django-admin makemessages -l es -l fr
- name: Translate new strings
run: python manage.py translate --locale es --locale fr
- name: Compile messages
run: django-admin compilemessages
Here's a short walkthrough of the workflow in video form:
Your PR template should also ask two things:
- Did you add any new product terms that need glossary entries?
- Did translated output change because
TRANSLATING.mdchanged?
For review, keep a small set of translation tests around placeholders, protected terms, and known edge cases. If you need examples of what to validate, these translation test examples are a good starting point.
Before your next deploy, create TRANSLATING.md, commit one locale-specific glossary, and make your translation job read it on every run. That one file does more for consistency than another round of prompt tweaking.
If you want to keep this inside Django instead of pushing strings through a TMS, TranslateBot is one option. It runs as a manage.py translate command, works with .po files and model fields, and uses a repo-level TRANSLATING.md file to guide AI output while preserving placeholders and HTML so your diffs stay reviewable in Git.