> ## 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 13の翻訳ローダーを読み解き、PHP翻訳の部分上書き、JSON翻訳の共有キー、公開済み翻訳を壊さない更新方法を解説します。

## このページで達成すること

パッケージのメッセージを多言語で配布し、利用アプリケーションが必要な文言だけを変更できるようにします。翻訳キー・プレースホルダーを公開APIとして扱い、パッケージ更新時にカスタマイズを維持する方法まで整理します。

[ローカライゼーション](/jp/localization)がアプリケーションでの基本操作を、[Laravelパッケージ開発](/jp/advanced/package-development)が登録と公開の基本を扱います。このページはLaravel 13の `ServiceProvider`、`FileLoader`、`Translator` の実装に踏み込みます。

<Info>
  `loadTranslationsFrom()` は読み込み先の登録、`publishes()` はファイルのコピー先の登録です。翻訳を使うために、利用者が必ず `vendor:publish` を実行する必要はありません。
</Info>

## PHP翻訳を名前空間付きで配布する

パッケージ専用のキーを持ちたい場合は、PHP配列形式と名前空間を使います。以下は `Acme\Courier` というパッケージの例です。

```text theme={null}
courier/
├── src/
│   └── CourierServiceProvider.php
└── lang/
    ├── en/
    │   └── messages.php
    └── ja/
        └── messages.php
```

`lang/ja/messages.php` に日本語のデフォルト値を用意します。

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

return [
    'delivery' => [
        'queued' => ':nameさんへの配送を受け付けました。',
        'failed' => '配送できませんでした。',
    ],
];
```

`lang/en/messages.php` にフォールバック用の英語も用意します。

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

return [
    'delivery' => [
        'queued' => 'Delivery to :name has been queued.',
        'failed' => 'Delivery failed.',
    ],
];
```

サービスプロバイダーの `boot()` で読み込みと任意の公開を登録します。

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

namespace Acme\Courier;

use Illuminate\Support\ServiceProvider;

class CourierServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        $this->loadTranslationsFrom(__DIR__.'/../lang', 'courier');

        $this->publishes([
            __DIR__.'/../lang' => $this->app->langPath('vendor/courier'),
        ], 'courier-translations');
    }
}
```

使用側は名前空間・ファイル名・配列キーを指定します。言語コードはLaravelの設定に合わせて `ja` を使います。このドキュメントサイトのURLに使う `jp` とは別です。

```php theme={null}
echo __('courier::messages.delivery.queued', ['name' => '山田'], 'ja');
```

名前空間の `courier` は `loadTranslationsFrom()` の第2引数です。Composerのパッケージ名から自動で決まるわけではありません。

## PHP翻訳はファイルを丸ごと置き換えない

利用アプリケーションでは、標準の言語ディレクトリなら `lang/vendor/courier/ja/messages.php` に変更するキーだけを書けます。言語ディレクトリを変更している場合も、`$this->app->langPath('vendor/courier')` の配下を使います。

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

return [
    'delivery' => [
        'queued' => ':name様への配送予約が完了しました。',
    ],
];
```

この例では `queued` だけが変わり、`failed` はパッケージの日本語翻訳を使います。

### FileLoaderの読み込み順序

`ServiceProvider::loadTranslationsFrom()` はTranslatorの解決後に名前空間を登録します。実際のファイル取得は翻訳が要求されたときに行われます。

`FileLoader::loadNamespaced()` は、登録されたパッケージの言語ファイルを読み込み、その配列を `loadNamespaceOverrides()` に渡します。そこでローダーの各言語パスにある `vendor/{namespace}/{locale}/{group}.php` を読み、`array_replace_recursive()` で置換します。

```mermaid theme={null}
flowchart TD
    A["courier::messages.delivery.queued<br>locale: ja"] --> B["パッケージの<br>lang/ja/messages.php"]
    B --> C["アプリケーションの<br>lang/vendor/courier/ja/messages.php"]
    C --> D["array_replace_recursiveで<br>指定キーを置換"]
    D --> E["翻訳文字列の取得と<br>プレースホルダー置換"]
```

