Internationalization (i18n)

The DÁP Design System includes a built-in internationalization system powered by i18next and lit-i18n, allowing you to customize translations for your application or add support for additional languages.

Built-in Language Support

The design system comes with built-in support for:

  • Hungarian (hu) - Default language
  • English (en)
  • German (de)

The language is automatically detected from:

  1. The lang attribute on the <html> element
  2. Browser language settings
  3. Falls back to Hungarian if neither is available
<!-- Set language on the HTML element -->
<html lang="en">
Custom Language Injection

You can inject custom translations for additional languages or override existing translations by using the exported i18next instance.

Basic Usage
import { i18next } from 'dap-design-system'

// Add German translations
i18next.addResourceBundle('de', 'translation', {
  pager: {
    pageSize: '{{count}} Einträge / Seite',
    first: 'Erste',
    previous: 'Zurück',
    next: 'Weiter',
    last: 'Letzte',
    pageInfo: '{{rangeStart}}-{{rangeEnd}} von {{totalRows}}'
  },
  close: 'Schließen',
  cancel: 'Abbrechen',
  confirm: 'Bestätigen'
}, true, true)

// Switch to German
i18next.changeLanguage('de')
document.documentElement.lang = 'de'
Tree-Shakeable Import

The i18next instance is available from both main and tree-shakeable entry points:

// From components entry
import { i18next, DapDSPager } from 'dap-design-system/components'

// From main entry
import { i18next } from 'dap-design-system'
HTML Example
<!doctype html>
<html lang="fr">
  <head>
    <script type="module">
      import { i18next } from 'dap-design-system'

      // Add French translations
      i18next.addResourceBundle('fr', 'translation', {
        pager: {
          pageSize: '{{count}} Elemente / page',
          first: 'Première',
          previous: 'Précédent',
          next: 'Suivant',
          last: 'Dernière',
          pageInfo: '{{rangeStart}}-{{rangeEnd}} sur {{totalRows}}'
        }
      }, true, true)

      // Set language
      i18next.changeLanguage('fr')
    </script>
  </head>
  <body>
    <dap-ds-pager total-rows="100"></dap-ds-pager>
  </body>
</html>
Day.js Locale Loading

Components that display dates, months, and day names (Calendar, Datepicker, Timepicker) use Day.js for date formatting. While i18next handles text translations, Day.js requires separate locale files for date formatting.

Built-in Languages

The following languages have Day.js locales pre-loaded and work automatically:

  • Hungarian (hu)
  • English (en)
  • German (de)

For these languages, date/month/day names will be localized automatically without any additional setup.

Custom Languages

For custom languages (e.g., French, Italian, Spanish), you must manually import and register the Day.js locale to ensure date formatting works correctly.

Manual Loading (Required)

To add support for a custom language, manually import and register the Day.js locale:

import 'dayjs/locale/fr'  // Import the Day.js locale
import { i18next, registerDayjsLocale } from 'dap-design-system'

// Register the locale with Day.js
registerDayjsLocale('fr')

// Add your i18next translations
i18next.addResourceBundle('fr', 'translation', {
  calendar: {
    today: "aujourd'hui",
    now: 'maintenant',
    nextMonth: 'mois prochain',
    prevMonth: 'mois précédent',
    year: 'année',
    month: 'mois'
  }
}, true, true)

// Switch to French
i18next.changeLanguage('fr')
document.documentElement.lang = 'fr'
Complete Example: Adding French Support
<!doctype html>
<html lang="fr">
  <head>
    <script type="module">
      // Step 1: Import the Day.js locale
      import 'dayjs/locale/fr'
      
      // Step 2: Import the design system functions
      import { i18next, registerDayjsLocale } from 'dap-design-system'

      // Step 3: Register the Day.js locale
      registerDayjsLocale('fr')

      // Step 4: Add i18next translations
      i18next.addResourceBundle('fr', 'translation', {
        pager: {
          pageSize: '{{count}} éléments / page',
          first: 'Première',
          previous: 'Précédent',
          next: 'Suivant',
          last: 'Dernière',
          pageInfo: '{{rangeStart}}-{{rangeEnd}} sur {{totalRows}}'
        },
        calendar: {
          today: "aujourd'hui",
          now: 'maintenant',
          nextMonth: 'mois prochain',
          prevMonth: 'mois précédent',
          year: 'année',
          month: 'mois'
        },
        close: 'Fermer',
        cancel: 'Annuler',
        confirm: 'Confirmer'
      }, true, true)

      // Step 5: Set the language
      i18next.changeLanguage('fr')
      document.documentElement.lang = 'fr'
    </script>
  </head>
  <body>
    <dap-ds-datepicker label="Sélectionner une date"></dap-ds-datepicker>
    <dap-ds-pager total-rows="100"></dap-ds-pager>
  </body>
