django-autotranslate
Django's internationalization workflow generates gettext .po catalogs, but filling those catalogs with translations was still a repetitive manual task. I built django-autotranslate to automate that last mile.The project started as a small developer utility and grew into a reusable Python package with translation-service integrations and handling for the details that make gettext catalogs difficult to translate safely.
Visit siteHow I contributed
Challenges
- Automating .po files without breaking them
- Translation had to operate on gettext catalog entries while preserving the structure and formatting Django expected.
- Translation services can change application syntax
- Formatting placeholders such as %s, %d and %(name)s should survive translation unchanged, even though the surrounding text is sent through an external translation service.
- Localization is more than one string
- The command needed to account for locales, untranslated entries, fuzzy translations, Unicode, multiline messages and plural forms rather than treating every catalog entry as an independent plain-text string.
Role & ownership
- OwnedInitial project implementation and architecture
- OwnedDjango management command and translation workflow
- OwnedGoogle Translate integration and configuration
- OwnedPlaceholder-preservation and catalog-handling logic
- OwnedProject releases and Django compatibility through 2017
How it worked
- Application stringsmakemessages— extract
- makemessages.po catalogs— catalogs
- .po catalogstranslate_messages— read entries
- translate_messagesTranslator service— translate text
- Translator serviceGoogle Translate— provider
- Translator service.po catalogs— write translations
The management command handled the catalog workflow while the translation service was kept behind an abstraction. That separation made it possible to add or change providers without changing the catalog-processing layer.
Before translation, formatting placeholders were protected from the translation service and restored afterwards. Catalog-specific behavior such as untranslated entries, fuzzy flags and plural forms was handled by the library rather than delegated to the translation API.
Key decisions
- 01
Automate the existing gettext workflow instead of replacing it
- Why
- Django already had a well-established internationalization system. The useful gap was the manual translation step between catalog generation and completed translations.
- Trade-off
- The tool had to respect gettext's catalog semantics and Django's expectations rather than simplifying the problem by inventing a new format.
- 02
Keep translation providers behind a service abstraction
- Why
- The catalog-processing workflow should not depend on one translation provider.
- Trade-off
- Introduced an abstraction layer for what initially could have been a much smaller integration.
- Result
- The project could support different translation services without changing the command's core workflow.
- 03
Protect formatting placeholders before translation
- Why
- External translation services should translate human language, not modify application formatting syntax.
- Trade-off
- Added preprocessing and restoration logic around each translation.
What changed
- A repetitive Django localization workflow became a single management command.
- The package handled locale selection, untranslated entries, fuzzy flags, Unicode, placeholders, multiline messages and plural forms.
- It was published as an MIT-licensed Python package and released through PyPI.
- The project subsequently attracted community contributions and continued evolving after my active development period.
Timeline
- 2015–2016
Built and released the core library
Developed the translation command, locale handling, translation-service integration, Unicode support and placeholder preservation.
- 2016
Expanded catalog handling
Plural forms and additional gettext edge cases were incorporated as the open-source project evolved.
- 2017
Last active development period
Maintained Django compatibility through Django 1.10 before moving on.
Artifacts
Looking back
The project is a good example of the kind of engineering problem that looks trivial until it touches a real format. Calling a translation API was easy; preserving the semantics of Django's gettext catalogs was the actual problem.
I also like that the project remained useful after I moved on. The later history includes community fixes and compatibility work, but the personal timeline here deliberately ends with my active development rather than treating every later commit as mine.