> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# 本地化

> 介绍如何使用 Laravel 的多语言功能，通过 PHP 文件和 JSON 文件管理翻译字符串。

## 什么是本地化

Laravel 的本地化功能提供了一种便捷方式来获取多种语言的翻译字符串，用于在应用中支持多语言。

翻译字符串有两种管理方式：

| 方式          | 特点                                                |
| ----------- | ------------------------------------------------- |
| **PHP 文件**  | 例如 `lang/ja/messages.php`，采用键值数组。适合按功能整理（如校验错误消息） |
| **JSON 文件** | 例如 `lang/ja.json`，直接用翻译字符串作为键。适合翻译量大的应用           |

<Info>
  默认 Laravel 应用未包含 `lang` 目录。要自定义，请通过 `lang:publish` Artisan 命令发布。
</Info>

```shell theme={null}
php artisan lang:publish
```

## Locale 配置

### 默认 Locale

应用默认语言在 `config/app.php` 的 `locale` 中配置，通常通过 `.env` 中的 `APP_LOCALE` 环境变量控制。

```php theme={null}
// config/app.php
'locale' => env('APP_LOCALE', 'en'),
'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'),
```

```ini theme={null}
# .env
APP_LOCALE=ja
APP_FALLBACK_LOCALE=en
```

`fallback_locale` 是指定语言无对应翻译时所使用的备用语言。

### 运行时修改 Locale

使用 `App` 门面的 `setLocale()` 方法可以按请求修改 locale。

```php theme={null}
use Illuminate\Support\Facades\App;

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

    App::setLocale($locale);

    // ...
});
```

### 检查当前 Locale

```php theme={null}
use Illuminate\Support\Facades\App;

$locale = App::currentLocale();

if (App::isLocale('ja')) {
    // 日语时的处理
}
```

## 创建语言文件

### PHP 文件形式

在 `lang/{语言代码}/` 目录下创建 PHP 文件，返回键值数组。

```text theme={null}
/lang
    /en
        messages.php
    /ja
        messages.php
```

```php theme={null}
<?php

// lang/ja/messages.php

return [
    'welcome' => '欢迎使用本应用！',
    'goodbye' => '再见！',
];
```

<Warning>
  存在地区差异的语言请按 ISO 15897 命名目录。例如英式英语应写为 `en_GB` 而不是 `en-gb`。
</Warning>

### JSON 形式

翻译字符串较多时推荐使用 JSON 形式。
在 `lang/` 目录下创建以语言代码命名的 JSON 文件。

```text theme={null}
/lang
    en.json
    ja.json
```

将原始英文文本作为键。

```json theme={null}
{
    "Welcome to our application!": "欢迎使用本应用！",
    "I love programming.": "我热爱编程。",
    "Logout": "登出",
    "Dashboard": "仪表盘"
}
```

<Tip>
  JSON 形式下英文原文本身就是键，因此英文默认展示可自动生效。没有翻译文件的语言会直接返回键（英文原文）。
</Tip>

### PHP 与 JSON 的选择

<AccordionGroup>
  <Accordion title="适合 PHP 文件的场景">
    * 想按功能整理，例如校验错误信息
    * 覆盖 Laravel 内置翻译（如 `validation.php`、`auth.php`）
    * 需要分层管理键

    ```php theme={null}
    // lang/ja/validation.php
    return [
        'required' => ':attribute 是必填项。',
        'email' => ':attribute 必须是有效的邮箱地址。',
    ];
    ```
  </Accordion>

  <Accordion title="适合 JSON 的场景">
    * UI 文案多，不想再费心命名键
    * 模板里直接写英文，其他语言通过翻译文件覆盖
    * 后续增加国际化需求

    ```blade theme={null}
    {{-- 在 Blade 模板里直接写英文 --}}
    {{ __('Welcome to our application!') }}
    ```
  </Accordion>
</AccordionGroup>

## 获取翻译字符串

### `__()` 辅助函数

最常用方式。PHP 文件形式下用 "文件名.键" 语法。

```php theme={null}
// PHP 文件形式：lang/ja/messages.php 中的 'welcome'
echo __('messages.welcome');
// => 欢迎使用本应用！

// JSON 形式：将翻译原文作为键
echo __('I love programming.');
// => 我热爱编程。
```

