Curso de Laravel
Traducir los mensajes de Laravel al español
Por Víctor Peña · Publicado el
Hola, ¿cómo están? Ya tenemos un CRUD completo y funcionando, pero cuando la validación falla el usuario lee «The cedula field is required».
Hoy lo ponemos todo en español.
Parece un detalle menor, pero es de las primeras cosas que nota quien usa el sistema, y arreglarlo cuesta diez minutos.
¡Empecemos!
Publicar los archivos de idioma
En las versiones actuales de Laravel la carpeta lang/ no viene incluida. Se genera con:
php artisan lang:publish
Eso crea lang/en/ con cuatro archivos:
lang/
└── en/
├── auth.php mensajes de autenticación
├── pagination.php "Previous" / "Next"
├── passwords.php recuperación de contraseña
└── validation.php todos los mensajes de validación
Crear la carpeta en español
cp -r lang/en lang/es
En Windows, copia y pega la carpeta y renómbrala a es.
Cambiar el idioma de la aplicación
En el archivo .env:
APP_LOCALE=es
APP_FALLBACK_LOCALE=es
APP_FAKER_LOCALE=es_ES
Ese APP_FAKER_LOCALE hace que los factories generen nombres y direcciones en español, que para datos de prueba se agradece bastante.
Y después:
php artisan config:clear
Ese paso se olvida constantemente. Si cambiaste el .env y sigues viendo inglés, casi siempre es la configuración en caché.
Traducir validation.php
Es el archivo importante. Abre lang/es/validation.php y traduce las reglas que uses:
return [
'required' => 'El campo :attribute es obligatorio.',
'email' => 'El campo :attribute debe ser un correo electrónico válido.',
'unique' => 'El :attribute ya está registrado.',
'confirmed' => 'La confirmación de :attribute no coincide.',
'numeric' => 'El campo :attribute debe ser un número.',
'integer' => 'El campo :attribute debe ser un número entero.',
'date' => 'El campo :attribute no es una fecha válida.',
'before' => 'El campo :attribute debe ser una fecha anterior a :date.',
'after' => 'El campo :attribute debe ser una fecha posterior a :date.',
'exists' => 'El :attribute seleccionado no es válido.',
'image' => 'El campo :attribute debe ser una imagen.',
'in' => 'El :attribute seleccionado no es válido.',
'max' => [
'numeric' => 'El campo :attribute no debe ser mayor que :max.',
'file' => 'El archivo :attribute no debe pesar más de :max kilobytes.',
'string' => 'El campo :attribute no debe tener más de :max caracteres.',
'array' => 'El campo :attribute no debe tener más de :max elementos.',
],
'min' => [
'numeric' => 'El campo :attribute debe ser al menos :min.',
'file' => 'El archivo :attribute debe pesar al menos :min kilobytes.',
'string' => 'El campo :attribute debe tener al menos :min caracteres.',
'array' => 'El campo :attribute debe tener al menos :min elementos.',
],
];
Fíjate en que max y min tienen cuatro variantes. Laravel elige la correcta según el tipo del campo, y por eso el mensaje de un archivo habla de kilobytes y el de un texto de caracteres.
Los :attribute, :max, :date son marcadores que se reemplazan al mostrar el mensaje.
Existen traducciones completas ya hechas en internet, pero conviene revisarlas: muchas usan un español neutro que suena raro en un sistema de gestión.
Nombres de campos: el arreglo attributes
Sin esto, los mensajes quedan a medias: «El campo fecha_nacimiento es obligatorio».
Al final de validation.php hay un arreglo attributes vacío. Ahí se define cómo se llama cada campo para el usuario:
'attributes' => [
'cedula' => 'cédula',
'nombre' => 'nombre',
'apellido' => 'apellido',
'fecha_nacimiento' => 'fecha de nacimiento',
'correo' => 'correo electrónico',
'telefono' => 'teléfono',
'doctor_id' => 'doctor',
'password' => 'contraseña',
],
Y el mensaje pasa a ser: «El campo fecha de nacimiento es obligatorio».
Este arreglo es el que más diferencia hace y el que más se ignora. Con las reglas traducidas pero sin los nombres, el sistema sigue viéndose descuidado.
Ese doctor_id es un buen ejemplo: el usuario no tiene por qué saber que hay una columna llamada así.
Mensajes por campo y regla
Para casos concretos, dentro del arreglo custom:
'custom' => [
'cedula' => [
'unique' => 'Ya existe un paciente registrado con esa cédula.',
'required' => 'Necesitamos la cédula para registrar al paciente.',
],
'fecha_nacimiento' => [
'before' => 'La fecha de nacimiento no puede ser futura.',
],
],
Esto tiene prioridad sobre el mensaje general. Sirve para las reglas donde el texto genérico no explica bien qué pasó.
También se puede hacer desde el Form Request con el método messages(). La diferencia práctica: en validation.php los mensajes están centralizados; en el Form Request quedan junto a las reglas. Para reglas de negocio concretas, prefiero el Form Request; para textos que se repiten en todo el sistema, el archivo de idioma.
Traducir la paginación
lang/es/pagination.php:
return [
'previous' => '« Anterior',
'next' => 'Siguiente »',
];
Aparece automáticamente en el {{ $pacientes->links() }} que vimos en la lección de listado.
Traducir la autenticación
lang/es/auth.php:
return [
'failed' => 'Las credenciales no coinciden con nuestros registros.',
'password' => 'La contraseña es incorrecta.',
'throttle' => 'Demasiados intentos de acceso. Inténtalo de nuevo en :seconds segundos.',
];
Y lang/es/passwords.php:
return [
'reset' => 'Tu contraseña ha sido restablecida.',
'sent' => 'Te hemos enviado un correo con el enlace para restablecer tu contraseña.',
'throttled' => 'Espera un momento antes de volver a intentarlo.',
'token' => 'Este enlace de recuperación no es válido.',
'user' => 'No encontramos ningún usuario con ese correo electrónico.',
];
Fechas en español
Los mensajes ya están traducidos, pero las fechas siguen mostrando «Monday» y «January».
Laravel usa Carbon para las fechas, y hay que configurarlo aparte. En app/Providers/AppServiceProvider.php:
use Carbon\Carbon;
public function boot(): void
{
Carbon::setLocale('es');
}
Y ahora:
{{ $cita->fecha->translatedFormat('l d \d\e F \d\e Y') }}
{{-- lunes 24 de mayo de 2026 --}}
{{ $paciente->created_at->diffForHumans() }}
{{-- hace 3 días --}}
translatedFormat() y no format(). El format() normal siempre devuelve los nombres en inglés; es el segundo tropiezo clásico después del config:clear.
Si quieres un formato fijo en todo el sistema, un accesor en el modelo evita repetirlo:
protected function fechaFormateada(): Attribute
{
return Attribute::get(
fn () => $this->fecha->translatedFormat('d \d\e F \d\e Y')
);
}
{{ $cita->fecha_formateada }}
Traducir tus propios textos
El mismo sistema sirve para los textos de tu aplicación. Crea lang/es/mensajes.php:
return [
'guardado' => 'Los datos se guardaron correctamente.',
'eliminado' => 'El registro fue eliminado.',
'sin_permiso' => 'No tienes permiso para realizar esta acción.',
'bienvenida' => 'Bienvenido, :nombre.',
];
Y se usan con la función __():
return redirect()->route('pacientes.index')
->with('exito', __('mensajes.guardado'));
<p>{{ __('mensajes.bienvenida', ['nombre' => auth()->user()->name]) }}</p>
¿Vale la pena si el sistema solo va a estar en español? En muchos proyectos, no: escribir el texto directamente es más simple y más legible.
Donde sí conviene es cuando el mismo mensaje aparece en varios sitios, o cuando existe la posibilidad real de añadir otro idioma. Si el sistema es de un solo idioma y no va a cambiar, no te compliques.
Plurales
Cuando el texto depende de una cantidad:
'citas' => '{0} Sin citas|{1} Una cita|[2,*] :count citas',
{{ trans_choice('mensajes.citas', $paciente->citas_count) }}
Con 0 muestra «Sin citas», con 1 «Una cita», con 5 «5 citas».
Varios idiomas
Si el sistema debe funcionar en dos idiomas, el cambio se hace con:
app()->setLocale('en');
Lo habitual es un middleware que lea la preferencia del usuario:
public function handle(Request $request, Closure $next)
{
if ($idioma = $request->user()?->idioma) {
app()->setLocale($idioma);
Carbon::setLocale($idioma);
}
return $next($request);
}
Con eso, cada usuario ve el sistema en su idioma sin tocar nada más.
Errores comunes
- No ejecutar
config:cleardespués de cambiar el.env. - Traducir las reglas pero no el arreglo
attributes, dejando los mensajes con nombres de columna. format()en lugar detranslatedFormat()para nombres de meses y días.- Olvidar
Carbon::setLocale('es'). - Traducir las cien reglas de
validation.phpcuando usas ocho. - Cambiar el nombre de la carpeta
enen lugar de copiarla: conviene conservar el respaldo en inglés.
Para cerrar
Poner Laravel en español son tres pasos: lang:publish, copiar la carpeta a es y cambiar APP_LOCALE. Y luego el detalle que marca la diferencia: llenar el arreglo attributes.
Es de los cambios con mejor relación entre esfuerzo y percepción de calidad que hay en todo el framework.
Con esta lección cerramos el módulo de la aplicación web. En la siguiente empezamos con la autenticación: registro, inicio de sesión y rutas protegidas.
Saludos y éxitos.
Norvic Software
Desarrollamos el software que tu empresa necesita
Somos una fábrica de software en Bolivia. Construimos sistemas a medida y aplicaciones móviles, y llevamos Inteligencia Artificial a las empresas que ya tienen un sistema funcionando.
- Chatbots con Inteligencia Artificial
- Creación de agentes de IA
- Integración de IA en tus sistemas
- Desarrollo de software a medida
- Aplicaciones móviles iOS y Android
- Consultoría y asesoramiento técnico
Cotización sin costo · Respuesta directa por WhatsApp