# Localization - [Introduction](#introduction) - [Configuration](#configuration) - [Defining Translation Strings](#defining-translation-strings) - [Retrieving Translations](#retrieving-translations) - [Pluralization](#pluralization) - [Automatic HTTP Headers](#automatic-http-headers) - [Changing Locale](#changing-locale) - [CLI Commands](#cli-commands) - [Development: Hot Restart](#development-hot-restart) ## Introduction Magic provides a Laravel-style localization system that allows you to easily retrieve strings in various languages **without needing BuildContext**. This is a game-changer for Flutter developers who are tired of passing context everywhere. ```dart // Anywhere in your code - no context needed! Text(trans('welcome', {'name': 'Magic'})) // "Welcome, Magic!" Text(trans('auth.failed')) // "Authentication failed." ``` ## Configuration ### Enabling Localization Add `LocalizationServiceProvider` to your providers in `config/app.dart`: ```dart 'providers': [ (app) => LocalizationServiceProvider(app), // ... other providers ], ``` ### Localization Configuration Create `lib/config/localization.dart`: ```dart Map get localizationConfig => { 'localization': { 'locale': 'en', 'fallback_locale': 'en', 'supported_locales': ['en', 'tr', 'es', 'de'], 'auto_detect_locale': true, 'path': 'assets/lang', }, }; ``` ### Configuration Options | Key | Default | Description | |-----|---------|-------------| | `locale` | `'en'` | Default locale | | `fallback_locale` | `'en'` | Fallback when translation is missing | | `supported_locales` | `['en']` | Supported locale codes | | `auto_detect_locale` | `false` | Auto-detect from device on boot | | `path` | `'assets/lang'` | Translation JSON files path | ### Register Assets Add translation files to your `pubspec.yaml`: ```yaml flutter: assets: - assets/lang/en.json - assets/lang/tr.json - assets/lang/es.json ``` ## Defining Translation Strings Translation strings are stored as JSON files in `assets/lang/`: ```json // assets/lang/en.json { "welcome": "Welcome, :name!", "auth": { "login": "Login", "register": "Register", "logout": "Logout", "failed": "These credentials do not match our records.", "throttle": "Too many login attempts. Please try again in :seconds seconds." }, "validation": { "required": "The :attribute field is required.", "email": "The :attribute must be a valid email address.", "min": { "string": "The :attribute must be at least :min characters." } }, "attributes": { "email": "email address", "password": "password", "password_confirmation": "password confirmation" } } ``` ### Nested Keys Use dot notation to access nested translations: ```dart trans('auth.failed') // "These credentials do not match..." trans('validation.required') // "The :attribute field is required." ``` ## Retrieving Translations ### Basic Usage ```dart // Simple translation WText(trans('auth.login')) // With parameters WText(trans('welcome', {'name': user.name})) // Output: "Welcome, John!" // Nested keys WText(trans('auth.throttle', {'seconds': '60'})) // Output: "Too many login attempts. Please try again in 60 seconds." ``` ### The trans() Helper The `trans()` function is globally available: ```dart String trans(String key, [Map? params]) ``` - Returns the translation for the current locale - Falls back to `fallback_locale` if not found - Returns the key itself if no translation exists - Replaces `:param` placeholders with provided values ### Using in Controllers ```dart class AuthController extends MagicController { Future login(Map data) async { final response = await Http.post('/login', data: data); if (response.successful) { Magic.success(trans('common.success'), trans('auth.logged_in')); } else { Magic.error(trans('common.error'), trans('auth.failed')); } } } ``` ## Automatic HTTP Headers When `LocalizationServiceProvider` is registered, Magic automatically attaches two headers to every outgoing HTTP request via `LocalizationInterceptor`. No manual setup is required. | Header | Value | Example | |--------|-------|---------| | `Accept-Language` | Current locale language code | `en`, `tr`, `es` | | `X-Timezone` | Current IANA timezone identifier | `Europe/Istanbul`, `America/New_York` | Both values are resolved at request-time, so they always reflect the locale and timezone that are active when the request is dispatched. ```dart // When the current locale is 'tr' and timezone is 'Europe/Istanbul', // every Http.get/post/put/delete call automatically includes: // // Accept-Language: tr // X-Timezone: Europe/Istanbul // // This works transparently — no configuration needed. final response = await Http.get('/api/profile'); ``` > [!NOTE] > `LocalizationInterceptor` is registered automatically by `LocalizationServiceProvider`. You do not need to add it to your provider list or network configuration manually. ## Changing Locale ### Programmatically ```dart // Change locale at runtime await Lang.setLocale(Locale('tr')); // Get current locale final currentLocale = Lang.locale; // Locale('tr') // Check if locale is supported if (Lang.isSupported(Locale('fr'))) { await Lang.setLocale(Locale('fr')); } ``` ### Language Picker Example ```dart WFormSelect( value: Lang.locale.languageCode, options: [ SelectOption(value: 'en', label: 'English'), SelectOption(value: 'tr', label: 'Türkçe'), SelectOption(value: 'es', label: 'Español'), ], onChange: (code) async { await Lang.setLocale(Locale(code!)); Magic.toast(trans('common.language_changed')); }, label: trans('settings.language'), ) ``` ## CLI Commands ### Create Translation File ```bash dart run magic:magic make:lang fr dart run magic:magic make:lang de dart run magic:magic make:lang tr ``` This command: 1. Creates `assets/lang/.json` with a starter template 2. Automatically registers the asset in `pubspec.yaml` ### Starter Template The generated file includes common translation keys: ```json { "welcome": "Welcome, :name!", "common": { "save": "Save", "cancel": "Cancel", "delete": "Delete", "success": "Success", "error": "Error" }, "auth": { "login": "Login", "register": "Register", "logout": "Logout", "failed": "Authentication failed.", "throttle": "Too many attempts. Please try again in :seconds seconds." }, "validation": { "required": "The :attribute field is required.", "email": "The :attribute must be a valid email address." }, "attributes": { "email": "email", "password": "password" } } ``` > [!TIP] > Keep your translation keys organized by feature (auth, validation, users, etc.) for easier maintenance. ## Development: Hot Restart During development, Magic attempts to bypass Flutter's asset bundle cache so that translation JSON changes can be picked up on **hot restart** (`Shift+R`) without a full rebuild. | Platform | Mechanism | Reliability | |----------|-----------|-------------| | Web (Chrome) | Fetches JSON via HTTP with cache-busting query parameter | Verified | | macOS / Linux / Windows | Reads JSON from disk via `dart:io` | Best-effort (works when `flutter run` sets the working directory to the project root) | | iOS / Android | Attempts disk read via `dart:io`, falls back to `rootBundle` | Limited (asset files are typically not on disk) | This behavior is **debug-mode only** (`kDebugMode`). Release builds use Flutter's standard `rootBundle` with full caching for optimal performance. > [!NOTE] > Hot **reload** (lowercase `r`) only reloads Dart code — it cannot pick up asset changes. Use hot **restart** (`Shift+R`) to see translation updates.