若不存在对应翻译，则原样返回键。

```php theme={null}
echo __('messages.not_exists');
// => messages.not_exists（键原样返回）
```

### 在 Blade 模板中使用

Blade 中使用 `{{ __() }}`：

```blade theme={null}
{{-- PHP 文件形式 --}}
<h1>{{ __('messages.welcome') }}</h1>

{{-- JSON 形式 --}}
<button>{{ __('Logout') }}</button>

{{-- @lang 指令（已不推荐，仅保留向后兼容） --}}
@lang('messages.welcome')
```

<Tip>
  `@lang` 指令已不推荐。目前建议使用 `{{ __() }}`。
</Tip>

## 占位符

翻译字符串中可以嵌入形如 `:name` 的占位符。

```php theme={null}
// lang/ja/messages.php
return [
    'welcome' => '欢迎你，:name！',
    'greet'   => '你好，:Name！',  // 首字母大写
    'shout'   => 'HEY :NAME!',      // 全部大写
];
```

向 `__()` 的第二个参数传入替换值数组：

```php theme={null}
echo __('messages.welcome', ['name' => '田中']);
// => 欢迎你，田中！

echo __('messages.greet', ['name' => 'taro']);
// => 你好，Taro！（Name 首字母大写）

echo __('messages.shout', ['name' => 'taro']);
// => HEY TARO!（NAME 全大写）
```

Blade 中同样可用：

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

## 复数

不同语言的复数规则不同。Laravel 使用 `|` 分隔单数与复数。

### 基本复数

```php theme={null}
// lang/ja/messages.php
return [
    'apples' => '有 1 个苹果|有多个苹果',
];
```

JSON 形式同理：

```json theme={null}
{
    "There is one apple|There are many apples": "有 1 个苹果|有多个苹果"
}
```

用 `trans_choice()` 传入数量：

```php theme={null}
echo trans_choice('messages.apples', 1);
// => 有 1 个苹果

echo trans_choice('messages.apples', 5);
// => 有多个苹果
```

### 指定范围的复数

也可指定更精确的范围：

```php theme={null}
'apples' => '{0} 没有苹果|[1,19] 有若干苹果|[20,*] 有很多苹果',
```

```php theme={null}
echo trans_choice('messages.apples', 0);
// => 没有苹果

echo trans_choice('messages.apples', 10);
// => 有若干苹果

echo trans_choice('messages.apples', 50);
// => 有很多苹果
```

### 复数中的占位符

`:count` 会展示数量。第 3 参数可传入额外替换值。

```php theme={null}
// lang/ja/messages.php
return [
    'minutes_ago' => '{1} :value 分钟前|[2,*] :value 分钟前',
    'items'       => '共 :count 项',
];
```

```php theme={null}
echo trans_choice('messages.minutes_ago', 5, ['value' => 5]);
// => 5 分钟前

echo trans_choice('messages.items', 3);
// => 共 3 项
```

## 覆盖包的语言文件

若第三方包自带语言文件，可以在 `lang/vendor/{包名}/{语言}/` 下放置同名文件进行覆盖。

例如自定义 `skyrim/hearthfire` 包的英文消息：

```text theme={null}
lang/
└── vendor/
    └── hearthfire/
        └── en/
            └── messages.php
```

```php theme={null}
// lang/vendor/hearthfire/en/messages.php
return [
    'welcome' => '自定义消息',
    // 只定义需要覆盖的键，其他键会读原文件
];
```

## 实践示例：中英切换中间件

一个根据 URL 路径、Session 或用户设置自动切换 locale 的中间件示例：

