Your Django app is translated. Technically.
makemessages runs. compilemessages passes. The locale folders exist. Then someone opens the billing page in another language and sees a term like ARR translated as if it were ordinary prose, or a phrase like churn rendered as the wrong kind of loss entirely. The app still works, but the meaning is broken.
That’s the annoying part of business jargon translation. Most i18n setups handle language files. They don’t handle domain vocabulary. If your product has subscriptions, finance screens, admin dashboards, customer support flows, or B2B copy, you already have strings that need more than literal translation.
The fix is not “avoid jargon” and it’s not “trust generic AI more.” The fix is to treat terminology as part of the codebase. Version it. Review it. Feed it into your translation workflow the same way you feed code and tests into release automation.
The Hidden Cost of Bad Jargon Translation
You’ve probably seen this bug before. A feature ships fine in English. Then a non-English user opens the app and gets wording that is grammatically correct but operationally wrong.
Maybe the invoice page uses MRR and the translator expands it badly. Maybe support docs translate churn like a physical spinning action instead of customer turnover. Maybe a sales dashboard uses “move the needle” and the translated copy sounds like nonsense. None of these break Python. They break trust.

For a Django team, that makes jargon translation a product bug, not a copy issue. Bad terms leak into templates, emails, error messages, admin labels, and onboarding flows. Support gets tickets. PMs rewrite strings by hand. Developers start adding weird translator comments after the damage is already done.
A 2023 Worklife survey found that 60% of workers globally had to self-decode workplace jargon, rising to 75% among non-native speakers in tech hubs. The same source says 22% of open-source i18n bugs stem from untranslated jargon in docs. That tracks with what many teams see in practice. The gettext pipeline works, but the vocabulary layer is missing.
Why this hurts more in software
Business jargon rarely appears in isolation. It sits inside real product surfaces:
- Billing pages: ARR, MRR, renewal, downgrade, seat, prorated
- Support emails: SLA, escalation, incident, account owner
- Admin UI: churn risk, conversion funnel, trial expiration
- Internal docs: approved product names, legal wording, pricing tiers
If one term drifts, related strings drift with it.
Treat mistranslated jargon like corrupted data. The text renders, but the meaning no longer matches the business logic.
Where manual portals fall down
Manual translation portals help with workflow. They don’t solve terminology by themselves. Copy-pasting .po entries into a generic model is worse. The translator sees fragments, not context. Your term choices end up living in chat logs, someone’s memory, or an old spreadsheet nobody trusts.
That’s why this problem keeps returning after every release.
Why Generic AI Translation Mangles Your Terms
Generic AI translation fails for the same reason generic code generation fails on domain-heavy code. It guesses from broad patterns when your app needs narrow meaning.
The problem is not that the model is “bad at languages.” The problem is that business jargon is packed with ambiguity.
One word, three meanings
Take a simple word like key.
In a Django project, key can mean:
| Context | Meaning |
|---|---|
settings.py |
a secret or API credential |
| database schema | a primary or foreign key |
| UI copy | a physical key or an important thing |
A generic translator sees one token. You see three different concepts. The same thing happens with account, seat, owner, pipeline, tier, margin, close, and conversion.
Business jargon adds another layer because many terms are metaphorical. The OECD Glossary of Statistical Terms contains over 6,700 definitions, which tells you how dense specialized vocabulary gets even before you add product-specific language. Terms like EBITDA, churn, and pivot need context. Idioms don’t translate word for word, and English business language is full of them.
Fragmented strings make the model dumber
Django apps often present translators with bad units of meaning:
msgid "Move the needle"msgid "Churn"msgid "Owner"msgid "Close date"
Those strings are tiny. The model doesn’t know whether it’s translating a CRM, an analytics product, an HR tool, or an accounting app. If you also split strings in templates or reuse labels in different screens, the ambiguity gets worse.
The result looks polished but wrong.
Generic translation is especially bad at local product language
Teams usually assume the dangerous strings are legal copy. In practice, the steady source of bugs is ordinary business language that became product vocabulary over time.
A few common failures:
- Acronyms get expanded inconsistently. One screen leaves
ARRas-is, another expands it badly, another translates only part of it. - Terms drift across files.
customer,account, anduserget treated as synonyms when the app treats them as different records. - Idioms survive where they shouldn’t. “Move the needle” or “circle back” lands in another language as awkward literal text.
- Financial wording gets softened. A subscription term becomes plain-language copy and loses legal or billing precision.
The core issue is uncontrolled terminology
A generic model does fine on plain sentences. It breaks on uncontrolled term sets. The core issue is uncontrolled terminology.
That’s why teams get better results when they stop treating every string as free text. Business jargon translation works when you constrain the translator with approved terms, usage notes, and protected phrases. Without that, the model is just autocomplete with confidence.
If a term has product meaning, don’t ask the model to infer it from a five-word
msgid.
The Glossary First Approach A TRANSLATING.md File
The best fix I’ve seen is boring. Put your terminology in Git and make it part of the repo.
Not in a spreadsheet. Not in a portal comment. Not in Slack. Use a TRANSLATING.md file at the project root and treat it like an interface contract for language.

