Índice de contenido
- 1. Qué es la localización en Laravel y por qué es importante
- 2. Configuración inicial de localización en Laravel
- 3. Creación de archivos de idioma (PHP y JSON) y cadenas de textos para la traducción
- 4. Mostrar traducciones en vistas y controladores
- 5. Cómo cambiar el idioma dinámicamente en tu app
- 6. Localización avanzada y buenas prácticas
- Middleware para Verificar el prefijo del idioma es/en en Laravel
- Traducciones en Laravel Inertia
- La solución manual
- La Solución Elegante: Integración Bilateral con un Paquete
- Instalación en el Backend (PHP)
- Configuración de los Archivos de Idioma (Lang)
- Lógica en las Rutas y el Controlador
- El Controlador de Idiomas
- Consumo de Traducciones en el Componente Vue
- Seleccionar Idioma (Lenguaje) en Laravel Livewire
- El componente y el manejo del lenguaje
- Transición y recarga de interfaz
- Middleware para mantener el idioma
- Archivos de idioma
- 7. Conclusión: experiencia y recomendaciones prácticas
- ❓Preguntas frecuentes
Con las funciones que ofrece Laravel de manera nativa para el manejo de la localización, es decir, el lugar donde está siendo consumida la aplicación y con esto, poder ofrecer otro servicio, como lo es el de la traducción de textos de manera automática en base a la localización o por selección del usuario. En este apartado, veremos implementar ambos temas.
Ya sabemos como manejar Excepciones personalizadas en Laravel.
La localización en Laravel es una de esas funciones que, una vez las dominas, te permiten llevar tus aplicaciones a otro nivel. En mi experiencia, no solo mejora la usabilidad, sino que también abre la puerta a nuevos mercados y usuarios. A lo largo de esta guía te contaré cómo configurar, crear y gestionar traducciones en Laravel, apoyándome en ejemplos reales y buenas prácticas.
1. Qué es la localización en Laravel y por qué es importante
La localización (L10n) es el proceso de adaptar tu aplicación a diferentes idiomas, monedas o formatos regionales. Laravel lo integra de forma nativa, facilitando tanto la traducción de textos como la detección automática del idioma del usuario.
En uno de mis primeros proyectos multi-idioma, descubrí que bastaba una configuración mínima para ofrecer la interfaz completa en inglés y español. Esta capacidad es especialmente útil si tu aplicación tiene usuarios internacionales o si planeas expandirte fuera de tu mercado inicial.
Diferencia clave:
- Internacionalización (i18n): preparar la app para manejar múltiples idiomas.
- Localización (L10n): adaptar los textos y configuraciones específicas a cada idioma.
2. Configuración inicial de localización en Laravel
Al crear un proyecto Laravel nuevo, notarás que la carpeta /lang no existe. En mis primeros intentos pensé que era un error, pero en realidad Laravel espera que la generes tú con un simple comando:
$ php artisan lang:publishEste comando crea la carpeta /lang y publica los archivos de idioma base que Laravel usa internamente.
El comando lang:publish creará el directorio lang en su aplicación y publicará el conjunto predeterminado de archivos de idioma utilizados por Laravel:
Dentro de config/app.php puedes definir el idioma por defecto modificando la línea:
'locale' => 'es',Si trabajas con múltiples idiomas, también es recomendable ajustar el fallback (idioma de respaldo):
'fallback_locale' => 'en',De esta forma, si falta una traducción en español, Laravel mostrará el texto en inglés.
3. Creación de archivos de idioma (PHP y JSON) y cadenas de textos para la traducción
Laravel proporciona dos formas de gestionar cadenas de traducción que son las empleadas para mostrar los textos traducidos de nuestra aplicación en diferentes idiomas; en ambos casos, debemos de crear una carpeta para almacenar los mismos:
/lang
En donde creamos nuestro archivos de traducción ya sean PHP:
/lang /en messages.php /es messages.php
O en JSONs:
/lang en.json es.json
Usaremos el formato de los archivos en PHP; puedes crear tantos como quieras y modularizar los mensajes según nuestras preferencias.
Finalmente, somos nosotros los que debemos de definir las cadenas traducidas y personalizar las mismas cambiando el texto predefinido por la aplicación y creando las propias; mediante la pareja de clave/valor, de indica la clave y la traducción; por ejemplo:
lang/en/messages.php
<?php
return [
'welcome' => 'Welcome to our application!',
];lang/es/messages.php
<?php
return [
'welcome' => 'Bienvenido a nuestra aplicación!',
];4. Mostrar traducciones en vistas y controladores
Una vez creadas las cadenas, puedes mostrarlas con la función __() o con la directiva @lang en Blade.
echo __('messages.welcome')Como puedes apreciar, debemos de colocar el nombre del archivo y la clave como clave para la función de __() la cual devuelve el texto traducido; este esquema, lo podemos emplear tanto en el controlador y similar como en la vista.
Errores comunes:
- Usar una clave incorrecta (Laravel devolverá la clave literal).
- No publicar la carpeta /lang.
- Olvidar limpiar la caché con php artisan config:clear después de cambiar archivos de idioma.
5. Cómo cambiar el idioma dinámicamente en tu app
Para cambiar el idioma durante la navegación, puedes hacerlo manualmente o mediante un middleware.
Generar middleware de localización
$ php artisan make:middleware LocalizationY en el método handle:
public function handle($request, Closure $next)
{
$locale = $request->get('lang', config('app.locale'));
app()->setLocale($locale);
return $next($request);
}Así, si el usuario visita /home?lang=es, la aplicación cambiará automáticamente al español.
En uno de mis proyectos, añadí un selector de idioma en la cabecera del sitio y guardé la preferencia en sesión para mantener la elección en cada visita:
session(['locale' => $locale]);
app()->setLocale(session('locale', 'es'));6. Localización avanzada y buenas prácticas
Cuando ya dominas la configuración básica, puedes avanzar hacia escenarios más complejos:
- Traducción de contenido dinámico (desde la base de datos): usando paquetes como Spatie Translatable.
- Rutas traducidas: con Laravel Localization.
- SEO multi-idioma: implementando etiquetas <link rel="alternate" hreflang="...">.
Además, Laravel permite pluralización y sustitución de variables en las cadenas:
'notifications' => '{0} No tienes notificaciones|{1} Tienes una notificación|[2,*] Tienes :count notificaciones',Con esta estructura, el framework elige automáticamente la traducción correcta según el número.
Middleware para Verificar el prefijo del idioma es/en en Laravel
El siguiente desarrollo que vamos a realizar, es mediante un middleware detectar el idioma configurado por el usuario y establecerlo mediante las cadenas de textos de traducciones que definimos antes (antes vimos como utilizar las las Colas y Trabajos), además, también definiremos las siglas o etiqueta del lenguaje en la URL para devolver la traducción acorde; para ello:
$ php artisan make:middleware LanguagePrefixMiddlewareQue tendrá el siguiente contenido:
app\Http\Middleware\LanguagePrefixMiddleware.php
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class LanguagePrefixMiddleware
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
$language = $request->segment(1);
if(!in_array($language,['es','en'])){
return redirect('/es/blog');
}
app()->setLocale($language);
return $next($request);
}
}Con el código anterior, solamente se redirecciona cuando las etiquetas de idioma no están disponibles en la URL, luego, se establece el lenguaje correspondiente.
Y usamos el middleware en las rutas:
Route::get('/{lang}/mi-ruta', 'MiControlador@miMetodo');Por ejemplo, en nuestra aplicación:
function routeBlog() {
Route::get('', [BlogController::class, 'index'])->name('blog.index');
Route::get('detail/{id}', [BlogController::class, 'show'])->name('blog.show');
}
Route::group(['prefix' => '{locale}/blog','middleware' => LanguagePrefixMiddleware::class], function () {
routeBlog();
});
Route::group(['prefix' => 'blog','middleware' => LanguagePrefixMiddleware::class], function () {
function () {
routeBlog();
});Como puedes apreciar, creamos una función routeBlog() para agrupar las rutas a la cuales queremos verificar el uso del lenguaje, en este ejemplo, las del blog, para que se pueda consumir mediante el locale:
- es/blog/*
- en/blog/*
Y sin el locale, y en este caso, se redirecciona al idioma español según la redirección definida en el middleware:
- blog/*
Traducciones en Laravel Inertia
Vamos a conocer como puedes crear una aplicación multidioma tal cual lo haríamos en un desarrollo de Laravel tradicional, pero con la particularidad de que aquí estamos empleando Inertia.js con Vue. Cabe destacar que esta misma implementación te va a servir exactamente igual si en tu arquitectura utilizas React o Svelte.
En Laravel base, es tan simple como hacer algo como:
__('messages.welcome')O
trans('messages.welcome')Recordemos que en el esquema nativo de Laravel creamos nuestros archivos de traducción y llamamos a los textos mediante las funciones de ayuda __() o trans(). El problema radica en que ya no estamos trabajando con archivos Blade, sino con archivos .vue, .jsx o .svelte. Al cambiar de entorno, perdemos el acceso directo a esas funciones de servidor.
La solución manual
Anteriormente, la forma de abordar esto era creando un Middleware personalizado.
Tenías que leer los archivos de traducción en el backend y metérselos a Inertia::share() para inyectar toda esa carga de textos de manera global en el cliente.
Si bien este enfoque funciona (es muy similar a lo que hicimos en clases anteriores con los mensajes Flash), la realidad es que es bastante engorroso y pesado. Estar pasando manualmente colecciones masivas de traducciones de un lado a otro puede sobrecargar innecesariamente el tamaño de la página y ensuciar la lógica de negocio.
La Solución Elegante: Integración Bilateral con un Paquete
Para evitarnos ese trabajo manual en las sombras, vamos a emplear un paquete especializado que conecta perfectamente el mundo de Laravel con el ecosistema de frontend (Vue/React/Svelte) de forma totalmente transparente.
Para implementar esta solución, requerimos cubrir dos instalaciones (una para el servidor y otra para el cliente):
Instalación en el Backend (PHP)
Ejecutamos el comando de Composer para requerir el paquete en nuestro núcleo de Laravel:
$ composer require erag/laravel-lang-sync-inertiaLuego, generamos la carpeta de lenguaje:
$ php artisan lang:publishPublicamos el archivo de configuración:
$ php artisan erag:install-langE instalamos el paquete de Node, que es el que integramos desde los Vue:
$ npm install @erag/lang-sync-inertiaConfiguración de los Archivos de Idioma (Lang)
El primer paso es tener listos nuestros archivos de traducción en el backend. Esto se hace de la forma tradicional de Laravel: creamos la carpeta lang/ en la raíz (o dentro de resources/lang/ según tu versión) y generamos las subcarpetas para cada idioma, como es/ o en/, con sus respectivos archivos de retorno de arrays.
Como este procedimiento ya lo cubrimos a detalle en el curso básico de Laravel, lo daré un poco por sentado, pero recuerda que es el estándar del framework para indexar cadenas de texto.
lang\es\messages.php
<?php
return [
'title' => 'Demo de Localización',
'welcome' => '¡Bienvenido a nuestra aplicación!',
'description' => 'Esta es una demostración de localización con Laravel Inertia.',
'greeting' => '¡Hola, :name!',
'select_language' => 'Seleccionar Idioma',
'current_language' => 'Idioma Actual',
'switch_to' => 'Cambiar a',
'content' => [
'intro' => 'Bienvenido a la página de demostración de localización.',
'features' => 'Características',
'feature_1' => 'Fácil gestión de traducciones',
'feature_2' => 'Sincronización automática con el frontend',
'feature_3' => 'Soporte para múltiples idiomas',
'footer' => '¡Gracias por visitarnos!',
],
'buttons' => [
'submit' => 'Enviar',
'cancel' => 'Cancelar',
'save' => 'Guardar',
'back' => 'Volver',
],
];
lang\en\messages.php
<?php
return [
'title' => 'Localization Demo',
'welcome' => 'Welcome to our application!',
'description' => 'This is a demonstration of Laravel Inertia localization.',
'greeting' => 'Hello, :name!',
'select_language' => 'Select Language',
'current_language' => 'Current Language',
'switch_to' => 'Switch to',
'content' => [
'intro' => 'Welcome to the localization demo page.',
'features' => 'Features',
'feature_1' => 'Easy translation management',
'feature_2' => 'Automatic sync with frontend',
'feature_3' => 'Support for multiple languages',
'footer' => 'Thank you for visiting!',
],
'buttons' => [
'submit' => 'Submit',
'cancel' => 'Cancel',
'save' => 'Save',
'back' => 'Go Back',
],
];
Lógica en las Rutas y el Controlador
Para controlar el cambio de idioma dinámico, preparé un par de rutas en nuestro archivo web.php: una encargada de renderizar la vista de inicio (index) y otra diseñada específicamente para procesar la acción de cambiar la localización (locale).
El Controlador de Idiomas
Vayamos al controlador para ver cómo gestionamos la petición del cliente:
app\Http\Controllers\LocalizationController.php
<?php
namespace App\Http\Controllers;
use Inertia\Inertia;
class LocalizationController extends Controller
{
public function index()
{
$locale = session('locale', 'en');
app()->setLocale($locale);
syncLangFiles('messages');
return Inertia::render('localization/Index');
}
public function changeLanguage(string $locale)
{
$availableLocales = ['en', 'es'];
if (! in_array($locale, $availableLocales)) {
$locale = 'en';
}
session(['locale' => $locale]);
app()->setLocale($locale);
return to_route('localization.index');
}
}
routes\web.php
// LOCALIZATION
Route::prefix('localization')->group(function () {
Route::get('/', [LocalizationController::class, 'index'])->name('localization.index');
Route::get('/lang/{locale}', [LocalizationController::class, 'changeLanguage']);
});Consumo de Traducciones en el Componente Vue
Una vez que el paquete está configurado e inyectado en la instancia de nuestra aplicación de Vue, el uso en los componentes es sumamente limpio. Disponemos de las dos sintaxis clásicas, por lo que puedes elegir y emplear la que más te guste en tus plantillas:
resources\js\pages\localization\Index.vue
<script setup>
import { ref } from 'vue';
import { router } from '@inertiajs/vue3';
import { vueLang } from '@erag/lang-sync-inertia';
const { trans, __ } = vueLang();
const currentLocale = ref('en');
const availableLocales = [
{ code: 'en', name: 'English', flag: '' },
{ code: 'es', name: 'Español', flag: '' },
];
function changeLocale(locale) {
currentLocale.value = locale;
router.visit(`/localization/lang/${locale}`, {
preserveState: true,
});
}
</script>
<template>
<div class="min-h-screen p-8">
<div class="mx-auto max-w-4xl">
<div class="mb-8">
<h1 class="mb-2 text-3xl font-bold text-gray-800">
{{ __('messages.title') }}
</h1>
<p class="text-gray-600">
{{ __('messages.description') }}
</p>
</div>
<div class="mb-8 rounded-lg p-6 shadow">
<h2 class="mb-4 text-lg font-semibold">
{{ __('messages.current_language') }}
</h2>
<div class="flex gap-2">
<button
v-for="locale in availableLocales"
:key="locale.code"
@click="changeLocale(locale.code)"
class="rounded px-4 py-2 transition-colors"
:class="
currentLocale === locale.code
? 'bg-blue-500 text-white'
: 'bg-gray-200 text-gray-700 hover:bg-gray-300'
"
>
{{ locale.flag }} {{ locale.name }}
</button>
</div>
</div>
<div class="rounded-lg bg-white p-6 shadow">
<h2 class="mb-4 text-xl font-bold text-gray-800">
{{ trans('messages.welcome') }}
</h2>
<p class="mb-4 text-gray-600">
{{ __('messages.content.intro') }}
</p>
<div class="mb-6">
<h3 class="mb-3 text-lg font-semibold text-gray-700">
{{ __('messages.content.features') }}
</h3>
<ul class="space-y-2">
<li class="flex items-center gap-2">
<span class="text-green-500">✓</span>
{{ __('messages.content.feature_1') }}
</li>
<li class="flex items-center gap-2">
<span class="text-green-500">✓</span>
{{ __('messages.content.feature_2') }}
</li>
<li class="flex items-center gap-2">
<span class="text-green-500">✓</span>
{{ __('messages.content.feature_3') }}
</li>
</ul>
</div>
<div class="mb-6">
<h3 class="mb-3 text-lg font-semibold text-gray-700">
{{ trans('messages.greeting', { name: 'Developer' }) }}
</h3>
</div>
<div class="flex gap-4">
<button
class="rounded bg-blue-500 px-4 py-2 text-white hover:bg-blue-600"
>
{{ __('messages.buttons.submit') }}
</button>
<button
class="rounded bg-gray-200 px-4 py-2 text-gray-700 hover:bg-gray-300"
>
{{ __('messages.buttons.cancel') }}
</button>
<button
class="rounded bg-green-500 px-4 py-2 text-white hover:bg-green-600"
>
{{ __('messages.buttons.save') }}
</button>
</div>
<div class="mt-8 border-t pt-4">
<p class="text-gray-500">
{{ __('messages.content.footer') }}
</p>
</div>
</div>
<div class="mt-8 rounded-lg bg-blue-50 p-6">
<h3 class="mb-2 font-semibold text-blue-800">How it works</h3>
<p class="text-sm text-blue-700">
This page uses the <code>__()</code> and
<code>trans()</code> functions from
<code>@erag/lang-sync-inertia</code> to display translations
synced from Laravel's language files.
</p>
<ul class="mt-2 text-sm text-blue-700">
<li>
<code>__('messages.title')</code> - Simple translation
</li>
<li>
<code
>trans('messages.greeting', {'{'} name: 'Developer'
{'}'})</code
>
- Translation with placeholders
</li>
</ul>
</div>
</div>
</div>
</template>Lo mas importante de la implementación anterior es el uso de syncLangFiles:
syncLangFiles('messages');Puedes sincronizar varios:
syncLangFiles(['messages', 'auth']);Si necesitas generar el JSON de las traducciones:
$ php artisan erag:generate-langSeleccionar Idioma (Lenguaje) en Laravel Livewire
Te muestro cómo puedes manejar el idioma en Laravel. Aquí puedes ver que la interfaz ahora cambia a español, y luego a inglés.
Muy sencillo: para esto vamos a utilizar lo que es Livewire. Aunque, si por cualquier masoquista razón no quieres emplear Livewire, seguramente cuentas con los conocimientos necesarios para hacer los cambios por tu cuenta.
Lo importante es conocer las funciones que debemos llamar, más allá de cómo las implementamos; es decir, entender cuáles métodos hay que usar para tal fin.
El componente y el manejo del lenguaje
Este es el componente. Aquí puedes ver que, una vez recibido el lenguaje (que viene de este listado que tenemos acá), en este caso estoy empleando Laravel + Livewire, cada vez que cambiamos el lenguaje mediante wire:model, lo que hacemos es un llamado al método render() que está definido:
<?php
namespace App\Livewire\User;
use Livewire\Attributes\Layout;
use Livewire\Component;
use Illuminate\Support\Facades\App;
#[Layout('layouts.store')]
class UserProfile extends Component
{
public $language;
public function render()
{
if (!isset($this->language)) {
// al cargar el componente, este codigo se ejecuta al language NO estar seleccionado
// por el usuario
$this->language = session('locale') ?? App::getLocale();
} else {
// se ejecuta desde la vista asociada por el .live
session(['locale' => $this->language]);
App::setLocale($this->language);
}
return view('livewire.user.user-profile');
}
}Hay muchas formas de hacerlo, pero esta fue la implementación que se me ocurrió. Lo importante es que te llegue el lenguaje del usuario. Yo te estoy mostrando cómo lo hice yo.
Entonces, aquí lo que hacemos es lo siguiente:
- Si no está definido, inicializo por defecto el lenguaje según el idioma seleccionado en el sistema (ya sea en la sesión, que es donde lo guardo, o usando alguna de las formas para obtener el idioma a nivel de la aplicación).
- Cuando cambiamos el idioma mediante wire:model, entramos aquí y establecemos el idioma en la sesión, ya que lo guardo ahí.
- Y también, importantísimo: hay que establecer el idioma a nivel del framework, porque si no, no estamos haciendo nada.
Aquí también puedes aprovechar para guardar preferencias del usuario si quieres que sea persistente (por ejemplo, guardarlo en base de datos).
Yo, en otro video, te comentaba cómo te recomendaba guardar las preferencias de usuario sin tener que crear 20 columnas por 20 preferencias distintas. Lo que hacía era guardar un pequeño objeto JSON.
Eso ya te lo comenté antes y es precisamente lo que hacemos aquí.
Transición y recarga de interfaz
En el caso de Livewire, para que recargue toda la interfaz, también le metí un pequeño AlpineJS que lo que hace es esperar unos segundos para que dé tiempo de hacer la petición (wire:model) y luego recargar toda la página.
Esto es para que veamos el cambio aplicado a toda la interfaz, y es por eso que se ve reflejado por completo:
<flux:select :label="__('Language')" id="language" wire:model.live="language" x-data @change="setTimeout(function(){window.location.reload()}, 100)" class="mt-1 block w-full rounded border-gray-300">
<option value="es">Español</option>
<option value="en">English</option>
</flux:select>Middleware para mantener el idioma
Es importante que los cambios se mantengan a lo largo de todas las peticiones. Si, por ejemplo, volvemos a "Blog" o navegamos a otra parte de la aplicación, se volvería al idioma inicial.
Para evitar esto, debemos implementar un middleware, el cual se encargue de establecer el idioma guardado en la sesión para cada una de las peticiones.
Porque, nuevamente, esto no es persistente: si recargamos la página sin middleware, se pierde.
Esto es un fastidio. Para resolverlo, implementamos el siguiente middleware. Tal cual puedes ver:
$ php artisan make:middleware SetLocaleLuego, dentro de ese middleware, establecemos el idioma que tenemos en la sesión, si y solo si existe.
También le puedes pasar como segundo parámetro el idioma por defecto (si no quieres usar el condicional), pero en este caso yo lo definí así:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\App;
class SetLocale
{
public function handle($request, Closure $next)
{
if (session('locale')) {
App::setLocale(session('locale'));
}
return $next($request);
}
}Dónde registrar el middleware
¿Dónde llamamos a este middleware? Puedes configurarlo directamente en las rutas si así lo prefieres:
Route::middleware([SetLocale::class])Pero me refiero a colocarlo dentro del grupo de rutas web.
bootstrap/app.php
->withMiddleware(function (Middleware $middleware) {
$middleware->web([
// Tu middleware personalizado
\App\Http\Middleware\SetLocale::class,
]);
})Aunque una forma más limpia es usar withMiddleware(), que desde Laravel 11 ahora está disponible.
Con este método puedes definirlo directamente en las rutas de tipo web, y así cada petición ejecuta este middleware automáticamente.
Recuerda que, por definición, los middleware se ejecutan antes de cada petición HTTP del navegador o lo que sea.
Archivos de idioma
Crea tus archivos de lenguaje en y es, en este caso para inglés y español.
Define las parejas clave–valor como un objeto:
resources/lang/es.json
{
"Light": "Claro",
"Dark": "Oscuro",
***
} resources/lang/en.json
{
"Light": "Light",
"Dark": "Dark",
***
} Luego, desde las vistas, simplemente referencias el texto mediante __(), que es el nombre del método que toma el nombre del campo.
Por ejemplo:
{{ __('Light') }}Así que, de esta forma tan sencilla, tenemos configurado el idioma en Laravel. Son simplemente dos pasos clave.
7. Conclusión: experiencia y recomendaciones prácticas
La localización es una herramienta que marca la diferencia entre una app funcional y una app preparada para escalar globalmente.
En mi experiencia, lo más importante es mantener una estructura clara en los archivos de idioma, usar nombres de claves coherentes y documentar las nuevas traducciones en equipo.
Si trabajas en equipo, un consejo adicional es usar una herramienta como Phrase o Linguise para sincronizar traducciones y evitar inconsistencias.
Y recuerda: aunque Laravel hace gran parte del trabajo por ti, la calidad de la localización depende de lo organizado que mantengas tus textos y tu flujo de trabajo.
❓Preguntas frecuentes
- ¿Qué diferencia hay entre JSON y PHP translations?
- Los archivos PHP permiten agrupar textos por módulo, mientras que JSON guarda las cadenas directamente. PHP es más escalable.
- ¿Cómo detecto el idioma automáticamente?
- Puedes usar Request::getPreferredLanguage() o crear un middleware que lea el idioma del navegador o de la URL.
- ¿Puedo traducir contenido dinámico?
- Sí, con paquetes como Spatie Translatable puedes guardar versiones en varios idiomas en la base de datos.
- ¿Cómo cambio el idioma sin recargar la página?
- Puedes usar AJAX o Livewire para actualizar el contenido según la selección del idioma del usuario.
El siguiente paso, es aprender el sistema de Autorización en Laravel con Gates y Policies.