Laravel Localization: Complete Guide to Multilingual PHP Applications

Learn how to implement localization in Laravel applications. This comprehensive guide covers translation files, pluralization, parameter replacement, and best practices for building multilin

  • date icon

    Friday, Dec 05, 2025

Laravel Localization: Complete Guide to Multilingual PHP Applications

Building multilingual applications is essential for reaching a global audience. Laravel provides a robust localization system that makes it easy to retrieve strings in various languages. In this guide, we’ll explore Laravel’s localization features and how to implement them effectively in your applications.

Understanding Laravel Localization

Laravel’s localization features provide a convenient way to retrieve strings in various languages, allowing you to easily support multiple languages within your application. The framework supports two approaches for managing translation strings:

  1. PHP array files - Traditional approach using .php files with arrays
  2. JSON files - Recommended for applications with many translatable strings

Setting Up Language Files

Directory Structure

Laravel stores language files in the lang directory. You can create this directory by running:

php artisan lang:publish

This creates the following structure:

/lang
    /en
        messages.php
        validation.php
    /es
        messages.php
        validation.php
    en.json
    es.json

PHP Array Files

PHP array files are ideal for organizing translations by feature or domain:

lang/en/messages.php:

<?php

return [
    'welcome' => 'Welcome to our application!',
    'goodbye' => 'Goodbye, see you soon!',
    'user' => [
        'profile' => 'User Profile',
        'settings' => 'Account Settings',
        'logout' => 'Log Out',
    ],
];

lang/es/messages.php:

<?php

return [
    'welcome' => '¡Bienvenido a nuestra aplicación!',
    'goodbye' => '¡Adiós, hasta pronto!',
    'user' => [
        'profile' => 'Perfil de Usuario',
        'settings' => 'Configuración de Cuenta',
        'logout' => 'Cerrar Sesión',
    ],
];

JSON Files

JSON files are recommended for applications with a large number of translatable strings. They use the default translation string as the key:

lang/en.json:

{
    "Welcome to our application!": "Welcome to our application!",
    "I love programming.": "I love programming.",
    "The :attribute must be a valid email.": "The :attribute must be a valid email."
}

lang/es.json:

{
    "Welcome to our application!": "¡Bienvenido a nuestra aplicación!",
    "I love programming.": "Me encanta programar.",
    "The :attribute must be a valid email.": "El :attribute debe ser un correo electrónico válido."
}

Configuring the Locale

Default Locale

Configure your application’s default locale in config/app.php or via the APP_LOCALE environment variable:

config/app.php:

'locale' => env('APP_LOCALE', 'en'),
'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'),

.env:

APP_LOCALE=en
APP_FALLBACK_LOCALE=en

Changing Locale at Runtime

You can change the locale dynamically for a single request:

use Illuminate\Support\Facades\App;

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

    App::setLocale($locale);

    return view('greeting');
});

Getting Current Locale

Check the current locale or determine if it matches a specific language:

use Illuminate\Support\Facades\App;

// Get current locale
$locale = App::currentLocale();

// Check if locale is a specific language
if (App::isLocale('en')) {
    // Handle English locale
}

Retrieving Translation Strings

Using the __() Helper

The most common way to retrieve translations:

// From PHP array file (lang/en/messages.php)
echo __('messages.welcome');
// Output: "Welcome to our application!"

// Nested keys
echo __('messages.user.profile');
// Output: "User Profile"

// From JSON file (using default string as key)
echo __('I love programming.');
// Output: "I love programming." (or translated version)

In Blade Templates

Use the {{ }} syntax in your Blade templates:

{{-- From PHP array file --}}
<h1>{{ __('messages.welcome') }}</h1>

{{-- From JSON file --}}
<p>{{ __('I love programming.') }}</p>

{{-- With @lang directive --}}
@lang('messages.goodbye')

Parameter Replacement

Laravel allows you to define placeholders in your translation strings:

Basic Parameters

lang/en/messages.php:

<?php

return [
    'welcome' => 'Welcome, :name',
    'greeting' => 'Hello, :Name! Welcome to :App.',
];

Usage:

echo __('messages.welcome', ['name' => 'John']);
// Output: "Welcome, John"

echo __('messages.greeting', ['name' => 'john', 'app' => 'Laravel']);
// Output: "Hello, John! Welcome to Laravel."

Capitalization

Placeholders automatically handle capitalization:

// :name - lowercase
// :Name - first letter capitalized  
// :NAME - all uppercase