標準の `TranslationServiceProvider` はフレームワークの言語パスとアプリケーションの言語パスを、この順序でローダーに渡します。追加パスを登録する拡張がある場合も、後から読み込む上書き配列が同じキーを優先します。

| 状態 | 結果 |
| - | - |
| アプリケーション側にキーがある | その値でパッケージの値を上書きする |
| 同じファイルはあるがキーがない | パッケージの同じ言語の値を維持する |
| 要求した言語でキーが見つからない | 通常は `fallback_locale` のPHP翻訳を探す |
| フォールバックにもキーがない | 標準動作では要求したキーを返す |

<Warning>
  名前空間が登録されていない場合、`FileLoader::loadNamespaced()` は空の配列を返します。`lang/vendor/courier` にファイルを置くだけでは、サービスプロバイダーの登録漏れを補えません。また、同じ名前空間を別パッケージが登録すると登録先が置き換わるため、衝突しない名前を選びます。
</Warning>

## JSON翻訳はパッケージ専用の名前空間を持たない

文章をキーにするJSON翻訳は、次のようにディレクトリを登録します。これは先ほどのPHP翻訳とは別の選択肢です。

```php theme={null}
public function boot(): void
{
    $this->loadJsonTranslationsFrom(__DIR__.'/../lang');
}
```

パッケージの `lang/ja.json` の例です。

```json theme={null}
{
  "Courier delivery queued": "配送を受け付けました。"
}
```

```php theme={null}
echo __('Courier delivery queued', [], 'ja');
```

`loadJsonTranslationsFrom()` には名前空間の引数がありません。登録されたJSON翻訳は、他のパッケージやアプリケーションと同じキー空間を共有します。

### JSONの上書き先はアプリケーションのja.json

`FileLoader::loadJsonPaths()` は、登録されたJSONパスを先に、その後に通常の言語パスを読み、`array_merge()` します。標準構成ではアプリケーションの `lang/ja.json` の同じ文字列キーがパッケージの値を上書きします。

```json theme={null}
{
  "Courier delivery queued": "配送予約が完了しました。"
}
```

* パッケージ間で同じ文字列キーを使うと、後から読み込むJSONの値が優先されます。プロバイダーの順序に頼る設計は避けます。
* `lang/vendor/courier/ja.json` は、標準のJSONローダーの上書き先ではありません。PHP用の公開設定をそのままJSONに流用しても、この場所は自動で読まれません。
* アプリケーションの `lang/ja.json` へパッケージのJSONを `publishes()` しても、ファイル内容はマージされません。既存翻訳を壊さないよう、必要なキーだけを利用者が追加する手順を案内します。

<Warning>
  `Translator::get()` は、まず要求した言語のJSONを確認し、見つからなければPHP形式のキーとして探索します。PHP翻訳と同じようにフォールバック言語のJSONまで順番に探す処理ではありません。JSONを英語の文章キーにする場合は、翻訳がないと原文のキーが表示される標準動作と区別してください。
</Warning>

PHP翻訳の名前空間は他パッケージのPHPキーを分離します。ただし、`Translator::get()` はJSONの完全一致キーを先に調べるため、JSONに `courier::messages.delivery.queued` のようなキーを定義するとPHP側より優先されます。通常は文章キーとPHP形式のキーを混在させない方針にします。

## 公開済み翻訳を壊さずに更新する

PHP翻訳を一括公開したい利用者には、対象を絞ったコマンドを案内できます。

```bash theme={null}
php artisan vendor:publish --provider="Acme\Courier\CourierServiceProvider" --tag=courier-translations
```

ただし、すべてのデフォルト値をコピーすると、そのコピーも以後は上書き値です。パッケージ側で誤字を修正しても、同じキーが公開済みファイルに残っていれば新しい値は見えません。一方、コピーにない新しいキーはパッケージ側から補われます。

<Tip>
  少数の文言だけを変更する場合は、全ファイルを公開せず、上書きファイルに必要なキーだけを置く方が更新を取り込みやすくなります。これはPHP翻訳の部分上書きを利用する運用です。
