Skip to main content

Laravel i18n: Hướng dẫn đầy đủ về quốc tế hóa và bản địa hóa

Từ tệp ngôn ngữ đầu tiên đến môi trường production: cấu hình hệ thống dịch của Laravel, xử lý dạng số nhiều, khắc phục lỗi dự phòng JSON và thêm chuỗi ngôn ngữ dự phòng thông minh cho các biến thể khu vực.

1

Tìm hiểu hệ thống dịch của Laravel

Laravel tích hợp sẵn hệ thống dịch hỗ trợ hai định dạng tệp: mảng PHP và JSON. Tệp PHP dùng khóa lồng nhau được sắp xếp theo tính năng (auth.failed, validation.required). Tệp JSON dùng chuỗi nguồn làm khóa nên đơn giản hơn nhưng không hỗ trợ cấu trúc lồng nhau.

Terminal
composer create-project laravel/laravel my-app
cd my-app

# Laravel includes i18n out of the box
# No extra packages needed for basic usage
Hệ thống dịch của Laravel nằm trong thư mục lang/ (Laravel 9+) hoặc resources/lang/ (Laravel 8 trở về trước). Framework tự động phát hiện thư mục. Nếu cả hai cùng tồn tại, lang/ được ưu tiên.
2

Cấu hình cài đặt ngôn ngữ

Đặt ngôn ngữ mặc định và ngôn ngữ dự phòng của ứng dụng trong config/app.php. Hệ thống dùng ngôn ngữ dự phòng khi thiếu khóa bản dịch trong ngôn ngữ đang hoạt động. Hãy cấu hình các ngôn ngữ mà ứng dụng hỗ trợ và thêm middleware để phát hiện rồi đặt ngôn ngữ ưu tiên của người dùng.

config/app.php
// config/app.php
return [
    'locale' => 'en',            // Default locale
    'fallback_locale' => 'en',   // Fallback when key is missing
    'faker_locale' => 'en_US',

    // Available locales (for your language switcher)
    'available_locales' => ['en', 'de', 'ja', 'es', 'fr', 'pt-BR'],
];
app/Http/Middleware/SetLocale.php
// app/Http/Middleware/SetLocale.php
namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;

class SetLocale
{
    public function handle(Request $request, Closure $next)
    {
        $locale = $request->segment(1); // e.g. /de/about

        if (in_array($locale, config('app.available_locales'))) {
            app()->setLocale($locale);
        }

        return $next($request);
    }
}
3

Sử dụng hàm dịch

Laravel cung cấp ba cách dịch chuỗi: hàm trợ giúp __() (khuyên dùng), hàm trans() và chỉ thị Blade @lang. Cả ba đều nhận khóa bản dịch và tham số thay thế tùy chọn. Dùng __() trong mã PHP và mẫu Blade, còn dùng @lang trong Blade khi bạn không cần thoát HTML.

lang/en/messages.php
// lang/en/messages.php
return [
    'welcome' => 'Welcome to our application',
    'greeting' => 'Hello, :name!',
    'nav' => [
        'home' => 'Home',
        'about' => 'About',
        'settings' => 'Settings',
    ],
];

// lang/de/messages.php
return [
    'welcome' => 'Willkommen in unserer Anwendung',
    'greeting' => 'Hallo, :name!',
    'nav' => [
        'home' => 'Startseite',
        'about' => 'Über uns',
        'settings' => 'Einstellungen',
    ],
];
Ưu tiên __() hơn trans() trong mã mới. __() hoạt động với cả tệp bản dịch PHP và JSON, còn trans() chỉ hoạt động với tệp PHP. Trong mẫu Blade, chỉ thị @lang tương đương với {'{ __() }'} nhưng có cú pháp gọn hơn đôi chút.
4

Xử lý dạng số nhiều

Laravel dùng cú pháp phân tách bằng dấu gạch đứng cho các dạng số nhiều. Dạng đơn giản nhất là 'apples' => 'There is one apple|There are many apples'. Với khoảng giá trị rõ ràng, dùng 'apples' => '{0} No apples|{1} One apple|[2,*] :count apples'. Hàm trợ giúp trans_choice() hoặc Str::plural() chọn dạng phù hợp dựa trên số lượng.

lang/en.json
// lang/en.json
{
    "Welcome back!": "Welcome back!",
    "You have :count new notifications": "You have :count new notifications",
    "Copyright :year :company": "Copyright :year :company"
}