</html>
Available Day.js Locales

Day.js supports many locales. You can import any available locale:

import 'dayjs/locale/fr'      // French
import 'dayjs/locale/it'      // Italian
import 'dayjs/locale/es'      // Spanish
import 'dayjs/locale/ja'      // Japanese
import 'dayjs/locale/zh-cn'   // Chinese (Simplified)
// ... and many more
Troubleshooting Day.js Locales

If date/month/day names are not localized correctly:

  1. Check if locale is imported: Ensure you've imported the Day.js locale file
  2. Check if locale is registered: Call registerDayjsLocale() after importing
  3. Check console warnings: The system logs warnings if a locale is not registered
  4. Verify locale code: Use the correct 2-letter language code (e.g., 'fr' not 'fr-FR')
// ❌ Wrong - locale code should be 2 letters
registerDayjsLocale('fr-FR')

// ✅ Correct
registerDayjsLocale('fr')
API Reference registerDayjsLocale(localeCode: string): void

Manually register a Day.js locale that has been imported.

Parameters:

  • localeCode - The 2-letter language code (e.g., 'fr', 'it', 'es')

Example:

import 'dayjs/locale/fr'
import { registerDayjsLocale } from 'dap-design-system'

registerDayjsLocale('fr')
Available Translation Keys

Below is the complete list of all translation keys available in the design system. You can override any of these keys when adding custom language support.