</Tip>

長期保守では、次の順序で更新を設計します。

1. **キーと名前空間を維持する** — キーの削除・移動は、利用者の `__()` 呼び出しと上書き先に影響します。新しいキーを追加し、旧キーを残す移行期間を検討します。
2. **プレースホルダーを維持する** — `:name` を `:recipient` に変えると、呼び出し側の置換配列も変更が必要です。翻訳ファイルだけの修正と考えないようにします。
3. **公開済みファイルを差分確認する** — 利用者の上書きと新しいデフォルトを比較します。不要になった上書きキーを削除すると、パッケージの値に戻せます。
4. **無条件の再公開を避ける** — `--force` による再公開は利用者のカスタマイズを上書きします。JSONをアプリケーションのファイルへコピーする設計では、他の翻訳まで失う可能性があります。
5. **常駐プロセスで確認する** — `Translator::load()` は名前空間・グループ・言語ごとの配列をインスタンス内に保持します。既に読み込み済みのTranslatorが残るプロセスでは、ファイル変更だけで再読込されるとは限りません。運用に応じてワーカー等を再起動します。

公開操作の対象選択や上書きオプションは[パッケージの公開アセットと更新](/jp/advanced/package-assets)で、バージョン更新時の互換性判断は[パッケージのバージョン互換性管理](/jp/advanced/package-versioning)で補足しています。

## 利用アプリケーションで確認する項目

サービスプロバイダーを登録した検証用アプリケーションで、次の組み合わせを確認します。パッケージ内のテスト環境構築は[パッケージのテスト](/jp/advanced/package-testing)を参照してください。

| ケース | 確認内容 |
| - | - |
| PHP翻訳を公開していない | パッケージの日本語・英語が取得できる |
| 日本語の `queued` だけ上書き | `queued` が変わり、`failed` はデフォルトのまま |
| パッケージ更新でキーを追加 | 既存の上書きファイルにないキーも取得できる |
| 要求言語にキーがない | PHP翻訳は設定したフォールバック言語から取得できる |
| JSONに同じキーを定義 | 標準構成ではアプリケーションのJSONが優先される |
| JSONを `lang/vendor/courier` にだけ配置 | 標準構成ではJSONの上書きにならない |
| `:name` を含む翻訳 | 呼び出し側の置換配列と一致し、未置換の文字列が残らない |

読み込み後に上書きファイルを作るテストでは、Translatorの既存の読み込み結果が影響しないようにします。ファイルを先に用意してから取得するか、ケースごとに新しいアプリケーションインスタンスを使って確認します。

## 参照した一次情報

公式ドキュメントは最新のデフォルトブランチ `13.x`、内部実装は参照時点の最新リリース `v13.35.0` を確認しています。

* [Laravel公式ドキュメント: パッケージの言語ファイル](https://github.com/laravel/docs/blob/13.x/packages.md#language-files)
* [Laravel公式ドキュメント: パッケージ翻訳の上書き](https://github.com/laravel/docs/blob/13.x/localization.md#overriding-package-language-files)
* [ServiceProvider: 翻訳の登録](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Support/ServiceProvider.php)
* [TranslationServiceProvider: 標準の言語パス](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Translation/TranslationServiceProvider.php)
* [FileLoader: PHPの再帰的置換とJSONの読み込み順序](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Translation/FileLoader.php)
* [Translator: JSON優先の取得と読み込み済み配列](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Translation/Translator.php)
* [公式テスト: 翻訳ローダー](https://github.com/laravel/framework/blob/v13.35.0/tests/Translation/TranslationFileLoaderTest.php)


## Related topics

- [Laravelパッケージ開発](/jp/advanced/package-development.md)
- [応用トピック](/jp/advanced/index.md)
- [パッケージビューの上書きと更新](/jp/advanced/package-views.md)
- [パッケージの公開アセットと更新](/jp/advanced/package-assets.md)
- [パッケージのマイグレーション公開と更新](/jp/advanced/package-migrations.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.