Why Markdown works better than a portal
Developers already trust files in version control. A Markdown glossary is:
- Diffable: term changes show up in PRs
- Reviewable: product, support, and engineering can comment on exact wording
- Local: no portal tab hunting
- Portable: works with any CLI workflow
That matters because terminology is not static. Product names change. Pricing language changes. Compliance wording gets tightened. If the approved translation for a term changes, you want a commit history, not a mystery.
A 2023 translation survey found that 29% of companies have no formal terminology management, and that this leads to 20-30% higher localization maintenance costs. The same source reports 80-92% post-edit acceptance for business content in glossary-based workflows, compared with 50-65% for generic machine translation. That gap is exactly why a glossary-first workflow pays off.
What to put in TRANSLATING.md
Keep it simple. You don’t need a giant linguistic database. You need the terms that cause real defects.
Start with entries like these:
# TRANSLATING.md
## Do not translate
- TranslateBot
- SuperWidget
- Django
- PostgreSQL
## Approved business terms
- ARR = Annual Recurring Revenue
- MRR = Monthly Recurring Revenue
- churn = customer or employee turnover, not physical spinning
- account owner = the responsible staff member for an account
- seat = licensed user slot in a subscription plan
## Product-specific distinctions
- user = authenticated end user
- customer = paying organization or buyer
- account = billing or workspace entity, not always the same as user
## UI and formatting rules
- Preserve placeholders like %(name)s, %s, {0}
- Preserve HTML tags and inline code
- Keep plan names exactly as written: Starter, Growth, Enterprise
What good entries look like
The useful pattern is term plus constraint.
| Weak entry | Better entry |
|---|---|
churn |
churn = customer or employee turnover, business metric |
owner |
account owner = responsible staff member, not legal owner |
Growth |
Growth = plan name, do not translate |
seat |
seat = licensed user slot in subscription billing |
A term without context still leaves room for guessing.
Keep it close to the code
The glossary should sit beside the code that creates the strings. If a developer adds a new billing concept, they update the same repo. If support says a translated term is confusing, the fix goes into Git and ships with the next release.
If you want a starter reference for what belongs in a terminology file, TranslateBot has a useful glossary of terms for localization work.
If a product term needs a Jira ticket when it changes, it also needs a glossary entry.
Automating Jargon Translation with TranslateBot
Once the glossary exists, the workflow gets much cleaner. You stop translating raw strings and start translating strings plus project rules.
Use the same path you already use for Django i18n. Extract messages. Update the glossary. Run the translator from the CLI. Review the diff.
Early in the process, it helps to visualize the flow.

