Enabling translation for module configurationfor Drupal 8 , 9 , 10 , and 11

Last updated :  

You've now made your module's interface strings translatable — but what about content that lives in configuration, not code? A block's custom text, a config entity's label, a settings-form description an administrator typed in — none of that gets scanned by the string-translation system, because it's not a literal argument to t() anywhere. Config translation solves this: it lets a site administrator provide a per-language override for specific configuration values, through the normal Configuration Translation UI.

What you'll learn in this lesson

  • Why configuration values need a completely different translation mechanism than interface strings
  • How to mark a schema property as translatable using type: label
  • How the Configuration Translation module discovers what's translatable on your config entity
  • Where translated config actually gets stored

Marking a schema property translatable

my_module.robot.*:
  type: config_entity
  label: 'Robot'
  mapping:
    id:
      type: string
      label: 'Robot id'
    label:
      type: label
      label: 'Label'
    description:
      type: label
      label: 'Description'

The key detail is type: label instead of plain type: string. Both store a text value identically — the difference is purely about intent. label tells Drupal's Configuration Translation system "this specific property is human-facing display text, eligible for translation." string is for identifiers and machine values that should never be translated, like the id field above.

Enabling translation for a config entity type

/**
 * @ConfigEntityType(
 *   id = "robot",
 *   label = @Translation("Robot"),
 *   ...
 *   links = {
 *     "config-translation-overview" = "/admin/structure/robot/manage/{robot}/translate"
 *   }
 * )
 */
class Robot extends ConfigEntityBase {

Adding a config-translation-overview link template to your entity's annotation, plus a dependency on the core config_translation module, is what makes a "Translate" operation appear next to each robot in the admin listing. Once that's wired up, an administrator can click through and provide a French, German, or Arabic version of any label-typed property — no code changes required for each new language.

Config translation is layered on top of the base config, not a replacement for it: the original English (or whatever your site's default language is) values stay exactly where they were. Each translation is stored as a language-specific override — deleting a translation never touches the base configuration, and a language with no translation simply falls back to the default.

Quick check: your robot config entity has a neural_system boolean property. Should you mark it type: label so administrators can "translate" it per language? (Answer: no — label is specifically for translatable human-facing text. A boolean has no meaningful translation; it should stay type: boolean, exactly as it would outside any translation context.)

Key takeaways

  • Configuration values need config_translation, not t() — they're never scanned for literal translation calls.
  • Use type: label in a config entity's schema for any property that's genuinely human-facing display text eligible for translation; keep identifiers and non-text values as their real type.
  • A config-translation-overview link template in your entity's annotation is what surfaces the "Translate" operation in the admin UI.
  • Translations are stored as per-language overrides on top of the base configuration — the original values are never overwritten.

Coming up next

Between interface strings and config translation, your module is now genuinely ready for a multilingual audience. Next, we shift from making things work correctly to making them easy to debug when they don't — starting with how to add useful debug logging to your own module code.