Skip to content

Localization & Multi-language Support

What is Localization vs Internationalization?

Section titled “What is Localization vs Internationalization?”
  • Localization (often abbreviated as l10n) is the process of adapting your application for a specific language or region:
    • For example, translating text, adjusting date formats (04/29/2026 in the US vs 29/04/2026 in France), using the correct currency symbol ($ vs ), and so on.
  • Internationalization (often abbreviated as i18n) is the process of designing your code to support multiple locales in the first place:
    • For example, using translation keys instead of hardcoded strings, structuring translation files, and building helper classes that load the right language at runtime.
  • In practice, you do both together. You internationalize your application once (build the translation infrastructure), then localize it for each supported language (create the actual translation files).
  • Instead of hardcoding text like “Welcome” directly in your views, you use translation keys such as trans('home.welcome') that display different text depending on the user’s language preference. This means the same view template can serve content in English, French, or any other supported language without duplicating code.

When a user visits your application, the following flow determines which language to display:

  1. The user visits the application, optionally with a language parameter such as ?lang=fr.
  2. A LocaleMiddleware detects the preferred language based on the URL parameter, a saved session value, or the default locale.
  3. The application loads the appropriate translation file (e.g., lang/fr/messages.json for French).
  4. Views use trans('home.welcome') instead of hardcoded text.
  5. The translation helper returns the French value (“Bienvenue”) from the loaded file.
  6. The user sees content in their preferred language.

The middleware checks three sources in order:

  1. URL parameter: ?lang=fr (the user explicitly selects a language, highest priority)
  2. Session value: a previously saved preference (persists across page navigation)
  3. Default locale: English (fallback when no parameter or session value exists)

Translation files are organized by locale, with each language in its own folder:

/lang
/en
messages.json
/fr
messages.json

Translations use dot notation to organize keys hierarchically inside JSON files:

{
"home": {
"title": "Home",
"welcome": "Welcome to our store"
},
"nav": {
"home": "Home",
"products": "Products"
}
}

You access these in views with trans('home.welcome') or trans('nav.home').


If a translation key is missing in the requested language, the translator automatically falls back to the default language. For example, if products.new_arrival does not exist in the French translation file, the English value is used instead. This ensures your application never displays a broken or missing label.

Translations can include placeholders for dynamic values. In the JSON file, placeholders are wrapped in % characters:

{
"user": {
"greeting": "Hello, %name%! You have %count% messages."
}
}

When calling the translation function, pass the values as an associative array:

trans('user.greeting', ['%name%' => 'Alice', '%count%' => 3])
// Returns: "Hello, Alice! You have 3 messages."

Symfony organizes translations into domains, which act as separate namespaces. In this course, all translations use the default messages domain. This means all keys live in messages.json files. Larger applications might use separate domains (e.g., validators.json, admin.json) to keep translations organized.


This course uses the Symfony Translation component, a widely adopted PHP library for internationalization. It supports multiple file formats (JSON, YAML, XLIFF, PHP arrays), provides automatic fallback for missing translations, and caches loaded translations for performance. You install it with Composer and configure it through a helper class that you will build in the localization lab.