> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Internacionalización

> Explicamos cómo gestionar cadenas de traducción con archivos PHP y JSON usando las funciones de internacionalización de Laravel.

## ¿Qué es la internacionalización?

Las funciones de internacionalización de Laravel ofrecen un mecanismo cómodo para obtener cadenas de traducción en varios idiomas.
Se usan para dar soporte a múltiples idiomas dentro de la aplicación.

Hay dos formas de gestionar las traducciones:

| Método           | Características                                                                                                               |
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| **Archivos PHP** | Arrays clave-valor como `lang/es/messages.php`. Adecuado si quieres organizar por funcionalidad (errores de validación, etc.) |
| **JSON**         | `lang/es.json` con las cadenas de traducción como claves. Recomendado para aplicaciones con muchas traducciones               |

<Info>
  Por defecto una aplicación Laravel no incluye el directorio `lang`. Para personalizarlo, publícalo con el comando Artisan `lang:publish`.
</Info>

```shell theme={null}
php artisan lang:publish
```

## Configuración del locale

### Locale por defecto

Configura el idioma por defecto en `config/app.php` con `locale`.
Habitualmente usa la variable `APP_LOCALE` de `.env`.

```php theme={null}
// config/app.php
'locale' => env('APP_LOCALE', 'en'),
'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'),
```

```ini theme={null}
# .env
APP_LOCALE=es
APP_FALLBACK_LOCALE=en
```

`fallback_locale` es el idioma alternativo cuando no existe traducción en el indicado.

### Cambio de locale en tiempo de ejecución

Con `setLocale()` de la fachada `App` puedes cambiar el locale por solicitud.

```php theme={null}
use Illuminate\Support\Facades\App;

Route::get('/greeting/{locale}', function (string $locale) {
    if (! in_array($locale, ['en', 'es', 'fr'])) {
        abort(400);
    }

    App::setLocale($locale);

    // ...
});
```

### Consultar el locale actual

```php theme={null}
use Illuminate\Support\Facades\App;

$locale = App::currentLocale();

if (App::isLocale('es')) {
    // Lógica para español
}
```

## Crear archivos de idioma

### Archivos PHP

Crea archivos PHP en `lang/{código_idioma}/`. Devuelven un array clave-valor.

```text theme={null}
/lang
    /en
        messages.php
    /es
        messages.php
```

```php theme={null}
<?php

// lang/es/messages.php

return [
    'welcome' => '¡Bienvenido a la aplicación!',
    'goodbye' => '¡Hasta luego!',
];
```

<Warning>
  Para idiomas con diferencias regionales, sigue ISO 15897. Por ejemplo, para inglés británico usa `en_GB`, no `en-gb`.
</Warning>

### JSON

Cuando hay muchas traducciones, se recomienda el formato JSON.
Crea archivos con el código del idioma en el directorio `lang/`.

```text theme={null}
/lang
    en.json
    es.json
```

Define las traducciones usando como clave la cadena original (por ejemplo, en inglés).

```json theme={null}
{
    "Welcome to our application!": "¡Bienvenido a nuestra aplicación!",
    "I love programming.": "Me encanta programar.",
    "Logout": "Cerrar sesión",
    "Dashboard": "Panel"
}
```

<Tip>
  Con JSON, la propia frase en inglés es la clave, así que la visualización por defecto en inglés funciona sola. En idiomas sin archivo, se muestra la clave (la cadena original).
</Tip>

### Cuándo elegir uno u otro

<AccordionGroup>
  <Accordion title="Cuándo conviene el formato PHP">
    * Si quieres organizar por funcionalidad (mensajes de validación, etc.)
    * Cuando sobreescribes las traducciones integradas de Laravel (`validation.php`, `auth.php`, etc.)
    * Cuando necesitas una jerarquía de claves

    ```php theme={null}
    // lang/es/validation.php
    return [
        'required' => 'El campo :attribute es obligatorio.',
        'email' => 'El :attribute debe ser una dirección de correo válida.',
    ];
    ```
  </Accordion>

  <Accordion title="Cuándo conviene el formato JSON">
    * Cuando hay muchos textos de UI y pensar claves resulta engorroso
    * Cuando prefieres escribir la plantilla en inglés y traducir el resto con archivos
    * Cuando añades i18n a una aplicación ya existente

    ```blade theme={null}
    {{-- Escribe el inglés directamente en Blade --}}
    {{ __('Welcome to our application!') }}
    ```
  </Accordion>
</AccordionGroup>

## Obtener cadenas de traducción

### Helper `__()`

