Skip to content

Tema 21: Localización (Locale) en Laravel 12


21.1 Introducción a la localización e internacionalización

Cuando desarrollamos aplicaciones web para usuarios que hablan diferentes idiomas, es fundamental adaptar los textos, formatos y contenidos para cada uno. Este proceso se llama localización (o locale), mientras que la capacidad de preparar la aplicación para varios idiomas y regiones se denomina internacionalización (i18n).

Beneficios:

  • Aumenta el alcance de la aplicación.
  • Mejora la experiencia del usuario.
  • Facilita la adaptación a diferentes mercados.

Conceptos clave:

  • Locale: configuración que indica el idioma y región usados (ejemplo: en, es, val).
  • Traducciones: textos en diferentes idiomas almacenados en archivos específicos.
  • Archivos de idioma: archivos PHP que contienen los textos traducidos organizados en claves.

21.2 Estructura y ubicación de archivos de idioma en Laravel 12

En Laravel 12, la carpeta que contiene los archivos de idiomas es lang/ y está directamente en la raíz del proyecto, no dentro de resources/ como en versiones anteriores.

¿Está creada la carpeta lang por defecto?

Al crear un proyecto Laravel 12 nuevo, esta carpeta puede no estar creada inicialmente porque Laravel no incluye los archivos de idioma directamente en el proyecto para reducir peso y facilitar personalización.


Crear manualmente la carpeta y los archivos

Para crear la estructura básica:

  • En la raíz del proyecto crea una carpeta llamada lang.
  • Dentro crea subcarpetas para cada idioma que quieras soportar, por ejemplo:

Estructura básica carpetas idioma

lang/
    en/
    es/
    val/
  • En cada carpeta, crea archivos PHP con las traducciones, por ejemplo messages.php. Pero es recomendable instalarlos desde el paquete de Laravel o el paquete laravel-lang/lang. Sigue el siguiente apartado.

Uso de paquete para idiomas predefinidos

También puedes instalar un paquete muy popular que contiene traducciones para muchos idiomas:

Instalación paquete laravel-lang

composer require laravel-lang/lang

Y luego publicar los archivos con:

Publicar archivos de idioma

php artisan lang:publish

Esto crea la carpeta lang con las traducciones disponibles en inglés (en) automáticamente. En la carpeta lang/en encontrarás archivos como:

  • 'auth.php': Mensajes de autenticación.
  • 'pagination.php': Mensajes de paginación.
  • 'validation.php': Mensajes de validación.
  • 'passwords.php': Mensajes de recuperación de contraseña.

Puedes echar un vistazo a estos archivos para ver cómo están estructurados y qué claves de traducción contienen. Por ejemplo en el fichero password.php puedes encontrar claves como:

Clave Mensaje
reset "Your password has been reset!"
sent "We have emailed your password reset link!"
throttled "Please wait before retrying."
user "We can't find a user with that email address."
token "This password reset token is invalid."

21.3 Configuración inicial del idioma por defecto

Para indicarle a Laravel qué idioma cargar por defecto y cuál usar como respaldo:

Abre el archivo config/app.php y modifica estas líneas:

config/app.php (fragmento)

'locale' => 'en',          // Idioma por defecto
'fallback_locale' => 'en', // Idioma de respaldo en caso de que falten traducciones

Cuando Laravel no encuentra una traducción en el idioma activo, busca en el idioma fallback.


21.4 Uso de traducciones en Laravel

Laravel ofrece varias formas de acceder a las traducciones.

Funciones y helpers más usados

  • __('clave'): función global para obtener la traducción de una clave.
  • trans('clave'): similar a __(), pero puede aceptar más opciones.
  • En vistas Blade se usa @lang('clave').

Si por ejemplo tenemos el siguiente archivo de idioma messages.php:

lang/en/messages.php

1
2
3
4
5
6
<?php

return [
    'welcome' => 'Welcome to our website!',
    'contact' => 'Contact us',
];

Podemos acceder a las traducciones de la siguiente manera:

Ejemplo uso en PHP

echo __('messages.welcome'); // Muestra: Welcome to our website!
echo __('messages.contact'); // Muestra: Contact us

O en una vista Blade:

Ejemplo uso en Blade

<h1>{{ trans('messages.welcome') }}</h1>
<a href="/contact">{{ __('messages.contact') }}</a>