'welcome' => 'Welcome, :NAME', // "Welcome, JOHN"
'greeting' => 'Hello, :Name',  // "Hello, John"

In Blade Templates

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

<p>{{ __('messages.greeting', ['name' => auth()->user()->name, 'app' => config('app.name')]) }}</p>

Pluralization

Laravel provides powerful pluralization support for handling different quantity forms:

Basic Pluralization

Use the | character to separate singular and plural forms:

lang/en/messages.php:

<?php

return [
    'apples' => 'There is one apple|There are many apples',
    'notifications' => '{0} No notifications|{1} One notification|[2,*] :count notifications',
];

Using trans_choice()

echo trans_choice('messages.apples', 1);
// Output: "There is one apple"

echo trans_choice('messages.apples', 5);
// Output: "There are many apples"

echo trans_choice('messages.notifications', 0);
// Output: "No notifications"

echo trans_choice('messages.notifications', 1);
// Output: "One notification"

echo trans_choice('messages.notifications', 10);
// Output: "10 notifications"

Advanced Pluralization Rules

Define multiple ranges for complex pluralization:

'items' => '{0} No items|[1,5] A few items|[6,10] Several items|[11,*] Many items',

Pluralization with Parameters

Combine pluralization with parameter replacement:

lang/en/messages.php:

<?php

return [
    'minutes_ago' => '{1} :value minute ago|[2,*] :value minutes ago',
    'cart_items' => '{0} Your cart is empty|{1} You have :count item in your cart|[2,*] You have :count items in your cart',
];

Usage:

echo trans_choice('messages.minutes_ago', 5, ['value' => 5]);
// Output: "5 minutes ago"

echo trans_choice('messages.cart_items', 3);
// Output: "You have 3 items in your cart"

In Blade Templates

<p>{{ trans_choice('messages.notifications', $notificationCount) }}</p>

<p>{{ trans_choice('messages.cart_items', $cartItems->count()) }}</p>

Building a Language Switcher

Create a language switcher for your application:

Route Setup

routes/web.php:

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

    session()->put('locale', $locale);

    return redirect()->back();
})->name('language.switch');

Middleware

Create middleware to set the locale from session:

app/Http/Middleware/SetLocale.php:

<?php

namespace App\Http\Middleware;

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

class SetLocale
{
    public function handle(Request $request, Closure $next)
    {
        if (session()->has('locale')) {
            App::setLocale(session('locale'));
        }

        return $next($request);
    }
}

Register in bootstrap/app.php (Laravel 11+):

->withMiddleware(function (Middleware $middleware) {
    $middleware->web(append: [
        \App\Http\Middleware\SetLocale::class,
    ]);
})

Blade Component

resources/views/components/language-switcher.blade.php:

<div class="language-switcher">
    @foreach(['en' => 'English', 'es' => 'Español', 'fr' => 'Français'] as $locale => $language)
        <a href="{{ route('language.switch', $locale) }}" 
           class="{{ App::currentLocale() === $locale ? 'active' : '' }}">
            {{ $language }}
        </a>
    @endforeach
</div>

Best Practices

1. Organize Translations by Domain

Group related translations into separate files:

/lang
    /en
        auth.php       # Authentication strings
        validation.php # Validation messages
        dashboard.php  # Dashboard UI
        emails.php     # Email templates
        api.php        # API responses

2. Use Descriptive Keys

Bad:

'msg1' => 'Welcome',
'btn' => 'Submit',

Good:

'auth.welcome_message' => 'Welcome to your dashboard',
'forms.submit_button' => 'Submit',

3. Provide Context in Comments

<?php

return [
    // Displayed on the login page
    'login_title' => 'Sign In to Your Account',
    
    // Button text for form submission
    'submit' => 'Submit',
    
    // Error message when email is invalid
    'invalid_email' => 'Please enter a valid email address.',
];

4. Handle Missing Translations

The __() function returns the key if no translation exists:

// If 'messages.unknown_key' doesn't exist
echo __('messages.unknown_key');
// Output: "messages.unknown_key"

// Provide a default value
echo __('messages.unknown_key') !== 'messages.unknown_key' 
    ? __('messages.unknown_key') 
    : 'Default text';

5. Use JSON for User-Facing Strings

For large applications, JSON files are easier to manage:

{
    "Welcome back, :name!": "Welcome back, :name!",
    "You have :count new messages.": "You have :count new messages.",
    "Your order has been shipped.": "Your order has been shipped."
}

Managing Translations with Azbox

