> ## 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의 로컬라이제이션 기능은 여러 언어로 번역 문자열을 취득하는 편리한 구조를 제공합니다.
애플리케이션 내에서 여러 언어를 지원하기 위해 사용합니다.

번역 문자열의 관리 방법은 2종류가 있습니다.

| 방법            | 특징                                                                |
| ------------- | ----------------------------------------------------------------- |
| **PHP 파일 형식** | `lang/ja/messages.php`와 같은 키·값 배열. 밸리데이션 에러 등 기능별로 정리하고 싶은 경우에 적합 |
| **JSON 형식**   | `lang/ja.json`에 번역 문자열을 그대로 키로 정의. 번역 대상이 많은 애플리케이션에 권장           |

<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` 파사드의 `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 파일을 작성합니다. 키·값 형식의 배열을 반환합니다.

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

## 플레이스홀더

번역 문자열에 `:이름` 형식의 플레이스홀더를 매입할 수 있습니다.

```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`로 수량을 표시할 수 있습니다. 세 번째 인수로 추가의 치환값을 전달할 수 있습니다.

```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 경로나 세션, 사용자 설정에 기반해 로케일을 자동 전환하는 미들웨어의 구현 예시입니다.

<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'];

            // 세션에 로케일이 저장되어 있다면 그것을 사용
            $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')`        | 요청 단위로 로케일을 변경     |
    | `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>


## Related topics

- [Eloquent 시리얼라이제이션](/ko/eloquent-serialization.md)
- [Laravel 12에서 13으로의 업그레이드 가이드](/ko/blog/upgrade-12-to-13.md)
- [Laravel 13 신기능 정리](/ko/blog/laravel-13-new-features.md)
- [Laravel AI SDK](/ko/ai-sdk.md)
- [Laravel Sentinel — 라우트 보호 미들웨어 조사](/ko/blog/sentinel-introduction.md)
