Translatable text should be translated
Properties like Caption and ToolTip, as well as Label variables, are translatable by default — the compiler generates a unique translation ID for each one. For users in other locales to see translated text, each ID must have a matching <trans-unit> entry in the project’s XLIFF files. When the entry is missing, empty, or still marked needs-translation, those users see raw source-language text instead: a caption that should read Programnavn in Danish displays as Program Name.
Add or complete the <trans-unit> entry in each target language’s XLIFF file.
Example
table 50100 "Reward Program"
{
fields
{
field(1; "Program Name"; Text[100])
{
Caption = 'Program Name'; // Translatable text should be translated [LC0091]
}
}
}Add the translation to the XLIFF file (Translations/MyApp.da-DK.xlf):
<trans-unit id="Table 2328808854 - Field 1296262074 - Property 2879900210"
size-unit="char" translate="yes" xml:space="preserve">
<source>Program Name</source>
<target state="translated">Programnavn</target>
<note from="Xliff Generator" annotates="general" priority="3">
Table Reward Program - Field Program Name - Property Caption
</note>
</trans-unit>When the diagnostic is reported
| Element | Properties checked |
|---|---|
| Tables, table extensions, enums, enum values, reports, XmlPorts, permission sets, profiles | Caption |
| Table fields | Caption, ToolTip |
| Pages, page extensions, queries | Caption, plus controls (Caption, ToolTip, OptionCaption) and actions (Caption, ToolTip) |
| Label variables (local and global) | The label value |
| Report labels | The label value |
By default, the rule checks every XLIFF file in the Translations/ directory. Restrict which languages are checked by setting LanguagesToTranslate in alcops.json:
{
"LanguagesToTranslate": ["da-DK", "de-DE"]
}For extension objects, translation IDs are generated relative to the translation root symbol, not the extension itself. When multiple extensions in the same app target the same base object, the compiler folds them under the extension with the lowest ID. This rule uses the same folding logic, so the reported translation IDs match exactly what the AL compiler generates.
Exception
The rule does not trigger when:
- The label or property has
Locked = true. This is the intended mechanism for text that should remain in the source language — API field names, technical identifiers, or format strings. - The caption belongs to a table or field with
ObsoleteState = Removed, or to a field inside such a table. The compiler locks these trans-units and drops them from the generated XLIFF, so no translation is expected. - The element sits inside an object with
ObsoleteState = Moved. The compiler does not visit a moved object at all. - No XLIFF files exist in the
Translations/directory. - The project manifest does not include
"TranslationFile"in thefeaturesarray.
Elements with ObsoleteState = Pending are still reported. The compiler only locks trans-units for Removed tables and fields, so a pending caption keeps its translate="yes" entry in the XLIFF and needs a translation until it is removed. Label variables and report labels inside an obsolete object are reported as well, whatever the object’s state: the compiler never locks a label because of its containing object, only because of the label’s own Locked = true. A label left behind in a removed table keeps appearing in the translation file until it is locked or deleted.
When a translatable property cannot use Locked but should remain untranslated, suppress with a pragma:
#pragma warning disable LC0091 // Internal tooling page, not exposed to end users
Caption = 'Debug Panel';
#pragma warning restore LC0091See also
- Working with Translation Files on Microsoft Learn
- Translations Overview on Microsoft Learn