21.5 Creación de archivos de idioma personalizados

Para organizar mejor las traducciones, puedes crear tus propios archivos.

Pasos:

  1. Dentro de la carpeta del idioma (ej. lang/en/) crea un archivo PHP con nombre descriptivo, por ejemplo, landing.php.

  2. Define claves y textos dentro:

lang/en/landing.php

1
2
3
4
5
6
7
8
<?php

return [
    'title' => 'Landing Page',
    'description' => 'Welcome to our landing page.',
    'button_contact' => 'Contact Us',
    'link' => 'home',
];

lang/en/contact.php

1
2
3
4
5
6
7
<?php
    return [
        'title' => 'Contact Us',
        'email_label' => 'Email',
        'message_label' => 'Message',
        'submit_button' => 'Send',
];
  1. Ahora creamos las vistas que van a utilizar estas traducciones.

Ejemplo resources/views/landing/index.blade.php:

Uso en Blade

1
2
3
<h1>{{ __('landing.title') }}</h1>
<p>{{ __('landing.description') }}</p>
<a href="/contact">{{ __('landing.button_contact') }}</a>

Ejemplo resources/views/landing/contact.blade.php:

Uso en Blade

1
2
3
4
5
6
7
8
<h1>{{ __('contact.title') }}</h1>
<form>
    <label>{{ __('contact.email_label') }}</label>
    <input type="email" name="email">
    <label>{{ __('contact.message_label') }}</label>
    <textarea name="message"></textarea>
    <button type="submit">{{ __('contact.submit_button') }}</button>
</form>

Y añadimos las rutas en routes/web.php:

web.php

1
2
3
4
use Illuminate\Support\Facades\Route;

Route::view('/', 'landing.index')->name('landing');
Route::view('/contact', 'landing.contact')->name('contact');

Si comprobamos el funcionamiento de la aplicación, veremos que los textos se muestran correctamente en inglés. Ya que son los textos que hemos definido en el archivo lang/en/landing.php y lang/en/contact.php. Cualquier cambio en los textos de estos archivos se reflejará automáticamente en la aplicación.

Pero claro, seguimos teniento un sitio monolingüe. En el próximo apartado vamos a ver cómo añadir más idiomas y poder gestionar el idioma de la aplicación.

21.6 Gestión dinámica del idioma

Lo primero que necesitaremos es definir una ruta que nos permita cambiar el idioma de la aplicación. Esta ruta se encargará de recibir el idioma seleccionado por el usuario y guardarlo en la sesión o en una cookie.

Permitir al usuario seleccionar idioma

Para cambiar el idioma de forma dinámica:

  • Crea una ruta o acción que reciba el idioma seleccionado. Por ejemplo, una ruta lang/{locale}.
    • En esta ruta, verifica si el idioma es válido (por ejemplo, en, es, val).
  • Guarda esta selección en la sesión o en una cookie.
1
2
3
4
5
6
7
Route::get('lang/{locale}', function ($locale, Request $request) {
        $available = ['en', 'es', 'val'];
        if (in_array($locale, $available)) {
            $request->session()->put('locale', $locale);
        }
        return redirect()->back();
    })->name('lang.switch');

Como podemos ver esta ruta se encarga de recibir el idioma seleccionado y guardarlo en la sesión. Luego redirige al usuario a la página anterior. Lo guarda en la sesión para que la selección persista entre diferentes peticiones.


Middleware para aplicar el idioma

Ahora crearemos middleware que se encargue de gestionar el idioma de la aplicación. Este middleware se ejecutará en cada petición y establecerá el idioma según la sesión o cookie. De esta manera nos liberamos de tener que establecer el idioma en cada controlador o vista.

Crea un middleware que se ejecute en cada petición y establezca el idioma según la sesión o cookie.

php artisan make:middleware SetLocale

Ejemplo básico:

Middleware SetLocale.php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Session;

class SetLocale
{
    public function handle(Request $request, Closure $next)
    {
        if (Session::has('locale')) {
            App::setLocale(Session::get('locale'));
        }
        return $next($request);
    }
}

Y registramos el middleware en app/bootstrap/app.php:

app/Http/Kernel.php