While Laravel’s built-in localization is powerful, managing translations across multiple languages and team members can become complex. Azbox provides a centralized platform to streamline your Laravel localization workflow:

Benefits of Using Azbox with Laravel

  • Centralized Management: Manage all translations in one place
  • Team Collaboration: Work with translators without sharing code access
  • Import/Export: Import existing Laravel PHP files and export updates
  • Translation Memory: Reuse translations across projects
  • Quality Assurance: Automated checks for missing translations and inconsistencies

Exporting Laravel Translations to Azbox

  1. Export your lang/*.php files as JSON or upload directly
  2. Azbox preserves your key structure and supports nested arrays
  3. Translators work in a user-friendly interface
  4. Export back to Laravel PHP format when ready

Workflow Integration

# Export translations to Azbox
php artisan translations:export --format=azbox

# Import translations from Azbox
php artisan translations:import --from=azbox

Common Patterns

Date and Time Formatting

// lang/en/dates.php
return [
    'formats' => [
        'short' => 'M d, Y',
        'long' => 'F d, Y',
        'datetime' => 'M d, Y H:i',
    ],
];

// Usage
$date = now()->format(__('dates.formats.long'));

Currency Formatting

// lang/en/currency.php
return [
    'format' => '$:amount',
    'thousand_separator' => ',',
    'decimal_separator' => '.',
];

// lang/es/currency.php  
return [
    'format' => ':amount €',
    'thousand_separator' => '.',
    'decimal_separator' => ',',
];

Validation Messages

Laravel includes localized validation messages. Customize them:

lang/en/validation.php:

return [
    'required' => 'The :attribute field is required.',
    'email' => 'The :attribute must be a valid email address.',
    'custom' => [
        'email' => [
            'required' => 'We need your email address to continue.',
        ],
    ],
    'attributes' => [
        'email' => 'email address',
        'password' => 'password',
    ],
];

Testing Translations

Feature Tests

public function test_welcome_page_displays_correct_translation()
{
    $response = $this->get('/');
    $response->assertSee(__('messages.welcome'));
}

public function test_spanish_locale_displays_spanish_text()
{
    App::setLocale('es');
    
    $response = $this->get('/');
    $response->assertSee('¡Bienvenido a nuestra aplicación!');
}

Check for Missing Translations

Create a command to audit translations:

// app/Console/Commands/AuditTranslations.php
public function handle()
{
    $englishKeys = $this->getKeys('en');
    $spanishKeys = $this->getKeys('es');
    
    $missing = array_diff($englishKeys, $spanishKeys);
    
    foreach ($missing as $key) {
        $this->warn("Missing Spanish translation: {$key}");
    }
}

Conclusion

Laravel’s localization system provides a powerful foundation for building multilingual applications. By following the patterns and best practices outlined in this guide, you can:

  • Structure translations effectively using PHP arrays or JSON files
  • Handle complex scenarios with parameter replacement and pluralization
  • Build user-friendly language switchers
  • Maintain translation quality across your application

For teams managing translations across multiple projects and languages, Azbox offers a centralized platform that integrates seamlessly with Laravel’s localization system.

Get Started with Azbox

Ready to streamline your Laravel localization workflow?

  1. Sign up for Azbox - Start with a free trial
  2. Import your translations - Upload your existing Laravel PHP files
  3. Collaborate with translators - Invite team members to work on translations
  4. Export back to Laravel - Download updated PHP files for your project

Explore AZbox’s localization platform and simplify your translation workflow:

View AZbox Plans and Pricing

Learn about PHP Laravel file format support

Blog

Latest Posts

Discover our latest articles and updates.

Why Translations Have Always Been a Problem in Software Development
date icon

Sunday, Dec 21, 2025

Why Translations Have Always Been a Problem in Software Development

For decades, software developers have struggled with translations and localization. What should be a straightforward pro

Read More
How to Translate a Flutter App with Azbox: Complete Guide
date icon

Saturday, Dec 20, 2025

How to Translate a Flutter App with Azbox: Complete Guide

Translating your Flutter app is essential for reaching a global audience. Azbox provides a powerful Flutter SDK that sim

Read More
Android Localization: Complete Guide to Expanding Your App to New Markets
date icon

Saturday, Dec 13, 2025

Android Localization: Complete Guide to Expanding Your App to New Markets

Android runs on many devices, in many regions. To reach the majority of users, make sure your app handles text, audio fi

Read More
Call to action background

Start Global Growth Today

Join hundreds of successful companies already using AZbox to reach customers worldwide. Start with a free trial, no credit card required.

Get Started - It's Free