<Steps>
  <Step title="创建中间件">
    ```shell theme={null}
    php artisan make:middleware SetLocale
    ```
  </Step>

  <Step title="实现中间件">
    ```php theme={null}
    <?php

    namespace App\Http\Middleware;

    use Closure;
    use Illuminate\Http\Request;
    use Illuminate\Support\Facades\App;
    use Symfony\Component\HttpFoundation\Response;

    class SetLocale
    {
        public function handle(Request $request, Closure $next): Response
        {
            // 支持的 locale
            $supportedLocales = ['ja', 'en'];

            // Session 中已存则使用它
            $locale = $request->session()->get('locale');

            // 若无则参考浏览器的 Accept-Language
            if (! $locale) {
                $browserLocale = substr($request->getPreferredLanguage($supportedLocales) ?? 'ja', 0, 2);
                $locale = in_array($browserLocale, $supportedLocales) ? $browserLocale : 'ja';
            }

            App::setLocale($locale);

            return $next($request);
        }
    }
    ```
  </Step>

  <Step title="注册中间件">
    在 `bootstrap/app.php` 中注册：

    ```php theme={null}
    use App\Http\Middleware\SetLocale;

    ->withMiddleware(function (Middleware $middleware) {
        $middleware->append(SetLocale::class);
    })
    ```
  </Step>

  <Step title="添加切换路由">
    ```php theme={null}
    // routes/web.php
    Route::post('/locale/{locale}', function (string $locale) {
        if (! in_array($locale, ['ja', 'en'])) {
            abort(400);
        }

        session(['locale' => $locale]);

        return back();
    })->name('locale.switch');
    ```
  </Step>

  <Step title="在 Blade 模板中添加切换按钮">
    ```blade theme={null}
    <div>
        <form method="POST" action="{{ route('locale.switch', 'ja') }}">
            @csrf
            <button type="submit">日本語</button>
        </form>

        <form method="POST" action="{{ route('locale.switch', 'en') }}">
            @csrf
            <button type="submit">English</button>
        </form>
    </div>
    ```
  </Step>
</Steps>

### 翻译文件结构示例

```text theme={null}
lang/
├── ja.json          # JSON（UI 文案）
├── en.json
├── ja/
│   └── validation.php   # PHP（校验相关）
└── en/
    └── validation.php
```

```json theme={null}
// lang/ja.json
{
    "Dashboard": "仪表盘",
    "Login": "登录",
    "Logout": "登出",
    "Welcome, :name!": "欢迎你，:name！",
    "Save": "保存",
    "Cancel": "取消",
    "Are you sure?": "确定吗？"
}
```

```blade theme={null}
{{-- resources/views/layouts/app.blade.php --}}
<nav>
    <a href="{{ route('dashboard') }}">{{ __('Dashboard') }}</a>
    <span>{{ __('Welcome, :name!', ['name' => auth()->user()->name]) }}</span>

    <form method="POST" action="{{ route('logout') }}">
        @csrf
        <button type="submit">{{ __('Logout') }}</button>
    </form>
</nav>
```

## 小结

<AccordionGroup>
  <Accordion title="翻译字符串的定义方式">
    | 方式     | 文件路径                   | 键示例                  | 获取方式                     |
    | ------ | ---------------------- | -------------------- | ------------------------ |
    | PHP 文件 | `lang/ja/messages.php` | `'welcome' => '...'` | `__('messages.welcome')` |
    | JSON   | `lang/ja.json`         | `"Welcome": "..."`   | `__('Welcome')`          |
  </Accordion>

  <Accordion title="常用函数与门面">
    | 函数 / 方法                       | 用途           |
    | ----------------------------- | ------------ |
    | `__('key')`                   | 获取翻译字符串      |
    | `trans('key')`                | 与 `__()` 等价  |
    | `trans_choice('key', $count)` | 获取复数翻译       |
    | `App::setLocale('ja')`        | 按请求切换 locale |
    | `App::currentLocale()`        | 获取当前 locale  |
    | `App::isLocale('ja')`         | 判断当前 locale  |
  </Accordion>

  <Accordion title="Blade 用法">
    ```blade theme={null}
    {{-- 基础 --}}
    {{ __('messages.welcome') }}

    {{-- 含占位符 --}}
    {{ __('messages.welcome', ['name' => $user->name]) }}

    {{-- 复数 --}}
    {{ trans_choice('messages.apples', $count) }}
    ```
  </Accordion>
</AccordionGroup>


## Related topics

- [Laravel MCP](/zh/mcp.md)
- [Socialite - Laravel Bluesky](/zh/packages/laravel-bluesky/socialite.md)
- [自定义提供商](/zh/packages/laravel-copilot-sdk/custom-providers.md)
- [Laravel Sentinel — 路由保护中间件调查](/zh/blog/sentinel-introduction.md)
- [laravel/symfony-on-cloud — 在 Laravel Cloud 上运行 Symfony 应用](/zh/blog/symfony-on-cloud-introduction.md)