The practical loop
Here’s the workflow I’d recommend for a small Django team.
- Extract new strings
- Add or update jargon rules
- Run translation from the repo
- Review the diff
- Compile and test
That sounds obvious, but the important part is step 2. Many teams skip it and then blame the model.
A minimal Django example
Start with your normal extraction step:
python manage.py makemessages -a
Then update TRANSLATING.md if the new strings introduced product or business terms:
## New billing terms
- expansion revenue = revenue from existing customers upgrading
- downgrade = move to a lower-priced plan
- renewal date = subscription renewal date, billing context
- trial conversion = when a trial becomes a paid subscription
Now run translation from the CLI:
translatebot locale/ --glossary TRANSLATING.md
Then compile messages:
python manage.py compilemessages
What changes in the .po file
A typical bad input looks like this:
#: billing/templates/billing/summary.html:18
msgid "ARR"
msgstr ""
#: billing/templates/billing/summary.html:22
msgid "Churn rate"
msgstr ""
#: billing/templates/billing/summary.html:30
#, python-format
msgid "Your %(plan)s plan renews on %(date)s."
msgstr ""
After a glossary-aware run, you want output that preserves both terminology and formatting:
#: billing/templates/billing/summary.html:18
msgid "ARR"
msgstr "ARR"
#: billing/templates/billing/summary.html:22
msgid "Churn rate"
msgstr "Taux d’attrition"
#: billing/templates/billing/summary.html:30
#, python-format
msgid "Your %(plan)s plan renews on %(date)s."
msgstr "Votre forfait %(plan)s se renouvelle le %(date)s."
The point is not that every language should keep ARR unchanged. The point is consistency. If your glossary says the acronym stays, it stays. If it says expand or annotate, the model follows that rule.
Why placeholder preservation matters
Business jargon often shows up inside formatted strings, templates, and HTML-heavy UI text. If the translator damages placeholders, you don’t get a wording bug. You get a runtime or rendering bug.
Redokun’s translation statistics summary says tools that preserve placeholders like %(name)s and HTML tags reduce functional breakage in localized web apps by over 70%. For Django projects, that’s not a nice extra. It’s required.
A safe workflow should preserve things like:
%(name)s%s{0}<strong>...</strong>{% blocktrans %}variable slots
Use context where Django already supports it
If a string is ambiguous, give translators a better source string or explicit context.
For example:
from django.utils.translation import pgettext_lazy
status_label = pgettext_lazy("billing metric", "Churn")
key_label = pgettext_lazy("security credential", "Key")
That won’t replace the glossary, but it reduces ambiguity before translation even starts.
For model fields and admin labels, TranslateBot’s docs on model translation workflows are worth reading if your terms are generated from models rather than only templates.
Review the diff, not a portal screen
The big win of a CLI workflow is that your review happens in Git.
You can inspect exact string changes:
git diff locale/fr/LC_MESSAGES/django.po
That catches bad term choices fast. It also lets non-developers review wording in a normal PR without needing access to a SaaS portal.
A short review checklist works better than a giant language QA process:
- Check protected terms: plan names, brand names, product names
- Check billing language: revenue, renewal, upgrade, downgrade
- Check placeholders: no broken
%()or HTML tags - Check repeated terms: the same concept should map to the same word everywhere
Here’s a short walkthrough if you want to see the workflow in motion:
If your translation review doesn’t happen in the same PR as the code change, your terminology will drift.
Handling Jargon Edge Cases and Protected Terms
The easy cases are single words with clear definitions. Real projects have uglier strings.
Jargon gets embedded inside placeholders. Brand names sit next to translatable labels. One term should be translated in docs but never in UI chrome. That’s where many teams fall back to manual edits and start accumulating silent inconsistency.