use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
    )
    ->withMiddleware(function (Middleware $middleware) {
        // Aquí agregas el middleware LocaleMiddleware
        $middleware->group('web', [
            // Mantener el middleware que inicia la sesión primero:
            \Illuminate\Session\Middleware\StartSession::class,
            // Aquí agregas el middleware LocaleMiddleware
            \App\Http\Middleware\SetLocale::class,
        ]);
    })
    ->withExceptions(function (Exceptions $exceptions) {
        //
    })->create();

Middleware StartSession

Asegúrate de que el middleware StartSession se ejecute antes que el middleware locale. Esto es importante porque el middleware StartSession debe establecer la sesión antes de que el middleware locale intente acceder a ella.


Creación de layout para las vistas

Ahora vamos a crear nuestro layout para añadir un menú que sea visible en todas las páginas. Este menú contendrá el selector de idioma y los enlaces a las diferentes páginas.

Ejemplo resources/views/_layouts/app.blade.php:

app.blade.php

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>
        @yield('title', 'Default Title')
        - {{ config('app.name') }}
    </title>
</head>
<body>
    <nav>
        <a href="{{ route('landing', 'val') }}">{{ __('landing.link')}}</a>
        <a href="{{ route('lang.switch', 'en') }}">English</a> |
        <a href="{{ route('lang.switch', 'es') }}">Español</a> |
        <a href="{{ route('lang.switch', 'val') }}">Valencià</a>
    </nav>
    <h2>Idioma seleccionado: {{ Session::has('locale') ? Session::get('locale') : "Nothing"}}</h2>
    @yield('content')
</body>
</html>

Modificación de las vistas para que utilicen el layout

Modificamos las vistas landing/index.blade.php y landing/contact.blade.php para que utilicen el layout que acabamos de crear.

landing/index.blade.php

1
2
3
4
5
6
7
8
@extends('_layouts.app')
@section('title', __('landing.title'))

@section('content')
    <h1>{{ __('landing.title') }}</h1>
    <p>{{ __('landing.description') }}</p>
    <a href="{{ route('contact') }}">{{ __('landing.button_contact') }}</a>
@endsection

Ejemplo resources/views/landing/contact.blade.php:

landing/contact.blade.php

@extends('_layouts.app')
@section('title', __('contact.title'))

@section('content')
    <h1>{{ __('contact.title') }}</h1>
    <form>
        <label>{{ __('contact.email_label') }}</label>
        <input type="email" name="email">
        <label>{{ __('contact.message_label') }}</label>
        <textarea name="message"></textarea>
        <button type="submit">{{ __('contact.submit_button') }}</button>
    </form>
@endsection

Comprueba que funciona, que los textos se muestran correctamente y que el selector de idioma cambia el idioma de la página. Podrás observar que al cambiar el idioma, la página se recarga pero los texto se mantienen en ingñlés en. Esto es porque no hemos añadido los ficheros de idioma en español y valenciano. Al no encontrar los textos en el idioma seleccionado, Laravel recurre al idioma por defecto que es el inglés (fallback_locale).

5. Verificar que al cambiar de página se mantiene el idioma

  • Gracias al middleware que establece el idioma desde la sesión, el idioma no se pierde al cambiar entre / y /contact.
  • Los textos de ambas páginas se mostrarán en el idioma seleccionado.

6. Añadir los ficheros de idioma en español y valenciano

  • Crea las carpetas es y val dentro de lang/.
  • Copia los archivos landing.php y contact.php de en/ a es/ y val/.
  • Traduce los textos al español y valenciano.
  • Verifica que al cambiar el idioma, los textos se muestran correctamente.

21.9 Buenas prácticas y consideraciones

  • Mantener organizada la carpeta lang.
  • Documentar y usar claves claras y consistentes.
  • Para textos largos o dinámicos, usar JSON o bases de datos si es necesario.
  • Considerar paquetes externos para facilitar traducciones.
  • Probar siempre todos los idiomas y páginas.

21.10 Ejercicios propuestos

Ejercicio 1: Crear archivo de idioma personalizado

  • Crea una vista landing.services y añade servicios como venta d entradas, venta de merchandising, venta de entradas VIP.
  • Crea el archivo services.php en lang/en/. Y carga todos los textos que necesites en la vista.
  • Añade el enlace a la vista en el menú de navegación.
  • Crea el archivo services.php en lang/es/ y lang/val/ y traduce los textos.
  • Verifica que al cambiar el idioma, los textos se muestran correctamente.