Skip to content

Best Practices & Patterns

When to Use

When planning multilingual architecture, deployment workflows, or optimizing translation management.

Decision: Architecture Patterns

Scenario Pattern Why
New multilingual site Config-first approach Version-controlled, repeatable deployments
Entity translation Enable per bundle, not per entity type Allows granular control (some bundles translatable, some not)
URL structure Path prefix with no prefix for default SEO-friendly, user-friendly, CDN-compatible
Language negotiation URL → User → Browser → Selected Explicit first, personal second, automatic last
Field translatability Only translatable fields that need it Performance — translatable fields have storage overhead

Pattern: Config-First Multilingual Setup

Workflow: 1. Add languages via config YAML 2. Configure negotiation via config YAML 3. Enable content translation per bundle via config YAML 4. Export config after UI changes 5. Deploy config consistently across environments

Benefits: - Version-controlled translation settings - Repeatable deployments - Environment parity - Easier testing

Config files to track:

config/sync/
  language.entity.*.yml
  language.types.yml
  language.negotiation.yml
  language.content_settings.*.*.yml
  field.field.*.*.*.yml (translatable: true)

Pattern: Translation Deployment Workflow

Development: 1. Add/change translatable content and config 2. Export base config (English) 3. Add translations via UI or TMGMT 4. Export language-specific config overrides 5. Commit to version control

Staging/Production: 1. Deploy code and config 2. Import base config 3. Import language-specific overrides 4. Clear caches 5. Test language switching

Config sync structure:

config/sync/
  views.view.frontpage.yml          # Base config (English)
  language/
    es/
      views.view.frontpage.yml      # Spanish overrides
    fr/
      views.view.frontpage.yml      # French overrides

Pattern: Performance Optimization

Cache contexts: - Always include languages:language_interface for UI elements - Include languages:language_content for entity displays - Include url.path with path prefix negotiation

Example render array:

$build = [
  '#markup' => $this->t('Translatable text'),
  '#cache' => [
    'contexts' => [
      'languages:language_interface',
      'url.path',
    ],
  ],
];

CDN configuration: - Vary on URL path if using path prefixes - Vary on Accept-Language header if using browser negotiation - Don't cache language switcher blocks without proper contexts

Pattern: Security Considerations

Access control per language:

// Check if user can translate this entity to Spanish
if ($entity->access('update', $account) && $entity->access('translate', $account)) {
  // Allow translation
}

Permission checks: - "translate ENTITY_TYPE" — per entity type - "translate any entity" — global - "translate editable entities" — only entities user can edit - "administer languages" — language configuration

Avoid translation privilege escalation: - Users shouldn't be able to translate content they can't view - Check source content access before allowing translation - Translation metadata (author, status) should respect permissions

Pattern: Accessibility & UX

Language switcher: - Use hreflang attribute on links - Show language names in their own language (Español, not Spanish) - Indicate current language visually - Keyboard-accessible

RTL language support: - Test layout with RTL languages (Arabic, Hebrew) - Use logical properties in CSS (margin-inline-start not margin-left) - Don't hardcode text direction

Content fallback: - If translation doesn't exist, show source content with indicator - Allow users to request translation - Don't show broken layout if one language has more content

Best Practices Summary

DO: - Use config YAML for language setup - Enable translation per bundle, not globally - Mark only necessary fields translatable - Use $this->t() with StringTranslationTrait - Add context to ambiguous strings - Use proper cache contexts - Test with RTL languages - Version control language config

DON'T: - Translate user input with t() - Make all fields translatable by default - Concatenate translated strings - Bypass placeholder sanitization (only @, %, : supported in t()) - Hardcode language codes in business logic - Skip testing language switching - Forget hreflang tags for SEO

See Also