Protected terms should be explicit
If a term must never be translated, say so directly in the glossary.
Use a section like this:
## Never translate
- TranslateBot
- SuperWidget
- Starter
- Growth
- Enterprise
- Django Admin
Do not rely on capitalization to protect names. Translators and models will still “help.”
Terms inside format strings
Here, business jargon translation becomes operational. A string like this is normal in Django:
{% blocktrans with percent=usage_percent %}
Your ARR increased by {{ percent }}%
{% endblocktrans %}
The variable is not the problem. The term is.
Handle it with a glossary note that binds the term to the business meaning:
- ARR = Annual Recurring Revenue, subscription metric
- "increased by" in billing context = revenue growth wording
- Preserve template variables and percent formatting
Then verify the output manually in the .po file:
msgid "Your ARR increased by %(percent)s%%"
msgstr "Votre ARR a augmenté de %(percent)s%%"
If the placeholder moves or disappears, reject the change.
Similar terms that are not synonyms
A lot of localization mess comes from teams collapsing distinct concepts:
usercustomermemberaccountorganization
In English product copy, people often use these loosely. Your database does not.
A useful rule is to define these like schema entities:
## Domain entities
- user = authenticated individual
- member = user within an organization
- customer = paying company or buyer
- account = billing or workspace container
- owner = role with account responsibility, not generic possession
That one block prevents a huge amount of drift.
Handle comments and docs too
Business jargon bugs are not limited to UI strings. They also show up in:
- translator comments
- README files
- onboarding docs
- support macros
- release notes
If those artifacts describe product concepts differently from the app, your team ends up debugging language instead of code. Keep the glossary as the single source of truth for both .po files and supporting docs.
A small rule set beats clever prompting
For edge cases, teams often try to fix the problem with longer prompts. That helps a bit. A clear glossary plus protected-term rules helps more.
Use rules like these:
- Never translate plan names
- Preserve placeholders exactly
- Translate the concept, not the idiom
- Use one approved term per product concept
- Reject output that changes formatting tokens
That gives you something enforceable. Prompt-only workflows tend to drift because nobody can review them line by line later.
Maintaining Consistency with QA and CI CD
A good business jargon translation workflow is not a one-time cleanup. It needs to survive new features, rushed releases, and new developers joining the repo.
That’s where QA and CI matter. If terminology only lives in a human review step, it will decay.
Make terminology changes visible
Every term change should produce a normal diff:
TRANSLATING.mdchanged- one or more
.pofiles changed - reviewers can inspect both in the same PR
That gives you reproducible jargon diffs, which matter more than teams admit. A cited summary of future-facing claims says a Harvard Business Review study found jargon overuse significantly costs firms in miscommunication and that engineering teams lose substantial time weekly. The same source also says 52% of DevOps teams cite a need for reproducible jargon diffs in CI/CD in the context of clear multilingual communication requirements (straightnorth.com PDF citing those claims). Even if you ignore the bigger industry framing, the practical point holds: if you can’t diff terminology, you can’t control it.
Add checks to CI
A simple pipeline is enough:
name: i18n
on:
push:
branches: [main]
pull_request:
jobs:
translations:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pip install -r requirements.txt
- run: python manage.py makemessages -a
- run: translatebot locale/ --glossary TRANSLATING.md
- run: python manage.py compilemessages
- run: git diff --exit-code
That last line is useful. If extracting and translating messages changes tracked files unexpectedly, CI tells you.
If you want a ready-made starting point, the TranslateBot docs include a practical CI setup guide.
QA should focus on term integrity
Don’t build a giant review ceremony. Many teams won’t keep it up.
Use a small QA pass that checks:
| Check | Why it matters |
|---|---|
| Protected names unchanged | avoids brand drift |
| Core business terms consistent | avoids product confusion |
| Placeholders preserved | avoids broken UI and emails |
| New terms added to glossary | avoids repeat mistakes |
Onboarding gets easier too
New team members don’t need a history lesson from three departments. They read TRANSLATING.md, inspect past diffs, and follow the same workflow.
That is the chief payoff of treating translation like code. You get repeatability. You stop fixing the same vocabulary mistakes every quarter. And you stop pretending a portal or a clever prompt is enough for business language that already behaves like part of your domain model.
TranslateBot fits this workflow well because it stays inside the repo, reads a versioned TRANSLATING.md, writes directly to .po files, and keeps translation review in Git instead of a portal. If you’re tired of copy-pasting strings into generic tools or paying for a heavy SaaS stack just to translate Django locale files, try TranslateBot. It’s built for developers who want glossary-aware, CLI-first localization without leaving their editor.