Ship translations
How to move translated texts to other iDempiere databases, for a plug-in, a language pack or a customer. Applies to iDempiere 11 and later. Checked against iDempiere master in October 2026.
- Use translation XML files, the files of the Translation Import/Export process. The import only updates existing rows and reports what it did not find.
- Core records: match by ID. Plug-in and tenant records: match by UU.
- Log Migration Scripts does not record translations.
Core or plug-in
| Core | Plug-in | |
|---|---|---|
| Records | IDs up to 999999, same in every database | IDs differ per database |
| Match by | id or uuid | uuid of the base record |
| Goes to | The language pack of the language | The plug-in's own XML files or 2Pack |
Choose a format
| Format | Use for | Creates rows | Sets IsTranslated |
|---|---|---|---|
| Translation XML files | Records that already exist | No | Y, or the trl value |
| 2Pack | A plug-in shipping its records with their translations | Yes | The 2Pack value, N if missing |
| Migration script | A plug-in that already ships SQL | If you write INSERT | What you write |
Ship with translation files
Export a translation file
- Run the Translation Import/Export process: Export, the Language, the "System" Tenant. Table is optional.
- Select Only Centralized Data for core records only. Clear it for plug-in records, then remove rows that are not yours (there is no Entity Type filter).
- Download the ZIP. It has one file per table:
<Table>_Trl_<language>.xml.
Centrally maintained fields are not exported. Their texts come from the element.
Translation file format
Replace every YOUR_... value before you import the file.
<?xml version="1.0" encoding="UTF-8"?>
<idempiereTrl language="sk_SK" table="AD_Message">
<row id="YOUR_AD_MESSAGE_ID" trl="Y">
<value column="MsgText" original="YOUR_ENGLISH_TEXT">YOUR_TRANSLATION</value>
</row>
<row uuid="YOUR_AD_MESSAGE_UU" trl="N">
<value column="MsgText" original="YOUR_ENGLISH_TEXT">YOUR_TRANSLATION</value>
</row>
</idempiereTrl>
tableis the base table.uuidis the UU of the base record (AD_Message_UU), not of the_Trlrow. When a row hasuuid, the import uses it and ignoresid.trlsetsIsTranslated. Missing meansY.originalis ignored on import.
Import a translation file
- Zip the files, flat or in a
<language>/folder. - Run the Translation Import/Export process: Import, the Language, the ZIP file (or an
https://URL), the "System" Tenant. An empty Tenant updates every Tenant. - Compare the
Updated=count in the log with your row count. - Run Synchronize Terminology if you changed elements, then Cache Reset.
Command line: RUN_TrlImport.sh <language> [folder].
Ship translations with 2Pack
Translations travel only when 2PACK_HANDLE_TRANSLATIONS is Y on both systems.
| Key | Default | Level | What it does |
|---|---|---|---|
2PACK_HANDLE_TRANSLATIONS | N | System | Packs _Trl rows with their parent record |
- Translations sit inside their parent element. A 2Pack with only
_Trlrows fails. - Pack In skips languages that are not system languages on the target.
See Using 2Pack to transfer data.
Ship translations with a migration script
Write it by hand. Match by a key that is the same everywhere. PostgreSQL and Oracle. Put the file in the migration folder of your release; iD14 is an example.
SELECT register_migration_script('202610251200_IDEMPIERE-NNNN_sk_SK.sql') FROM dual;
UPDATE AD_Message_Trl SET MsgText='YOUR_TRANSLATION', IsTranslated='Y',
Updated=TO_TIMESTAMP('2026-10-25 12:00:00','YYYY-MM-DD HH24:MI:SS'), UpdatedBy=100
WHERE AD_Message_ID=(SELECT AD_Message_ID FROM AD_Message WHERE Value='PasswordResetSuccess')
AND AD_Language='sk_SK' AND AD_Client_ID=0
;
AD_Client_ID=0keeps tenant-level messages untouched.- Elements: match by
ColumnName. Tenant data such asR_MailText: match by UU. - iDempiere does not run plug-in SQL files by itself. See Run Script with 2Pack.
Translate with a tool or AI
- Export the language.
- Translate only the text inside
value. Keepid,uuidandcolumn. - Set
trl="N"on rows nobody has reviewed. - Import and check the
Updated=count.
A wrong id that exists in your database changes another record. Prefer uuid outside core.
Troubleshooting
| Symptom | Fix |
|---|---|
| Fewer rows updated than in the file | Run Language Maintenance with Add. Use uuid for IDs above 999999. |
Update Rows=n (Should be 1) | Set the Tenant and import again. |
| Old text still shows | Run Cache Reset. |
To keep translations through upgrades, see Roll out a language.