Don't Let i18n Become an International Incident
You ship a new feature. Your Django app now speaks five languages. CI is green, compilemessages passed, and the screenshots in staging looked fine.
Then support gets a complaint from German users. Not a traceback. Not a broken placeholder. Your onboarding copy used an idiom that reads as insulting in their context. The translation was technically correct. The release still failed.
That’s the part many teams miss. Internationalization is not just string extraction and .po files. It’s also risk management for culture, politics, religion, race, and historical baggage. A clean gettext workflow won’t save you if the source string is bad, the context is missing, or an automated system ships labels nobody reviewed.
The expensive failures usually don’t start as “ethics issues.” They start as normal product decisions. A map color. A slogan. An image tag. A product name. A recommendation label. Then they turn into a user trust problem because nobody treated cultural review as part of engineering.
There are very public cultural insensitivity examples that prove the point. Microsoft got banned in India after a Kashmir map-coloring mistake and had to recall all 200,000 copies of Windows 95 there, at a cost of millions of dollars, according to Commisceo’s write-up of costly cultural sensitivity failures. That wasn’t a translation typo. It was a localization failure with geopolitical impact.
If you build multilingual Django apps, the lesson is practical. Put guardrails in the same place you already put tests, linters, and code review. Add context to msgids. Separate risky copy from routine UI strings. Keep a real glossary in TRANSLATING.md. Block bad terms before they reach translators or models.
Here are ten cultural insensitivity examples, and the engineering fixes that would have caught them earlier.
1. Airbnb's Racial Discrimination Listings (2016)
Airbnb’s issue wasn’t a mistranslated headline. It was platform behavior. Hosts rejected some guests based on racial signals in names and profiles, which exposed a design and moderation gap.
For Django teams, that matters because bias often shows up before translation. It starts in user-generated content, profile metadata, search ranking, and trust signals. If your app translates listing descriptions and profile text into multiple languages, you can spread the problem farther and faster.
Where the workflow usually breaks
Teams often focus on translating user policies while ignoring the product surfaces where discrimination appears. That’s backwards. A nice translated Terms of Service won’t offset biased listings or weak moderation tools.
A better pattern is to treat moderation language as product infrastructure. Put your anti-discrimination rules in the same workflow as your app copy, and add context for translators so legal and cultural nuance survives.
Here’s a practical Django setup for keyword auditing:
# app/management/commands/audit_listings.py
from django.core.management.base import BaseCommand
from listings.models import Listing
FLAGGED_TERMS = [
"whites only",
"no foreigners",
"locals only",
]
class Command(BaseCommand):
help = "Scan listings for flagged discriminatory language"
def handle(self, *args, **options):
for listing in Listing.objects.all().iterator():
text = f"{listing.title} {listing.description}".lower()
hits = [term for term in FLAGGED_TERMS if term in text]
if hits:
self.stdout.write(
self.style.WARNING(f"Listing {listing.pk} flagged: {hits}")
)
That won’t solve systemic bias. It does catch obvious garbage before it becomes normalized.
What works better
Use translator comments for policy strings that carry legal meaning:
#. Anti-discrimination policy shown during host signup.
#. "Discrimination" includes race, religion, caste, ethnicity, and nationality.
msgid "Discrimination is prohibited on this platform."
msgstr ""
Then keep hard rules in TRANSLATING.md:
## Moderation and policy terminology
- Never soften terms related to discrimination.
- Do not translate slurs word for word unless required for reporting or policy context.
- Prefer established local legal or policy wording for protected groups.
- Flag any phrase that implies exclusion by race, religion, caste, or nationality.
If your app allows user-generated text, don’t treat localization as a final polish step. Treat it as one layer in a moderation pipeline.
One more reason to care. A survey of 1,200 individuals of South Asian descent in the United States found that 26% reported physical assault and 59% reported offensive jokes or remarks due to caste, according to the Asian Pacific Institute on Gender-Based Violence PDF on bias, discrimination, and cultural sensitivity. If your platform translates community guidelines or profile text without cultural review, you can easily miss how exclusionary language lands in diaspora communities.
2. Google's Gorilla Image Recognition Misclassification (2015)
Google Photos labeling Black people as “gorillas” is one of the clearest examples of what happens when AI output reaches users without enough review.
The immediate lesson for Django developers is simple. Don’t pipe raw model labels straight into templates. If the label touches identity, appearance, religion, ethnicity, or body traits, put a human or a hard rule in the loop.