// lang/de.json
{
    "Welcome back!": "Willkommen zurück!",
    "You have :count new notifications": "Sie haben :count neue Benachrichtigungen",
    "Copyright :year :company": "Copyright :year :company"
}
Tính năng xử lý số nhiều tích hợp sẵn của Laravel chỉ xử lý đúng quy tắc one/other đơn giản cho phần lớn ngôn ngữ. Với ngôn ngữ có dạng số nhiều phức tạp (tiếng Ả Rập có 6, tiếng Nga có 4, tiếng Ba Lan có 3), bạn cần định nghĩa mọi danh mục số nhiều CLDR bắt buộc. Nếu thiếu, người dùng sẽ thấy nội dung sai ngữ pháp.
5

Thêm chuỗi ngôn ngữ dự phòng

Cơ chế dự phòng tích hợp sẵn của Laravel chỉ chuyển từ ngôn ngữ đang hoạt động sang fallback_locale mà không có bước trung gian. Khi thiếu khóa, người dùng pt-BR sẽ thấy tiếng Anh thay vì bản dịch pt-PT hoàn toàn phù hợp. laravel-locale-chain khắc phục vấn đề này bằng cách hợp nhất sâu các bản dịch từ chuỗi dự phòng có thể cấu hình tại thời điểm tải.

resources/views/example.blade.php
{{-- Simple translation --}}
<h1>{{ __('messages.welcome') }}</h1>

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

{{-- Using @lang directive --}}
<h2>@lang('messages.nav.home')</h2>

{{-- JSON translations (use the string itself as key) --}}
<p>{{ __('Welcome back!') }}</p>

{{-- Pluralization --}}
{{ trans_choice('{0} No items|{1} One item|[2,*] :count items', $count) }}

{{-- Inside Blade components --}}
<x-button>{{ __('messages.nav.settings') }}</x-button>
6

Tự động hóa bản dịch

Sau khi hoàn tất thiết lập Laravel i18n, hãy dùng AI để dịch các tệp ngôn ngữ. Trỏ trợ lý AI đến tệp ngôn ngữ nguồn hoặc dùng i18n Agent CLI trong quy trình CI/CD. Công cụ hỗ trợ cả tệp bản dịch PHP và JSON.

config/locale-chain.php
// Install locale chain package
composer require i18n-agent/laravel-locale-chain

// config/locale-chain.php
return [
    'chains' => [
        'pt-BR' => ['pt-BR', 'pt', 'en'],
        'zh-Hant-TW' => ['zh-Hant-TW', 'zh-Hant', 'zh', 'en'],
        'es-419' => ['es-419', 'es', 'en'],
    ],
];

// In AppServiceProvider::boot()
use I18nAgent\LocaleChain\LocaleChainServiceProvider;

// The package automatically deep-merges translations
// across the chain: pt-BR -> pt -> en
Dịch tăng dần — khi thêm khóa mới, chỉ dịch phần khác biệt thay vì tạo lại toàn bộ tệp. Cách này giữ nguyên các bản dịch đã được con người duyệt và tránh thay đổi không cần thiết.

Lỗi thường gặp

Mã hóa cứng logic số ít/số nhiều

Viết $count == 1 ? 'item' : 'items' thay vì dùng trans_choice() sẽ gây lỗi với ngôn ngữ coi 0 là số ít (tiếng Pháp), có hơn 3 dạng số nhiều (tiếng Nga, tiếng Ba Lan) hoặc có 6 dạng (tiếng Ả Rập). Luôn dùng cú pháp số nhiều của Laravel và định nghĩa đầy đủ mọi dạng bắt buộc.

Bản dịch JSON không dùng ngôn ngữ dự phòng

fallback_locale của Laravel chỉ hoạt động với tệp bản dịch PHP. Bản dịch JSON dùng chuỗi nguồn làm khóa nên khi thiếu bản dịch, hệ thống trả về chính khóa đó (nội dung tiếng Anh) thay vì tìm trong ngôn ngữ dự phòng. Vì vậy, bản dịch JSON không có cơ chế dự phòng ngôn ngữ thực sự. Hãy dùng laravel-locale-chain để khắc phục.

Kết hợp PHP và JSON mà không hiểu thứ tự ưu tiên

Khi cùng một khóa tồn tại trong cả tệp PHP và JSON, PHP được ưu tiên. Điều này có thể gây ra hành vi khó hiểu: cập nhật tệp JSON không có tác dụng vì tệp PHP che khuất khóa đó. Hãy chọn một định dạng cho mỗi không gian tên tính năng và dùng nhất quán.

Dùng thử i18n Agent ngay

Thả tệp bản dịch của bạn vào đây

JSON, YAML, PO, XML, CSV, Markdown, Properties

hoặc nhấp để duyệt

Ngôn ngữ đích

Không cần đăng kýBáo giá tức thì

Câu hỏi thường gặp về Laravel i18n