{
  "bannerType": {
    "neutral": "Information",
    "informative": "Information",
    "positive": "Success",
    "warning": "Warning",
    "negative": "Error"
  },
  "close": "Close",
  "delete": "delete",
  "cancel": "Cancel",
  "clear": "Clear selection",
  "copy": "Copy",
  "confirm": "Confirm",
  "ok": "Ok",
  "search": "Search",
  "tag": "tag",
  "opensInNewTab": "(opens in new tab)",
  "label": {
    "optional": "(Optional)",
    "tooltip": "Information"
  },
  "errors": {
    "maxLength": "Exceeded the maximum length by {{count}} character"
  },
  "feedback": {
    "info": "Info",
    "success": "Success",
    "error": "Error"
  },
  "listItems": {
    "selected": "kiválasztva",
    "disabled": "nem elérhető"
  },
  "dap-badge": "Prepared within the framework of the Digital Citizenship Program.",
  "dap-badge-large": "The service has been updated within the framework of the Digital Citizenship Program.",
  "loading": "loading",
  "spinner": "Loading...",
  "show": "show",
  "hide": "hide",
  "decrease": "decrease",
  "increase": "increase",
  "tocTitle": "Table of contents",
  "date": "{{val, datetime}}",
  "selected": "selected",
  "calendar": {
    "today": "today",
    "now": "now",
    "nextMonth": "next month",
    "prevMonth": "previous month",
    "year": "year",
    "month": "month"
  },
  "form": {
    "validationMessage": {
      "select": "Please select an option",
      "datepicker": "Please select a date"
    }
  },
  "fileInput": {
    "dragDropArea": "Drag and drop files here or click to upload",
    "dragDropInstructions": "You can also upload files by dragging and dropping them into the area.",
    "files": "Files",
    "uploadButtonLabel": "Upload",
    "browseButtonLabel": "Browse",
    "browse": "Browse files",
    "fileList": "File list",
    "fileSize": "{{size}} MB",
    "upload": "Upload",
    "uploading": "Uploading",
    "dropzone": {
      "label": "Upload files",
      "text": "Drag and drop files here or click to upload",
      "acceptedTypes": "Accepted file types"
    },
    "link": "Link",
    "cancel": "Cancel",
    "delete": "Delete",
    "view": "View",
    "error": {
      "fileSize": "The file size exceeds the maximum limit of {size} MB",
      "fileType": "The file type is not allowed",
      "fileAmount": "The number of files exceeds the maximum limit of {count}",
      "perFileSize": "The file size exceeds the maximum limit of {size} MB"
    },
    "removeDialog": {
      "title": "Remove file",
      "message": "Are you sure you want to remove {{fileName}}?",
      "confirm": "Delete",
      "cancel": "Cancel"
    }
  },
  "pager": {
    "pageSize": "{{count}} items / page",
    "first": "First",
    "previous": "Previous",
    "next": "Next",
    "last": "Last",
    "pageInfo": "{{rangeStart}}-{{rangeEnd}} of {{totalRows}}"
  },
  "datatable": {
    "sortAscending": "Sort ascending",
    "sortDescending": "Sort descending",
    "clearSort": "Clear sort",
    "tableDescription": "Data table with sortable columns",
    "defaultCaption": "Data table",
    "rowSelected": "Row selected",
    "rowDeselected": "Row deselected",
    "sortedBy": "Sorted by {{column}} {{direction}}",
    "pageChanged": "Page {{page}} of {{total}}",
    "noData": "No data available"
  },
  "accordion": {
    "expand": "Expand section",
    "collapse": "Collapse section"
  },
  "official-website-banner": {
    "heading": "An official website of the Government of Hungary.",
    "heading-2": "Secure website",
    "content": "The lock icon or https:// indicates that you have securely connected to the website. Only share personal information on official, secure websites. The certificates for the official websites of the Hungarian Government are issued by Microsec."
  },
  "avatar-group": {
    "overflow-label": "{{count}} more avatar{{count > 1 ? 's' : ''}}"
  },
  "timepicker": {
    "hour": "Hour",
    "minute": "Minute",
    "second": "Second"
  },
  "chip": {
    "remove": "Remove"
  },
  "snackbar": {
    "notifications": "Notifications"
  },
  "combobox": {
    "multiselect": {
      "selected": "{{count}} item{{count > 1 ? 's' : ''}} selected"
    }
  },
  "progress": {
    "loading": "Loading",
    "progress": "Progress: {{percentage}}%"
  }
}
Dynamic Language Switching

You can switch languages at runtime, and all reactive components will update automatically:

import { i18next } from 'dap-design-system'

// Function to switch language
function switchLanguage(lang) {
  i18next.changeLanguage(lang)
  document.documentElement.lang = lang
}

// Example usage
switchLanguage('de')  // Switch to German
switchLanguage('en')  // Switch to English
switchLanguage('hu')  // Switch to Hungarian
Automatic Re-rendering

The following components use reactive translations and will automatically update when the language changes:

  • Calendar
  • Datepicker
  • Timepicker
  • Pager
  • Select (including dropdowns)
  • Modal
  • Callout
  • Official Website Banner
  • Password Input
  • Number Input
  • DAP Badge
  • Copybox Input
  • Banner
  • Spinner

Other components may require a manual re-render or page reload after language changes.

Partial Translations

You don't need to translate everything - you can override only specific components or keys:

import { i18next } from 'dap-design-system'

// Only override specific pager translations
i18next.addResourceBundle('de', 'translation', {
  pager: {
    next: 'Weiter',
    previous: 'Zurück'
  }
}, true, true)

// Other translations will use the library defaults
Multiple Languages

You can register multiple languages at once:

import { i18next } from 'dap-design-system'

const languages = {
  de: {
    pager: {
      pageSize: '{{count}} Einträge / Seite',
      first: 'Erste',
      previous: 'Zurück',
      next: 'Weiter',
      last: 'Letzte'
    }
  },
  fr: {
    pager: {
      pageSize: '{{count}} Elemente / page',
      first: 'Première',
      previous: 'Précédent',
      next: 'Suivant',
      last: 'Dernière'
    }
  }
}

// Register all languages
Object.entries(languages).forEach(([lang, translations]) => {
  i18next.addResourceBundle(lang, 'translation', translations, true, true)
})
Advanced i18next Features

The exported i18next instance supports all standard i18next methods and features:

Pluralization
i18next.addResourceBundle('en', 'translation', {
  items_one: "{{count}} item",
  items_other: "{{count}} items"
}, true, true)
Nesting
i18next.addResourceBundle('en', 'translation', {
  button: {
    save: "Save",
    cancel: "Cancel"
  },
  form: {
    submit: "$t(button.save) Form"
  }
}, true, true)
Formatting
i18next.addResourceBundle('en', 'translation', {
  date: "{{val, datetime}}"
}, true, true)
API Reference i18next Methods
  • addResourceBundle(lng, ns, resources, deep, overwrite) - Add translations for a language
  • changeLanguage(lng) - Switch active language
  • t(key, options) - Translate a key (used internally by components)
  • language - Get current language (property)
  • languages - Get supported languages (property)

See the i18next documentation for the complete API.

Day.js Locale Methods
  • registerDayjsLocale(localeCode) - Manually register a Day.js locale that has been imported
    • Parameters: localeCode (string) - The 2-letter language code (e.g., 'fr', 'it', 'es')
    • Example: registerDayjsLocale('fr')
Best Practices
  1. Set the HTML lang attribute: Always update document.documentElement.lang when changing languages for accessibility
  2. Load translations early: Add custom translations before your components are rendered
  3. Load Day.js locales for custom languages: If using Calendar, Datepicker, or Timepicker with custom languages, import and register the Day.js locale
  4. Use interpolation: Take advantage of i18next's interpolation for dynamic values
  5. Keep translations centralized: Store all your translations in a single location
  6. Test with RTL languages: If supporting RTL languages, test with the dir attribute
Examples Complete Language Setup
// Import Day.js locales for custom languages
import 'dayjs/locale/fr'
import 'dayjs/locale/it'

import { i18next, registerDayjsLocale } from 'dap-design-system'

// Register Day.js locales for custom languages
registerDayjsLocale('fr')
registerDayjsLocale('it')

// Define all your translations
const translations = {
  de: {
    pager: { /* German translations */ },
    calendar: { /* German translations */ },
    // ... more translations
  },
  fr: {
    pager: { /* French translations */ },
    calendar: { /* French translations */ },
    // ... more translations
  },
  it: {
    pager: { /* Italian translations */ },
    calendar: { /* Italian translations */ },
    // ... more translations
  }
}

// Initialize all languages
Object.entries(translations).forEach(([lang, trans]) => {
  i18next.addResourceBundle(lang, 'translation', trans, true, true)
})

// Set initial language
const userLang = navigator.language.slice(0, 2)
i18next.changeLanguage(userLang)
document.documentElement.lang = userLang
Language Switcher Component
<dap-ds-select id="language-selector">
  <dap-ds-option-item value="en">English</dap-ds-option-item>
  <dap-ds-option-item value="hu">Magyar</dap-ds-option-item>
  <dap-ds-option-item value="de">Deutsch</dap-ds-option-item>
</dap-ds-select>

<script type="module">
  import { i18next } from 'dap-design-system'

  const selector = document.querySelector('#language-selector')
  selector.value = i18next.language

  selector.addEventListener('dds-change', (e) => {
    const newLang = e.detail.value
    i18next.changeLanguage(newLang)
    document.documentElement.lang = newLang
  })
</script>
Troubleshooting Translations not updating

If translations don't update after calling changeLanguage():

  1. Check component support: Ensure the component uses reactive translations (see list above)
  2. Verify language was added: Make sure you called addResourceBundle() before switching
  3. Check the HTML lang attribute: Update document.documentElement.lang along with changeLanguage()
Missing translations

If some text isn't translated:

  1. Check translation keys: Ensure you're using the correct key structure
  2. Add missing keys: Some components may use keys not listed in the common section
  3. Check console: i18next logs missing translation keys in development mode
Date/month/day names not localized

If date/month/day names in Calendar, Datepicker, or Timepicker components are not localized:

  1. Check if Day.js locale is imported: Ensure you've imported the locale file (e.g., import 'dayjs/locale/fr')
  2. Check if Day.js locale is registered: Call registerDayjsLocale() after importing
  3. Check console warnings: The system logs warnings if a locale is not registered
  4. Verify locale code: Use the correct 2-letter language code matching your i18next language
Related Resources