Why the CustomObjectTranslation Metadata Type Matters
In multi-language global Salesforce deployments, CustomObjectTranslation controls how every custom object (and custom metadata on standard objects) renders when a user changes their personal language locale. It translates not just the object’s singular/plural name, grammatical gender, and case forms (caseValues), but also all child metadata attached to that object:
- Custom Fields & Help Text (
CustomFieldTranslation) - Local Picklist & Lookup Filter Messages
- Validation Rule Error Messages (
ValidationRuleTranslation) - Record Type Labels & Descriptions (
RecordTypeTranslation) - Object-Specific Quick Actions, Layouts, Field Sets, and WebLinks
SFDX Source Decomposition (objectTranslations/)
In SFDX source format, CustomObjectTranslation is decomposed into one folder per <ObjectName>-<locale> inside force-app/main/default/objectTranslations/:
force-app/main/default/objectTranslations/Invoice__c-de/
├── Invoice__c-de.objectTranslation-meta.xml
├── Amount__c.fieldTranslation-meta.xml
└── Status__c.fieldTranslation-meta.xmlReal-World XML Examples
1. Object-Level Translation (objectTranslations/Invoice__c-de/Invoice__c-de.objectTranslation-meta.xml)
<?xml version="1.0" encoding="UTF-8"?>
<CustomObjectTranslation xmlns="http://soap.sforce.com/2006/04/metadata">
<caseValues>
<caseType>Nominative</caseType>
<plural>false</plural>
<value>Rechnung</value>
</caseValues>
<caseValues>
<caseType>Nominative</caseType>
<plural>true</plural>
<value>Rechnungen</value>
</caseValues>
<gender>Feminine</gender>
<nameFieldLabel>Rechnungsnummer</nameFieldLabel>
<recordTypes>
<description>Standardrechnung für Unternehmenskunden</description>
<label>Unternehmensrechnung</label>
<name>Commercial_Invoice</name>
</recordTypes>
<validationRules>
<errorMessage>Der Rechnungsbetrag muss größer als 0 sein.</errorMessage>
<name>Positive_Invoice_Amount</name>
</validationRules>
</CustomObjectTranslation>2. Field-Level Translation (objectTranslations/Invoice__c-de/Status__c.fieldTranslation-meta.xml)
<?xml version="1.0" encoding="UTF-8"?>
<CustomFieldTranslation xmlns="http://soap.sforce.com/2006/04/metadata">
<help>Aktueller Zahlungsstatus der Rechnung.</help>
<label>Rechnungsstatus</label>
<name>Status__c</name>
<picklistValues>
<masterLabel>Draft</masterLabel>
<translation>Entwurf</translation>
</picklistValues>
<picklistValues>
<masterLabel>Paid</masterLabel>
<translation>Bezahlt</translation>
</picklistValues>
</CustomFieldTranslation>Senior Architect Deployment Gotchas
Warning
The nameFieldLabel Silent Failure: If you populate <nameFieldLabel> in .objectTranslation-meta.xml while leaving <caseValues><value></value></caseValues> blank (which Salesforce CLI frequently generates when retrieving untranslated objects), Salesforce ignores the nameFieldLabel translation during deployment! Always provide at least the singular and plural Nominative <caseValues> when translating an object’s Name field.
- Global Value Set Picklists Are Not Translated Here: If a picklist field uses a GlobalValueSet, do not put
<picklistValues>inside its.fieldTranslation-meta.xmlfile. Translate those shared values once in GlobalValueSetTranslation. - Enable Translation Workbench First: Deploying
CustomObjectTranslationto a new Scratch Org or Sandbox where Translation Workbench is disabled or the target language is inactive will fail unlessTranslationssettings are enabled in the org definition.
Frequently asked questions
How does SFDX decompose CustomObjectTranslation in source format?
In SFDX source format, force-app/main/default/objectTranslations/ObjectName-lang/ contains one main ObjectName-lang.objectTranslation-meta.xml file for object-level translations and separate FieldName.fieldTranslation-meta.xml files for each custom field.
Why does deploying a CustomObjectTranslation fail when updating nameFieldLabel?
Salesforce requires at least one non-empty caseValues entry inside the .objectTranslation-meta.xml file whenever you deploy a translated nameFieldLabel. If caseValues has an empty value tag, the nameFieldLabel translation is silently ignored or rejected.
Can CustomObjectTranslation translate Validation Rule error messages and Record Types?
Yes. Beyond field labels and help text, CustomObjectTranslation includes child arrays for validationRules, recordTypes, quickActions, layouts, fieldSets, sharingReasons, webLinks, and workflowTasks.
How are picklist values on a specific field translated when the field does not use a GlobalValueSet?
Local custom picklist values are translated directly inside that field's .fieldTranslation-meta.xml file using picklistValues blocks with masterLabel and translation elements.
Comments
Questions, corrections and tips are welcome. Comments are GitHub Discussions, so you sign in with GitHub.