Import & Export
Export translation overrides to CSV or PO files, and import them back. Useful for bulk editing in a spreadsheet, sharing translations between environments, or migrating between backends.
All import/export operations are available from the TranslationEntry admin page.
Exporting
From the translation overrides list in Django admin, click the Export button in the top-right toolbar.
Format
Choose between two export formats:
A flat CSV file with columns: language, msgid, context, msgid_plural, form_index, msgstr, is_active. Plural translations expand into multiple rows (one per form), while singular translations use a single row.
CSV exports support two scopes:
- Overrides only: exports entries from the database (your customizations)
- All: merges
.pofile defaults with database overrides, so you get a complete picture of every translation
Standard gettext .po files.
- When a single language is selected, you get one
.pofile - When "All languages" is selected, you get a
.ziparchive containing one.pofile per language
PO exports always include .po file defaults merged with database overrides.
Language filter
Select a specific language to export, or choose "All languages" to export everything.
Bulk action export
You can also export specific entries using the bulk actions dropdown. Select entries in the list view, then choose Export selected as CSV or Export selected as PO (zip) from the action menu.
Technical details
CSV bulk export writes only the selected entries, without merging .po defaults. PO bulk export creates a zip with one .po file per language found in the selection.
Importing
Click the Import button in the toolbar to open the import form.
Supported file types
| File type | Extension | Language handling |
|---|---|---|
| CSV | .csv |
Language per row (language column) |
| PO | .po |
Single language per file (select in form or auto-detect from PO metadata) |
| ZIP | .zip |
Multiple .po files, language derived from filename (e.g., en.po, cs.po) |
CSV format
The CSV file must have a header row. Required columns: language, msgid, msgstr. Optional columns: context, msgid_plural, form_index, is_active.
For singular translations, each entry is one row with msgid_plural and form_index left empty. For plural translations, each form gets its own row with form_index set to the form number (0, 1, 2, ...).
| language | msgid | context | msgid_plural | form_index | msgstr | is_active |
|---|---|---|---|---|---|---|
| en | hello | Hello | true | |||
| en | cart | cart.plural | 0 | %(n)s item | true | |
| en | cart | cart.plural | 1 | %(n)s items | true | |
| cs | hello | Ahoj | false | |||
| cs | cart | cart.plural | 0 | %(n)s kus | true | |
| cs | cart | cart.plural | 1 | %(n)s kusy | true | |
| cs | cart | cart.plural | 2 | %(n)s kusů | true |
language,msgid,context,msgid_plural,form_index,msgstr,is_active
en,hello,,,,Hello,true
en,cart,,cart.plural,0,%(n)s item,true
en,cart,,cart.plural,1,%(n)s items,true
cs,hello,,,,Ahoj,false
cs,cart,,cart.plural,0,%(n)s kus,true
cs,cart,,cart.plural,1,%(n)s kusy,true
cs,cart,,cart.plural,2,%(n)s kusů,true
When is_active is omitted, entries default to active. Values false, 0, and no (case-insensitive) are treated as inactive.
Dry run
Check Dry run before importing to preview what would change without writing to the database. The dry run report shows:
- How many entries would be created or updated
- Which entries are already up to date (unchanged)
- A detailed table of each change, showing old and new values side by side
- Any validation errors (empty msgid, missing language)
Review the preview, then re-upload the same file without dry run to apply the changes.
Import behavior
Imports use upsert logic. Rows are grouped by (language, msgid, context, msgid_plural) and each group becomes one translation entry:
- If no matching entry exists, a new one is created
- If a matching entry exists, its translation and
is_activefields are updated - All changes happen in a single database transaction
- Edit history is recorded for the import
Round-trip workflow
Export, edit externally, and re-import. The export and import formats are designed to round-trip without losing state.
CSV round-trip
- Export as CSV (with "All" scope if you want defaults included)
- Edit translations in a spreadsheet
- Import the modified CSV
The is_active column preserves activation state through the round-trip.
PO round-trip
PO exports encode the active/inactive state so that re-importing restores it exactly:
- Active overrides are written as regular PO entries with the override in
msgstr - Inactive overrides are written with the
.podefault inmsgstr, the override stored in a specialltpending:translator comment, and thefuzzyflag set
msgid "Hello"
msgstr "Hello World"
#, fuzzy
# ltpending:SGVsbG8gV29ybGQ=
msgid "Hello"
msgstr "Ahoj"
msgstr contains the .po default. The ltpending: comment holds the inactive override (base64-encoded). On import, the override is restored as inactive.
Technical details
When importing a .po file, entries with an ltpending: comment are imported with the pending value as msgstr and is_active=False. For entries without ltpending:, the fuzzy flag determines activation: fuzzy entries are imported as inactive, non-fuzzy as active. This means standard PO workflows (marking entries as fuzzy for review) also work as expected.