> ## 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` 的 key/value 陣列，適合像驗證錯誤等按功能整理 |
| **JSON 格式**  | 於 `lang/ja.json` 將翻譯字串直接作為 key 定義，建議翻譯量大的應用使用         |

<Info>
  Laravel 預設沒有 `lang` 目錄。要自訂請以 `lang:publish` Artisan 指令發布。
</Info>

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

## 語系設定

### 預設語系

應用程式的預設語言於 `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` 是在指定語言沒有對應翻譯時的備援語言。

### 執行期切換語系

透過 `App` Facade 的 `setLocale()`，可在每次請求中變更語系。

```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);

    // ...
});
```

### 檢查目前語系

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

$locale = App::currentLocale();

if (App::isLocale('ja')) {
    // 日文時的處理
}
```

## 建立語言檔

### PHP 檔案格式

在 `lang/{語言代碼}/` 目錄下建立 PHP 檔案，回傳 key/value 陣列。

```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
```

以預設翻譯字串（英語）作為 key 定義。

```json theme={null}
{
    "Welcome to our application!": "アプリケーションへようこそ！",
    "I love programming.": "プログラミングが大好きです。",
    "Logout": "ログアウト",
    "Dashboard": "ダッシュボード"
}
```

<Tip>
  JSON 格式將英文原句本身當作 key，因此在英文預設顯示時自然可用。若沒有對應的翻譯檔，key（英文原句）會直接顯示。
</Tip>

### PHP 與 JSON 的取捨

<AccordionGroup>
  <Accordion title="適合 PHP 檔案的情境">
    * 想按功能整理，像驗證錯誤訊息
    * 想覆寫 Laravel 內建翻譯（`validation.php`、`auth.php` 等）
    * 需要階層式的 key 管理

    ```php theme={null}
    // lang/ja/validation.php
    return [
        'required' => ':attributeは必須です。',
        'email' => ':attributeは有効なメールアドレスである必要があります。',
    ];
    ```
  </Accordion>

  <Accordion title="適合 JSON 檔案的情境">
    * UI 文案眾多，不想額外設計 key
    * 樣板中直接以英文書寫，其他語言以翻譯檔應對
    * 之後才加入國際化的應用程式

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

## 取得翻譯字串

### `__()` 輔助函式

最常用的方式。PHP 檔案格式時以「檔名.key」形式指定。

```php theme={null}
// PHP 檔案格式：lang/ja/messages.php 的 'welcome' key
echo __('messages.welcome');
// => アプリケーションへようこそ！

// JSON 格式：直接以翻譯原句作為 key
echo __('I love programming.');
// => プログラミングが大好きです。
```

若翻譯字串不存在，指定的 key 會被原樣回傳。

```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>

## 佔位符

可在翻譯字串中嵌入 `:名稱` 形式的佔位符。

```php theme={null}
// lang/ja/messages.php
return [
    'welcome' => 'ようこそ、:nameさん！',
    'greet'   => 'こんにちは、:Nameさん！',  // 首字轉大寫
    'shout'   => 'HEY :NAME!',              // 全部大寫
];
```

在 `__()` 的第 2 個參數傳入替換值陣列。

```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' => '客製化的訊息',
    // 只定義想覆寫的 key，其他仍會從原檔案讀取
];
```

## 實戰範例：中英切換的中介軟體

以下是根據 URL 路徑、session 或使用者設定自動切換語系的中介軟體實作。

<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
        {
            // 支援的語系
            $supportedLocales = ['ja', 'en'];

            // 若 session 保存了語系則優先使用
            $locale = $request->session()->get('locale');

            // session 沒有時參考瀏覽器的 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="翻譯字串的定義方式">
    | 方式     | 檔案路徑                   | key 範例               | 取得方式                     |
    | ------ | ---------------------- | -------------------- | ------------------------ |
    | PHP 檔案 | `lang/ja/messages.php` | `'welcome' => '...'` | `__('messages.welcome')` |
    | JSON   | `lang/ja.json`         | `"Welcome": "..."`   | `__('Welcome')`          |
  </Accordion>

  <Accordion title="常用函式 / Facade">
    | 函式 / 方法                       | 用途          |
    | ----------------------------- | ----------- |
    | `__('key')`                   | 取得翻譯字串      |
    | `trans('key')`                | 與 `__()` 相同 |
    | `trans_choice('key', $count)` | 取得複數形式翻譯    |
    | `App::setLocale('ja')`        | 每次請求中變更語系   |
    | `App::currentLocale()`        | 取得目前語系      |
    | `App::isLocale('ja')`         | 檢查目前語系      |
  </Accordion>

  <Accordion title="在 Blade 中的使用">
    ```blade theme={null}
    {{-- 基本 --}}
    {{ __('messages.welcome') }}

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

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