The bad pattern
I still see teams do this:
label = ai_service.classify(image)
context["alt_label"] = _(label)
That’s bad twice. First, the label may be wrong or offensive. Second, you’re sending the bad label through your translation system, which gives it extra legitimacy.
A safer design stages predictions first.
# models.py
class ImageTagPrediction(models.Model):
image = models.ForeignKey("Image", on_delete=models.CASCADE)
raw_label = models.CharField(max_length=200)
confidence = models.FloatField()
reviewed = models.BooleanField(default=False)
approved_label = models.CharField(max_length=200, blank=True)
Then only render approved labels:
approved_tags = image.imagetagprediction_set.filter(reviewed=True).exclude(approved_label="")
Safer locale handling
For sensitive features, disable them by locale until you trust the pipeline.
# settings.py
AI_TAGGING_DISABLED_LOCALES = {"de", "ja", "fr"}
from django.conf import settings
if request.LANGUAGE_CODE in settings.AI_TAGGING_DISABLED_LOCALES:
show_tags = False
That kind of feature flag is boring. Good. Boring is what you want here.
Use your glossary to reject unsafe terms before translation:
## Blocked AI labels
Never output or translate labels related to animal comparisons for humans.
Flag labels tied to race, skin tone, ethnicity, or disability for manual review.
You don’t need a perfect fairness framework to make progress. You need friction in the right places. Raw model output should be treated like untrusted input, because it is.
3. Pepsi's Kendall Jenner Protest Ad (2017)
Pepsi took protest imagery, shaved off the politics, and turned it into a soft-drink ad. People hated it because it trivialized real social conflict.
Marketing teams often create this mess in English first, then localization teams inherit it later. At that point, translation can’t fix the core issue. It can only spread it to more markets.

Keep campaign copy out of the generic string bucket
A lot of teams dump everything into one gettext domain. UI labels, legal text, ad copy, email subject lines. Then they wonder why risky strings get the same review as “Save changes”.
Use pgettext to separate high-stakes copy.
from django.utils.translation import pgettext
headline = pgettext("marketing_campaign", "Join the movement")
button = pgettext("ui_button", "Continue")
That lets you apply stricter review rules to campaign language without slowing down every button label.
If you’re using machine translation or LLMs, be extra strict with copy that borrows from activism, grief, religion, or conflict. Neural machine translation is useful, but it won’t tell you a slogan is tone-deaf. It only predicts plausible language.
Add no-go zones to your glossary
Most style guides focus on brand voice. Good, but incomplete. You also need explicit “don’t say this” rules.
## Campaign copy restrictions
- Do not frame protests, civil rights, war, or social unrest as playful or casual.
- Avoid metaphors that turn public suffering into product messaging.
- Escalate slogans that imply unity, resistance, or rebellion for native review.
Then label those strings in code comments or source references so reviewers know they aren’t ordinary UI copy.
A translator can make copy fluent. They can’t make a bad concept respectful.
For campaign messages, get a native reviewer from the target region, not just a translator. That person should answer a blunt question: “Would this feel insulting, trivializing, or politically loaded where you live?” If the answer is yes, rewrite the English source. Don’t ask the translation layer to perform magic.
4. H&M's Coolest Monkey in the Jungle Hoodie Campaign (2018)
H&M put a Black child in a hoodie with the phrase “Coolest Monkey in the Jungle.” The backlash was immediate because the phrase carried racist historical baggage that should have been obvious to anyone doing cultural review.
This is why product names and merchandising copy deserve more scrutiny than standard UI strings. They carry more context, more symbolism, and more risk.