Es el método más habitual. En formato PHP se indica como "archivo.clave".

```php theme={null}
// Formato PHP: clave 'welcome' de lang/es/messages.php
echo __('messages.welcome');
// => ¡Bienvenido a la aplicación!

// Formato JSON: la clave es la cadena original
echo __('I love programming.');
// => Me encanta programar.
```

Si no existe la traducción, se devuelve la propia clave.

```php theme={null}
echo __('messages.not_exists');
// => messages.not_exists (se devuelve la clave)
```

### Uso en plantillas Blade

En Blade usa `{{ __() }}`.

```blade theme={null}
{{-- Formato PHP --}}
<h1>{{ __('messages.welcome') }}</h1>

{{-- Formato JSON --}}
<button>{{ __('Logout') }}</button>

{{-- Directiva @lang (obsoleta, se mantiene por compatibilidad) --}}
@lang('messages.welcome')
```

<Tip>
  La directiva `@lang` está obsoleta. Actualmente se recomienda `{{ __() }}`.
</Tip>

## Marcadores (placeholders)

Puedes incluir marcadores de la forma `:nombre` en las traducciones.

```php theme={null}
// lang/es/messages.php
return [
    'welcome' => '¡Bienvenido, :name!',
    'greet'   => 'Hola, :Name.',  // Primera letra en mayúscula
    'shout'   => 'HEY :NAME!',    // Todo en mayúsculas
];
```

Pasa un array de sustituciones como segundo argumento de `__()`.

```php theme={null}
echo __('messages.welcome', ['name' => 'Tanaka']);
// => ¡Bienvenido, Tanaka!

echo __('messages.greet', ['name' => 'taro']);
// => Hola, Taro. (Name se pone en mayúscula la inicial)

echo __('messages.shout', ['name' => 'taro']);
// => HEY TARO! (NAME en mayúsculas)
```

Se usa igual en Blade.

```blade theme={null}
<p>{{ __('messages.welcome', ['name' => $user->name]) }}</p>
```

## Plurales

Las reglas de plural varían por idioma. Laravel permite separar singular y plural con `|`.

### Plural básico

```php theme={null}
// lang/es/messages.php
return [
    'apples' => 'Hay una manzana|Hay varias manzanas',
];
```

También puedes definirlo en JSON.

```json theme={null}
{
    "There is one apple|There are many apples": "Hay una manzana|Hay varias manzanas"
}
```

Obtén el texto con la cantidad usando `trans_choice()`.

```php theme={null}
echo trans_choice('messages.apples', 1);
// => Hay una manzana

echo trans_choice('messages.apples', 5);
// => Hay varias manzanas
```

### Plural por rangos

Puedes indicar rangos con detalle.

```php theme={null}
'apples' => '{0} No hay manzanas|[1,19] Hay algunas manzanas|[20,*] Hay muchas manzanas',
```

```php theme={null}
echo trans_choice('messages.apples', 0);
// => No hay manzanas

echo trans_choice('messages.apples', 10);
// => Hay algunas manzanas

echo trans_choice('messages.apples', 50);
// => Hay muchas manzanas
```

### Placeholders con plurales

Con `:count` puedes mostrar la cantidad. El tercer argumento admite sustituciones adicionales.

```php theme={null}
// lang/es/messages.php
return [
    'minutes_ago' => '{1} Hace :value minuto|[2,*] Hace :value minutos',
    'items'       => 'Hay :count elementos',
];
```

```php theme={null}
echo trans_choice('messages.minutes_ago', 5, ['value' => 5]);
// => Hace 5 minutos

echo trans_choice('messages.items', 3);
// => Hay 3 elementos
```

## Sobrescribir archivos de idioma de paquetes

Si un paquete de terceros trae sus propios archivos de idioma, puedes sobrescribirlos colocando el mismo archivo en `lang/vendor/{paquete}/{código_idioma}/`.

Por ejemplo, para personalizar los mensajes en inglés del paquete `skyrim/hearthfire`:

```text theme={null}
lang/
└── vendor/
    └── hearthfire/
        └── en/
            └── messages.php
```

```php theme={null}
// lang/vendor/hearthfire/en/messages.php
return [
    'welcome' => 'Mensaje personalizado',
    // Define solo las claves que sobrescribes; el resto se lee del archivo original
];
```

## Ejemplo práctico: middleware de conmutación de idiomas

Ejemplo de un middleware que ajusta el locale automáticamente según la URL, la sesión o las preferencias del usuario.

