Saltar al contenido principal

¿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étodoCaracterísticas
Archivos PHPArrays clave-valor como lang/es/messages.php. Adecuado si quieres organizar por funcionalidad (errores de validación, etc.)
JSONlang/es.json con las cadenas de traducción como claves. Recomendado para aplicaciones con muchas traducciones
Por defecto una aplicación Laravel no incluye el directorio lang. Para personalizarlo, publícalo con el comando Artisan lang:publish.
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.
// config/app.php
'locale' => env('APP_LOCALE', 'en'),
'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'),
# .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.
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

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.
/lang
    /en
        messages.php
    /es
        messages.php
<?php

// lang/es/messages.php

return [
    'welcome' => '¡Bienvenido a la aplicación!',
    'goodbye' => '¡Hasta luego!',
];
Para idiomas con diferencias regionales, sigue ISO 15897. Por ejemplo, para inglés británico usa en_GB, no en-gb.

JSON

Cuando hay muchas traducciones, se recomienda el formato JSON. Crea archivos con el código del idioma en el directorio lang/.
/lang
    en.json
    es.json
Define las traducciones usando como clave la cadena original (por ejemplo, en inglés).
{
    "Welcome to our application!": "¡Bienvenido a nuestra aplicación!",
    "I love programming.": "Me encanta programar.",
    "Logout": "Cerrar sesión",
    "Dashboard": "Panel"
}
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).

Cuándo elegir uno u otro

  • 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
// lang/es/validation.php
return [
    'required' => 'El campo :attribute es obligatorio.',
    'email' => 'El :attribute debe ser una dirección de correo válida.',
];
  • 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
{{-- Escribe el inglés directamente en Blade --}}
{{ __('Welcome to our application!') }}

Obtener cadenas de traducción

Helper __()

Es el método más habitual. En formato PHP se indica como “archivo.clave”.
// 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.
echo __('messages.not_exists');
// => messages.not_exists (se devuelve la clave)

Uso en plantillas Blade

En Blade usa {{ __() }}.
{{-- Formato PHP --}}
<h1>{{ __('messages.welcome') }}</h1>

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

{{-- Directiva @lang (obsoleta, se mantiene por compatibilidad) --}}
@lang('messages.welcome')
La directiva @lang está obsoleta. Actualmente se recomienda {{ __() }}.

Marcadores (placeholders)

Puedes incluir marcadores de la forma :nombre en las traducciones.
// 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 __().
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.
<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

// lang/es/messages.php
return [
    'apples' => 'Hay una manzana|Hay varias manzanas',
];
También puedes definirlo en JSON.
{
    "There is one apple|There are many apples": "Hay una manzana|Hay varias manzanas"
}
Obtén el texto con la cantidad usando trans_choice().
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.
'apples' => '{0} No hay manzanas|[1,19] Hay algunas manzanas|[20,*] Hay muchas manzanas',
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.
// lang/es/messages.php
return [
    'minutes_ago' => '{1} Hace :value minuto|[2,*] Hace :value minutos',
    'items'       => 'Hay :count elementos',
];
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:
lang/
└── vendor/
    └── hearthfire/
        └── en/
            └── messages.php
// 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.
1

Crear el middleware

php artisan make:middleware SetLocale
2

Implementar el middleware

<?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);
    }
}
3

Registrar el middleware

Regístralo en bootstrap/app.php.
use App\Http\Middleware\SetLocale;

->withMiddleware(function (Middleware $middleware) {
    $middleware->append(SetLocale::class);
})
4

Añadir la ruta para cambiar de idioma

// 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');
5

Añadir botones de cambio en Blade

<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>

Estructura recomendada de archivos de traducción

lang/
├── es.json          # JSON (textos de UI)
├── en.json
├── es/
│   └── validation.php   # PHP (validación)
└── en/
    └── validation.php
// 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?"
}
{{-- 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

MétodoRuta del archivoEjemplo de claveCómo obtenerla
Archivo PHPlang/es/messages.php'welcome' => '...'__('messages.welcome')
JSONlang/es.json"Welcome": "..."__('Welcome')
Función/MétodoUso
__('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
{{-- Básico --}}
{{ __('messages.welcome') }}

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

{{-- Plural --}}
{{ trans_choice('messages.apples', $count) }}
Última modificación el 13 de julio de 2026