Treat catalog content as high risk
A lot of ecommerce stacks send product text through the same pipeline as generic storefront copy. That’s lazy, and it breaks at scale.
Tag risky strings early:
from django.utils.translation import pgettext
product_name = pgettext("product_name_high_risk", "Coolest Monkey in the Jungle Hoodie")
Then create rules in CI that fail builds when blocked source terms appear.
# scripts/check_blocked_terms.py
import sys
from pathlib import Path
BLOCKED = ["monkey"]
content = Path("locale/en/LC_MESSAGES/django.po").read_text(encoding="utf-8").lower()
for term in BLOCKED:
if f'msgid "' in content and term in content:
print(f"Blocked term detected: {term}")
sys.exit(1)
That’s crude. It still catches things before they ship.
Localization starts before translation
The problem here was not “bad localization.” The problem was bad source material. That distinction matters. If the English string is already culturally toxic, translating it perfectly just creates a multilingual version of the same mistake.
That’s why teams need to understand the difference between adapting a product for global use and translating text inside it. If you want a clean mental model, this explanation of localization vs internationalization is the right split. Internationalization prepares the system. Localization decides what should be said in each market.
Use your glossary to force approved replacements instead of literal translation:
## Product naming guardrails
- Never translate animal comparisons for children's clothing directly when the phrase could target a racial group.
- Replace blocked English source phrases with pre-approved local alternatives.
- Escalate any product name involving identity, body traits, or historical stereotypes.
If a term needs a debate, it should not auto-ship.
5. Facebook's Automatic Photo Tagging of Black Users as Primates (2021)
Facebook ran into the same class of failure again. Automated photo tagging associated Black users with primates. Different company, same root problem. Unsafe automation plus weak review.
The engineering lesson is not “never use AI.” It’s “never trust AI labels enough to skip product design.”
Build reporting into the feature
If users can see a tag, they need a fast way to reject it.
# models.py
class OffensiveTagReport(models.Model):
prediction = models.ForeignKey("ImageTagPrediction", on_delete=models.CASCADE)
language_code = models.CharField(max_length=12)
reason = models.CharField(max_length=100)
created_at = models.DateTimeField(auto_now_add=True)
Then put the action right next to the label, not hidden in a support form.
<form method="post" action="{% url 'report_tag' prediction.id %}">
{% csrf_token %}
<button type="submit">{% trans "Report incorrect or offensive tag" %}</button>
</form>
This part matters more than teams think. If the reporting flow itself is badly translated, users can’t tell you what went wrong.
Review confidence, not just content
Developers love thresholds because thresholds feel objective. Fine. Use them, but use them internally first.
A practical pattern:
- Store raw confidence: Keep the model score in the database.
- Hide low-confidence tags: Don’t display uncertain predictions to end users.
- Queue sensitive labels: Any tag involving people goes into review first.
That gives your moderators or ops team something concrete to inspect.
Confidence scores don’t prove a tag is safe. They only tell you how certain the model is about its own guess.
Also, don’t localize offensive model output by default. If a term belongs on a blocklist in English, it belongs on a blocklist before translation too. Translating a harmful label into twelve languages doesn’t reduce harm. It scales it.
6. Dolce & Gabbana's Blackface Makeup Campaign (2016)
Some localization failures are visual first, textual second. Dolce & Gabbana’s blackface campaign hit that category. Even if every caption, ad line, and product blurb were translated perfectly, the core asset was still offensive.
That’s why a text-only localization workflow is incomplete. Your translators need context, and your reviewers need access to the actual creative.
Put image selection under configuration
Hardcoding campaign images into templates is a trap. It makes regional substitution painful, which means teams keep the same visuals everywhere even when they shouldn’t.
A simple region-aware pattern:
# settings.py
CAMPAIGN_ASSETS = {
"default": {"hero_makeup": "/static/img/default/makeup.jpg"},
"jp": {"hero_makeup": "/static/img/jp/makeup.jpg"},
"fr": {"hero_makeup": "/static/img/fr/makeup.jpg"},
}
# context processor
from django.conf import settings
def campaign_assets(request):
lang = request.LANGUAGE_CODE.split("-")[0]
assets = settings.CAMPAIGN_ASSETS.get(lang, settings.CAMPAIGN_ASSETS["default"])
return {"campaign_assets": assets}
Now your template can swap visuals by locale without code edits.
Give translators visual context
A caption without the image is dangerous. Add comments in .po files that explain what the text sits next to.
#. Caption displayed over campaign image showing a model in theatrical makeup.
#. Review for racial, religious, and historical connotations.
msgid "Bold beauty without rules"
msgstr ""
That won’t catch every problem. It does stop translators from working blind.
Localization teams should review hero images, product photos, icons, and campaign art. Not just strings. If your process ignores visuals, you’re only reviewing half the message.
7. Spotify's Racist Playlist Recommendations (2019-2020)
Recommendation systems can look neutral while encoding ugly assumptions. Playlist naming, genre grouping, and “because you listened to” logic often end up clustering artists by race or identity instead of true similarity.
That’s not only a ranking issue. It becomes a localization issue once those categories and explanations show up in every language.
Taxonomy drift causes real damage
Genre labels are not universal. “Urban,” “world music,” and similar buckets have a long history of acting as lazy catch-alls. Once you translate those labels, the problem gets harder to see because each locale smooths over it differently.
Audit the output, not just the dictionary. If “rock” in one market mostly returns local artists while another market treats the same category as broad and mixed, your system may be reinforcing cultural silos rather than helping discovery.
A practical file for this is TRANSLATING.md:
## Music and content categories
- Keep genre labels specific.
- Avoid vague buckets that group artists by ethnicity, race, or region instead of style.
- Escalate labels such as "urban", "ethnic", or "world" for editorial review.
- Prefer established local genre names where they are more precise.
You also need human review for category names. Automated translation will happily preserve a bad taxonomy.
For teams tuning machine-generated labels and metadata, quality of translation is not just grammar or fluency. It includes whether the translated category stays fair, specific, and culturally legible.
One useful audit
Export recommendation outputs by locale and query. Then inspect for homogeneity.
- Check artist diversity: Don’t let broad genre searches collapse into one demographic or one region.
- Check label consistency: Make sure equivalent categories carry equivalent scope across languages.
- Check complaints: Treat artist and user reports as product signals, not PR noise.
Recommendation bias rarely announces itself in logs. You have to look for patterns on purpose.
8. Twitter's @RealDonaldTrump Account and Hate Speech Moderation (2016-2020)
Twitter’s moderation problems showed how inconsistent rule enforcement becomes when policy language, cultural context, and local speech patterns don’t line up.
A “hate speech” rule written in English is not enough. The examples that make sense in one country may miss slurs, dog whistles, or harassment patterns in another. Worse, a literal translation can sound legally precise while still being socially useless.
Translate intent, not only wording
For policy text, gettext_lazy is handy because it lets you update string values centrally without rewriting templates.
from django.utils.translation import gettext_lazy as _
HATE_SPEECH_NOTICE = _(
"Content that targets people based on protected characteristics is prohibited."
)
That helps maintainability. It does not solve policy accuracy. You still need a policy glossary with examples that matter locally.
## Moderation policy terms
- "Harassment" should map to the local term users and moderators already understand.
- "Hate speech" should include local examples of protected-group targeting.
- Escalate political slogans, coded references, and reclaimed slurs for review.
Literal translation fails here
If your abuse-reporting queue relies on translated categories, those categories need to reflect how people in that language describe harm. Generic wording creates bad reports and weak triage.
Use translator comments aggressively:
#. Safety policy. Refers to attacks on people based on race, religion, caste, ethnicity,
#. nationality, sex, gender identity, sexual orientation, or disability.
msgid "Hate speech is not allowed."
msgstr ""
Teams also need local examples in moderator docs, not just in user-facing policy. The same phrase can be satire in one place and a threat in another. Moderation is one of the clearest cultural insensitivity examples because the same platform can seem strict in English and absent everywhere else.
9. YouTube's Algorithmic Content Promotion Amplifying Misinformation (2017-2019)
Recommendation engines don’t only show what users like. They shape what users keep seeing. On YouTube, that design problem became especially dangerous when conspiracy content, extremist material, and hate-adjacent narratives spread through recommendation loops in different language communities.
If you own any ranking or “recommended for you” system, your localization layer matters more than you think. The labels on report buttons, the categories in moderation tools, and the route from user complaint to review queue all decide whether bad content gets corrected or amplified.
Add circuit breakers to recommendation code
You don’t need a huge trust-and-safety team to implement basic brakes.
def diversify_feed(items, max_per_source=2, max_per_topic=3):
source_counts = {}
topic_counts = {}
result = []
for item in items:
source = item.source_id
topic = item.topic_slug
if source_counts.get(source, 0) >= max_per_source:
continue
if topic_counts.get(topic, 0) >= max_per_topic:
continue
result.append(item)
source_counts[source] = source_counts.get(source, 0) + 1
topic_counts[topic] = topic_counts.get(topic, 0) + 1
return result
That won’t fix misinformation. It does reduce single-topic spirals.
Reporting flows need first-class localization
If the “report misinformation” path is unclear in one locale, users in that market effectively have weaker safety tools.
Keep the reporting copy explicit:
msgid "Report misinformation"
msgstr ""
msgid "This content contains false or misleading claims"
msgstr ""
Then route reports by locale so the right queue gets them. If all non-English reports land in a generic bucket, your system will under-enforce outside English.
The pattern is simple. Recommendation systems need diversity controls, translated reporting surfaces, and locale-aware moderation routing. Skip any one of those and the algorithm does the rest.
10. Amazon's Rekognition System Bias in Facial Recognition (2018-Present)
Facial recognition products raise a separate problem from consumer tagging. Even when the issue is documented internally, teams often bury warnings in product docs, legal pages, or untranslated release notes.
That’s a product communication failure. If accuracy limits vary across demographics, users need to see that warning clearly, in their language, at the point of use.
Put warnings next to the feature
Don’t hide critical caveats in a docs portal. Render them in the UI.
# templatetags/ai_warnings.py
from django import template
from django.utils.translation import gettext as _
register = template.Library()
@register.simple_tag
def ai_bias_warning():
return _(
"This feature uses automated facial analysis. Review results carefully before making decisions."
)
<div class="warning-box">
{% load ai_warnings %}
{% ai_bias_warning %}
</div>
That pattern is boring and effective.
Keep approved wording in one place
Warnings about bias, limitations, and review requirements should have approved translations, not ad hoc ones.
## AI limitation notices
Use the approved legal wording for all bias and accuracy warnings.
Do not soften review requirements.
Do not translate "automated" as "objective" or "neutral".
Escalate any wording that implies certainty about identity or intent.
This is also where product docs and terms of service matter. If your app uses recognition, moderation, or scoring systems, state the limitations plainly and translate that section with care. Teams often spend more effort polishing feature announcements than documenting risk. That’s backwards.
Even if your app is much smaller than Amazon’s, the rule still applies. Any AI-driven feature that touches people needs visible warnings, reviewed copy, and locale-aware documentation. Otherwise you’re asking users to trust a system you haven’t clearly explained.
Cultural Insensitivity: 10-Case Comparison
| Case | Implementation Complexity 🔄 | Resources & Speed ⚡ | Expected Outcomes 📊 | Ideal Use Cases | Key Advantages ⭐ / Insights 💡 |
|---|---|---|---|---|---|
| Airbnb's Racial Discrimination Listings (2016) | Moderate: policy + UX changes, audit tooling | Moderate: moderation staff + automated checks; human review slows rollout | Reduced discriminatory rejections; restored trust when enforced | Marketplaces and UGC listings | ⭐ Prevents exclusion • 💡 Use translation glossaries & automated audits |
| Google's "Gorilla" Misclassification (2015) | High: retrain models + broaden test datasets | High: data collection/annotation intensive; staging tags adds latency | Fewer offensive mislabels; improved cross‑locale accuracy | Image tagging, automated labeling systems | ⭐ Human review layer • 💡 Stage AI tags in DB before display |
| Pepsi Kendall Jenner Protest Ad (2017) | Low-Moderate: editorial/localization workflow changes | Low: consultant/native review is inexpensive; fast to implement | Avoid PR crises; preserve brand credibility | Global marketing campaigns and ads | ⭐ Protects brand image • 💡 Transcreate marketing copy; use pgettext contexts |
| H&M "Coolest Monkey" Hoodie (2018) | Moderate: naming review + blocklist integration | Moderate: build blocklists and enforced review steps | Prevent offensive product launches; avoid boycotts | E‑commerce catalogs and product naming | ⭐ Prevents large-scale backlash • 💡 Tag product names as high‑risk; enforce blocklist checks |
| Facebook's "Primates" Tagging (2021) | High: bias mitigation + oversight systems | High: confidence thresholds, human moderation, reporting UI | Reduced humiliating errors; stronger user trust | Automated tagging, facial recognition features | ⭐ Restores trust via controls • 💡 Show confidence scores; add easy report flows |
| Dolce & Gabbana Blackface Campaign (2016) | Low-Moderate: visual asset review process needed | Low: localization review of images; asset management required | Avoid international outrage; better cultural fit of visuals | Visual campaigns, hero images, localized creatives | ⭐ Prevents visual offensiveness • 💡 Localize images; store locale‑specific assets |
| Spotify's "Racist Playlist" Recommendations (2019–20) | High: audit and redesign recommender taxonomy | High: data science, curators, and localized taxonomies | More diverse recommendations; fairer artist exposure | Recommendation engines and content taxonomy | ⭐ Mitigates segregation by design • 💡 Audit outputs; localize genre definitions |
| Twitter Account Moderation Issues (2016–20) | High: transcreation of policies + enforcement parity | High: multilingual moderation teams and training | More consistent enforcement; fewer cultural blind spots | Social platforms and community moderation | ⭐ Ensures safety across locales • 💡 Transcreate policies; provide translator context |
| YouTube Amplifying Misinformation (2017–19) | High: implement algorithmic circuit breakers | High: engineering + moderation to inject diversity | Reduced spread of harmful content; lower radicalization risk | Recommendation feeds and content promotion | ⭐ Limits harmful amplification • 💡 Inject diversity; down‑rank flagged content |
| Amazon Rekognition Bias (2018–Present) | High: bias testing, documentation, and warnings | High: demographic testing, legal review, translated warnings | Safer deployments; informed customers and reduced misuse | B2B AI features and public‑facing APIs | ⭐ Increases transparency • 💡 Prominently translate bias warnings; display limits in UI |
Building a Culturally Aware CI/CD Pipeline
A release goes green. Tests pass. The copy is translated. Two days later, support gets screenshots from a local market showing an offensive label, a loaded slogan, or a product image that should never have shipped there. That failure usually starts much earlier, in source strings, review gaps, and missing context.
These examples point to the same engineering problem. Teams treat localization as string replacement at the end of the pipeline. They extract text, ship .po files to translators, merge the results, and assume the risk is covered. That workflow catches missing translations. It does not catch harmful source copy, culturally loaded naming, unsafe classifier output, or policy terms that break once they leave the original market.
Build for that risk at the repo level.
Keep TRANSLATING.md in source control, next to the code and locale files it governs. It should define blocked terms, preferred alternatives, moderation vocabulary, product naming constraints, and notes for sensitive markets or topics. If a rule matters enough to block a release, it belongs in Git history and code review, not in a Slack thread or a vendor brief.
Then add checks before translation starts. Django teams already lint Python, templates, and migrations. Apply the same discipline to msgid values. A pre-commit hook can scan new English strings for blocked phrases. CI can reject risky product names, campaign slogans, and AI labels before they ever reach translators.
A simple hook is enough to start:
#!/usr/bin/env bash
python scripts/check_blocked_terms.py
Wire that into pre-commit or CI and fail fast.
Context handling matters just as much. Do not send legal notices, ad copy, AI-generated labels, and generic UI text through one flat review path. Use pgettext or separate translation domains so high-risk strings get different scrutiny. If the system treats "Save" and "Join the movement" as the same kind of translation task, the system is too blunt.
Translator comments do real work here. Add notes where meaning depends on adjacent imagery, audience, or policy intent. In Django templates and Python code, use translator comments aggressively for anything tied to identity, religion, race, ethnicity, caste, gender, protests, or geopolitics. One line of context often prevents the kind of mistake that later gets explained away as a bad translation.
Human review should be targeted. Native review for every button label is expensive and usually unnecessary. Native review for campaign copy, moderation text, product names, legal warnings, and identity-related terminology is a good trade. Save reviewer time for strings that can create reputational or legal problems.
AI output needs stricter controls than hand-written copy. Do not render raw model labels to users when those labels describe people. Store predictions and confidence scores, route them through a review layer, and give moderators an internal tool to resolve reports in the user's language. These small workflows are what keep an embarrassing model failure from turning into a public incident.
Visual localization belongs in the same pipeline. If a locale can change text but cannot swap an image, hero banner, or packaging mockup, the build is only half localized. Several of the worst failures in this article were visual, or visual plus copy. The pipeline has to treat assets as locale-specific release inputs.
The pattern is clear: respect is not a soft nice-to-have. It changes trust, adoption, and the amount of damage a mistake can do after release. The practical fix is to move cultural review into places engineers already use every day: Git diffs, gettext catalogs, CI jobs, code review, locale-aware feature flags, and moderation tooling.
If you are still copy-pasting .po strings into random tools, you are doing manual work at the weakest point in the process. TranslateBot keeps translations in your repo, respects placeholders, writes directly to Django locale files, and uses a versioned TRANSLATING.md glossary so cultural guardrails live next to the code they affect. That setup fits small teams well. You keep reviewable diffs, preserve technical context, and stop treating localization as cleanup work after the build is already done.