<Steps>
  <Step title="Crear el middleware">
    ```shell theme={null}
    php artisan make:middleware SetLocale
    ```
  </Step>

  <Step title="Implementar el middleware">
    ```php theme={null}
    <?php

    namespace App\Http\Middleware;

    use Closure;
    use Illuminate\Http\Request;
    use Illuminate\Support\Facades\App;
    use Symfony\Component\HttpFoundation\Response;

    class SetLocale
    {
        public function handle(Request $request, Closure $next): Response
        {
            // Lista de locales soportados
            $supportedLocales = ['es', 'en'];

            // Si hay locale en la sesión, se usa
            $locale = $request->session()->get('locale');

            // Si no, se mira Accept-Language del navegador
            if (! $locale) {
                $browserLocale = substr($request->getPreferredLanguage($supportedLocales) ?? 'es', 0, 2);
                $locale = in_array($browserLocale, $supportedLocales) ? $browserLocale : 'es';
            }

            App::setLocale($locale);

            return $next($request);
        }
    }
    ```
  </Step>

  <Step title="Registrar el middleware">
    Regístralo en `bootstrap/app.php`.

    ```php theme={null}
    use App\Http\Middleware\SetLocale;

    ->withMiddleware(function (Middleware $middleware) {
        $middleware->append(SetLocale::class);
    })
    ```
  </Step>

  <Step title="Añadir la ruta para cambiar de idioma">
    ```php theme={null}
    // routes/web.php
    Route::post('/locale/{locale}', function (string $locale) {
        if (! in_array($locale, ['es', 'en'])) {
            abort(400);
        }

        session(['locale' => $locale]);

        return back();
    })->name('locale.switch');
    ```
  </Step>

  <Step title="Añadir botones de cambio en Blade">
    ```blade theme={null}
    <div>
        <form method="POST" action="{{ route('locale.switch', 'es') }}">
            @csrf
            <button type="submit">Español</button>
        </form>

        <form method="POST" action="{{ route('locale.switch', 'en') }}">
            @csrf
            <button type="submit">English</button>
        </form>
    </div>
    ```
  </Step>
</Steps>

### Estructura recomendada de archivos de traducción

```text theme={null}
lang/
├── es.json          # JSON (textos de UI)
├── en.json
├── es/
│   └── validation.php   # PHP (validación)
└── en/
    └── validation.php
```

```json theme={null}
// lang/es.json
{
    "Dashboard": "Panel",
    "Login": "Iniciar sesión",
    "Logout": "Cerrar sesión",
    "Welcome, :name!": "¡Bienvenido, :name!",
    "Save": "Guardar",
    "Cancel": "Cancelar",
    "Are you sure?": "¿Estás seguro?"
}
```

```blade theme={null}
{{-- resources/views/layouts/app.blade.php --}}
<nav>
    <a href="{{ route('dashboard') }}">{{ __('Dashboard') }}</a>
    <span>{{ __('Welcome, :name!', ['name' => auth()->user()->name]) }}</span>

    <form method="POST" action="{{ route('logout') }}">
        @csrf
        <button type="submit">{{ __('Logout') }}</button>
    </form>
</nav>
```

## Resumen

<AccordionGroup>
  <Accordion title="Definir cadenas de traducción">
    | Método      | Ruta del archivo       | Ejemplo de clave     | Cómo obtenerla           |
    | ----------- | ---------------------- | -------------------- | ------------------------ |
    | Archivo PHP | `lang/es/messages.php` | `'welcome' => '...'` | `__('messages.welcome')` |
    | JSON        | `lang/es.json`         | `"Welcome": "..."`   | `__('Welcome')`          |
  </Accordion>

  <Accordion title="Funciones y facades habituales">
    | Función/Método                | Uso                                |
    | ----------------------------- | ---------------------------------- |
    | `__('key')`                   | Helper para obtener una traducción |
    | `trans('key')`                | Equivalente a `__()`               |
    | `trans_choice('key', $count)` | Obtener traducción con plurales    |
    | `App::setLocale('es')`        | Cambiar el locale por solicitud    |
    | `App::currentLocale()`        | Consultar el locale actual         |
    | `App::isLocale('es')`         | Comprobar el locale actual         |
  </Accordion>

  <Accordion title="Uso en Blade">
    ```blade theme={null}
    {{-- Básico --}}
    {{ __('messages.welcome') }}

    {{-- Con marcadores --}}
    {{ __('messages.welcome', ['name' => $user->name]) }}

    {{-- Plural --}}
    {{ trans_choice('messages.apples', $count) }}
    ```
  </Accordion>
</AccordionGroup>
