--- name: skill-translate description: "AL translation and localization for Business Central. Use when working with XLF files, NAB tools, or implementing multi-language support in extensions." --- # Skill: AL Translation Management ## Purpose Manage multilingual translations for AL extensions using XLF files: create and refresh language files, batch-translate with NAB AL Tools, preserve placeholders, enforce character limits, maintain terminology consistency, and implement quality review workflows. ## When to Load This skill should be loaded when: - A new target language needs to be added to an AL extension - Untranslated texts need to be translated after code changes - Translation quality review is needed (placeholder validation, character limits) - Regional language variations must be managed (es-ES vs es-MX) - Incremental translation is required after refreshing XLF files ## Core Patterns Before adopting BC29 namespace-aware translations, read the translation section of [AL18 capability checks](../skill-migrate/references/al18-capabilities.md). Verify `TranslationsWithNamespaces` in the installed schema and compare generated XLF IDs and retained translations before changing an existing project's setting. ### Pattern 1: Create Language File Create a new XLF translation file from the generated source: ``` createLanguageXlf generatedXlfFilePath: "Translations/MyApp.g.xlf" targetLanguageCode: "es-ES" matchBaseAppTranslation: true // pre-populate from Microsoft base translations ``` **Rules:** - The `.g.xlf` file is auto-generated by the AL compiler — never edit it manually - `matchBaseAppTranslation: true` copies existing Microsoft translations for standard terms - One language file per locale: `MyApp.es-ES.xlf`, `MyApp.fr-FR.xlf`, etc. ### Pattern 2: Refresh and Retrieve Texts After code changes, sync the language file and get new untranslated texts: ``` // Step 1: Refresh — adds new entries, preserves existing translations refreshXlf generatedXlfFilePath: "Translations/MyApp.g.xlf" filePath: "Translations/MyApp.es-ES.xlf" // Step 2: Get untranslated texts with context getTextsToTranslate filePath: "Translations/MyApp.es-ES.xlf" limit: 50 offset: 0 ``` Each text entry includes: - `id` — unique identifier for the translation unit - `source` — original text to translate - `type` — context: `Table Customer - Field Name - Property Caption` - `maxLength` — character limit (if set in AL source) - `comments` — notes about placeholders (%1, %2) ### Pattern 3: Batch Translation Translate multiple texts in a single operation: ``` saveTranslatedTexts filePath: "Translations/MyApp.es-ES.xlf" translations: [ { "id": "Table_ContosoProject_Field_Description_Caption", "targetText": "Descripción", "targetState": "translated" }, { "id": "Page_ContosoProjectCard_Action_Release_Caption", "targetText": "Lanzar", "targetState": "translated" }, { "id": "Codeunit_ContosoMgt_Error_CustomerNotFound", "targetText": "Cliente %1 no encontrado.", "targetState": "translated" } ] ``` **Translation rules:** 1. **Preserve placeholders** — `%1`, `%2`, `%3` must appear in the same order 2. **Respect `maxLength`** — abbreviate if needed, never exceed 3. **Match context** — same English word may have different translations depending on `type` 4. **Follow Microsoft terminology** — use base app translations for standard BC terms ### Pattern 4: Translation Quality Review Use translation states to implement a review workflow: | State | Meaning | Who | |---|---|---| | `translated` | Initial translation done | Translator | | `needs-review-translation` | Flagged for review | Translator / QA | | `final` | Reviewed and approved | Reviewer | | `signed-off` | Production-ready | Language lead | ``` // Find texts needing review getTranslatedTextsByState filePath: "Translations/MyApp.es-ES.xlf" translationStateFilter: "needs-review-translation" limit: 0 // After review, promote to final saveTranslatedTexts filePath: "Translations/MyApp.es-ES.xlf" translations: [ { "id": "...", "targetText": "...", "targetState": "final" } ] ``` **Common quality issues:** ``` // Issue 1: Missing placeholder ❌ Source: "Posted %1 of %2" ❌ Translation: "Registrado %2" // missing %1 ✅ Translation: "Registrado %1 de %2" // Issue 2: Character limit exceeded ❌ Source: "Post" (maxLength: 10) ❌ Translation: "Registrar y contabilizar" // 25 chars ✅ Translation: "Registrar" // 9 chars // Issue 3: Context-dependent meaning Source: "Post" Context: Action caption → "Registrar" ✅ Context: Table name → "Correo" ✅ (different meaning!) ``` ### Pattern 5: Translation Memory and Glossary Use existing translations for consistency: ``` // Get all translated texts as a reference map getTranslatedTextsMap filePath: "Translations/MyApp.es-ES.xlf" limit: 0 // Search for specific terms across translations getTextsByKeyword filePath: "Translations/MyApp.es-ES.xlf" keyword: "Customer|Vendor|Invoice" isRegex: true caseSensitive: false searchInTarget: true ``` **Glossary approach:** - Before translating a batch, retrieve existing translations with `getTranslatedTextsMap` - Search for related terms with `getTextsByKeyword` to ensure consistent terminology - Standard BC terms should match Microsoft's official base app translations ### Pattern 6: Regional Variations Manage locale-specific translations (e.g., es-ES vs es-MX): ``` // Step 1: Create base translation (es-ES) createLanguageXlf generatedXlfFilePath: "Translations/MyApp.g.xlf" targetLanguageCode: "es-ES" matchBaseAppTranslation: true // Step 2: Create regional variant from base createLanguageXlf generatedXlfFilePath: "Translations/MyApp.g.xlf" targetLanguageCode: "es-MX" matchBaseAppTranslation: true // Step 3: Get base translations as reference getTranslatedTextsMap filePath: "Translations/MyApp.es-ES.xlf" limit: 0 // Step 4: Copy base, then adjust regional terms // "Ordenador" (es-ES) → "Computadora" (es-MX) // "Factura" stays the same in both ``` ## Workflow ### Step 1: Setup 1. Build the extension to generate the `.g.xlf` file: `al_build` 2. Create language file for each target locale (Pattern 1) 3. If language files already exist, refresh them (Pattern 2) ### Step 2: Translate 1. Retrieve untranslated texts with `getTextsToTranslate` (paginate with `limit`/`offset`) 2. Check translation memory for consistency (Pattern 5) 3. Translate in batches of 20-50 items (Pattern 3) 4. Save progress frequently — use `targetState: "translated"` ### Step 3: Review 1. Retrieve texts by state: `getTranslatedTextsByState` with `"translated"` filter 2. Validate: - All placeholders preserved (`%1`, `%2`, etc.) - Character limits respected - Context-appropriate translations - Consistent terminology across the extension 3. Mark reviewed texts: `targetState: "needs-review-translation"` → `"final"` ### Step 4: Finalize 1. Verify no untranslated texts remain: `getTextsToTranslate` with `limit: 0` 2. Promote all `final` texts to `signed-off` 3. Build the extension to verify XLF integration: `al_build` 4. Test the UI in the target language ## Common Language Codes | Code | Language | Code | Language | |---|---|---|---| | `es-ES` | Spanish (Spain) | `fr-FR` | French (France) | | `es-MX` | Spanish (Mexico) | `fr-CA` | French (Canada) | | `de-DE` | German | `pt-BR` | Portuguese (Brazil) | | `it-IT` | Italian | `nl-NL` | Dutch | | `da-DK` | Danish | `sv-SE` | Swedish | | `nb-NO` | Norwegian | `fi-FI` | Finnish | | `pl-PL` | Polish | `cs-CZ` | Czech | | `ja-JP` | Japanese | `zh-CN` | Chinese (Simplified) | ## References - [Working with Translation Files](https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/devenv-work-with-translation-files) - [NAB AL Tools Extension](https://marketplace.visualstudio.com/items?itemName=nabsolutions.nab-al-tools) - [XLIFF 1.2 Standard](http://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html) - [MaxLength Property](https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/properties/devenv-maxlength-property) ## Constraints - This skill covers **XLF translation management, batch translation, and quality review workflows** - Do NOT edit `.g.xlf` files manually — they are compiler-generated - Do NOT remove or reorder placeholders (`%1`, `%2`) in translations - Do NOT exceed `maxLength` character limits defined in AL source - Do NOT translate without checking existing translation memory first (consistency) - Translation testing in UI → `skill-testing.md` | Page captions and tooltips → `skill-pages.md`