Translation & Internationalization¶
Bifolk supports English and German with full Django i18n support.
Target Audience: Software developers
Supported Languages¶
| Language | Code | Status |
|---|---|---|
| English | en |
Primary (source language) |
| German | de |
Formal "Sie" form |
Coverage target: 100%
Language Selection¶
Bifolk uses a two-layer language detection system:
1. User Profile Language (highest priority) → UserLanguageMiddleware
2. Language Cookie → LocaleMiddleware (Django)
3. Browser Accept-Language Header → LocaleMiddleware (Django)
4. LANGUAGE_CODE setting (default: 'en') → Fallback
UserLanguageMiddleware¶
Custom middleware (bifolk/middleware.py) that reads the language from user.profile.language and activates it for authenticated users. Overrides browser detection.
Configuration¶
# bifolk/settings.py
LANGUAGE_CODE = 'en'
LANGUAGES = [
('en', 'English'),
('de', 'Deutsch'),
]
LOCALE_PATHS = [
BASE_DIR / 'locale',
]
Translation Workflow¶
1. Mark strings → _("text") or {% trans "text" %}
2. Extract → python manage.py makemessages -l de --ignore=venv
3. Translate → Edit locale/de/LC_MESSAGES/django.po
4. Compile → python manage.py compilemessages -l de
5. Verify → python scripts/translation/check_translations.py
Marking Strings for Translation¶
In Python Code¶
from django.utils.translation import gettext_lazy as _
# Model fields (module-level) — use gettext_lazy
class Hive(models.Model):
name = models.CharField(max_length=100, verbose_name=_("Hive Name"))
# Choices — use gettext_lazy
HEALTH_CHOICES = [
('excellent', _('Excellent')),
('good', _('Good')),
]
# Messages in views (function-level) — gettext_lazy works here too
from django.contrib import messages
messages.success(request, _("Hive created successfully"))
Note
Use gettext_lazy (aliased as _) everywhere. It works at both module-level (models, forms) and function-level (views). Use plain gettext only when you specifically need immediate translation.
In Templates¶
{% load i18n %}
{# Simple string #}
<h1>{% trans "Hive Management" %}</h1>
{# With variables #}
{% blocktrans %}Welcome to {{ site_name }}{% endblocktrans %}
{# Pluralization #}
{% blocktrans count counter=hives.count %}
You have {{ counter }} hive.
{% plural %}
You have {{ counter }} hives.
{% endblocktrans %}
{# Context for ambiguous words #}
<button>{% trans "Save" context "button" %}</button>
In Forms¶
from django.utils.translation import gettext_lazy as _
class HiveForm(forms.ModelForm):
class Meta:
model = Hive
fields = ['name', 'location']
labels = {
'name': _('Hive Name'),
'location': _('Location'),
}
Translation Files¶
File Location¶
app/locale/
└── de/
└── LC_MESSAGES/
├── django.po # Source translations (edit this)
└── django.mo # Compiled binary (auto-generated)
.po File Format¶
# Translator comment
#: hives/models.py:28
msgid "Hive Type"
msgstr "Bienenstocktyp"
# Context-specific translation
msgctxt "button"
msgid "Save"
msgstr "Speichern"
# Pluralization
msgid "%(count)s hive"
msgid_plural "%(count)s hives"
msgstr[0] "%(count)s Bienenstock"
msgstr[1] "%(count)s Bienenstöcke"
Commands Reference¶
Extract Messages¶
# Extract for German
cd app && python manage.py makemessages -l de --ignore=venv
# Extract for all languages
cd app && python manage.py makemessages -a --ignore=venv
Compile Messages¶
Check Coverage¶
Audit Translations¶
Detect Hardcoded Strings¶
See scripts/translation/README.md for full script documentation.
Multi-Language Model Fields¶
For dynamic, user-configurable choices, Bifolk uses explicit language columns:
class ConfigurableChoice(models.Model):
value = models.CharField(max_length=100) # Internal value
label_en = models.CharField(max_length=200) # English label
label_de = models.CharField(max_length=200) # German label
def get_label(self, language='en'):
if language == 'de' and self.label_de:
return self.label_de
return self.label_en
Usage in views:
Adding a New Language¶
- Add to
LANGUAGESinbifolk/settings.py - Add to
Profile.LANGUAGE_CHOICESinusers/models.py - Create migration:
python manage.py makemigrations users - Extract messages:
python manage.py makemessages -l <code> --ignore=venv - Translate the
.pofile - Compile:
python manage.py compilemessages -l <code>
German Translation Guidelines¶
- Use formal German ("Sie" form, not "du")
- Maintain consistent terminology:
| English | German |
|---|---|
| Hive | Bienenstock |
| Queen | Koenigin |
| Inspection | Kontrolle |
| Harvest | Ernte |
| Batch | Charge |
| Bucket | Eimer |
| Jar | Glas |
Troubleshooting¶
| Problem | Solution |
|---|---|
| Translations not appearing | Run compilemessages and restart |
| Fuzzy translations ignored | Remove #, fuzzy from .po file, recompile |
| Wrong language showing | Check user profile language, clear browser cookies |
makemessages missing strings |
Verify _() or {% trans %} syntax, check --ignore patterns |
Related Documentation¶
- Architecture Overview - i18n architecture context
- Database Models - ConfigurableChoice model
- Translation scripts:
scripts/translation/README.md