# Baron SSO i18n(국제화) 처리 정책

## 1. 개요 (Overview)
본 문서는 Baron SSO 시스템(Backend, UserFront, AdminFront, DevFront)의 다국어 지원을 위한 표준 정책을 정의합니다. 
모든 UI 텍스트와 서버 메시지는 **하드코딩을 지양하고, 약속된 코드값(Key)을 기반으로 렌더링**해야 합니다.

---

## 2. 기본 원칙 (Core Principles)

1.  **Backend는 Key만 전달한다**: API 응답에는 사용자에게 보여질 텍스트(메시지)를 포함하지 않는다. 대신 기계가 해석 가능한 `code`를 반환한다.
2.  **Frontend가 렌더링 주체다**: 모든 번역 데이터(Dictionary)는 프론트엔드 애플리케이션이 보유하며, 백엔드로부터 받은 `code`나 UI의 키값을 매핑하여 적절한 언어로 표시한다.
3.  **URL 기반 로케일 격리**: 언어 설정은 URL 경로(`/{locale}/...`)에 명시적으로 드러나야 한다.
4.  **포맷 및 관리**:
    *   모든 리소스는 **TOML** 포맷을 사용한다. (주석 가능, 가독성 우수)
    *   **`template.toml`**을 기준(Master)으로 삼아, 각 언어 파일(`ko.toml`, `en.toml`)이 키를 빠짐없이 구현했는지 관리한다.

---

## 3. 키(Key) 명명 규칙 (Naming Convention)

메시지 키는 계층 구조를 가지며 `snake_case`와 `dot(.)` 표기법을 혼용하여 체계적으로 관리합니다.
TOML에서는 `[Section]`을 사용하여 계층을 표현합니다.

### 3.1 카테고리 (Category)
가장 상위 레벨에서 메시지의 성격을 정의합니다.

| Prefix (Section) | 설명 | 예시 |
| :--- | :--- | :--- |
| **`[ui]`** | 버튼, 레이블, 메뉴 등 짧은 단어 | `ui.btn.save`, `ui.nav.dashboard` |
| **`[msg]`** | 문장형 알림, 설명 텍스트, 다이얼로그 내용 | `msg.info.saved_success` |
| **`[err]`** | 백엔드 에러 코드 또는 프론트 유효성 검사 에러 | `err.auth.user_not_found` |
| **`[domain]`** | 비즈니스 도메인 용어 (명사 위주) | `domain.user.role` |
| **`[unit]`** | 시간, 화폐, 데이터 단위 | `unit.time.sec` |

### 3.2 TOML 예시 (`template.toml`)

```toml
# UI Elements
[ui]
  [ui.btn]
  save = ""
  cancel = ""
  
  [ui.label]
  password = ""

# Messages
[msg]
  [msg.info]
  saved_success = ""

# Errors (Backend Codes)
[err]
  [err.auth]
  user_not_found = ""
```

---

## 4. 로케일 및 라우팅 정책 (Locale & Routing)

### 4.1 지원 언어 및 Fallback
*   **기본 언어(Default)**: `en` (영어)
*   **지원 언어**: `ko` (한국어), `en` (영어)
*   **매핑 로직**:
    *   브라우저 설정이 `ko`, `ko-KR` 인 경우 → **한국어 (`ko`)** 노출
    *   그 외 모든 언어(`ja`, `zh`, `en-US` 등) → **영어 (`en`)** 노출

### 4.2 URL 구조
모든 페이지는 URL의 첫 번째 경로(Path Segment)로 언어를 구분합니다.

*   `https://sso.baron.io/ko/login` (한국어)
*   `https://sso.baron.io/en/dashboard` (영어)

### 4.3 라우팅 및 리다이렉트 동작
1.  **Root 접속 시 (`/`)**:
    *   사용자의 브라우저 `navigator.language`를 감지합니다.
    *   `ko` 계열이면 `/ko/dashboard`로 리다이렉트합니다.
    *   그 외에는 `/en/dashboard`로 리다이렉트합니다.
    *   *단, 이전에 언어를 선택한 쿠키나 로컬스토리지 값이 있다면 그 값을 우선합니다.*
    *   로컬스토리지 키는 **`locale`**을 사용합니다.
2.  **언어 변경 시**:
    *   모든 화면에는 **언어 선택기(Language Selector)**가 노출되어야 합니다.
    *   변경 시 해당 언어의 URL로 이동(`window.location` 변경 혹은 Router Push)하며, 선택된 언어를 로컬스토리지에 저장합니다.

---

## 5. 아키텍처 및 데이터 흐름

### 5.1 백엔드 (Go Fiber)
백엔드는 번역을 수행하지 않습니다. 오직 상황에 맞는 **Error Code**만 반환합니다.

**응답 예시 (401 Unauthorized):**
```json
{
  "error": "Invalid password", 
  "code": "auth.invalid_credentials", // TOML의 [err.auth] invalid_credentials 와 매핑
  "details": null
}
```

### 5.2 프론트엔드 (React / Flutter)

#### 5.2.1 리소스 공유 및 변환
*   TOML은 개발 및 관리 편의를 위한 포맷입니다.
*   **Build Time** 또는 **Runtime**에 TOML 파일을 로드하여 사용합니다.
    *   **React (Vite)**: `vite-plugin-toml` 등을 사용하거나, 빌드 스크립트로 JSON 변환 후 `i18next`에 주입.
    *   **Flutter**: `toml` 패키지를 사용하여 로드하거나, 전처리 스크립트로 ARB/JSON 변환 후 `easy_localization` 사용.

#### 5.2.2 관리 프로세스 (Template & CI)
1.  **`template.toml`**: 개발자가 새로운 번역 키를 추가할 때 반드시 이 파일에 먼저 정의해야 합니다.
2.  **`ko.toml`, `en.toml`**: 템플릿의 키를 바탕으로 실제 번역 값을 채워 넣습니다.
3.  **UserFront 런타임 리소스 동기화**:
    *   UserFront는 런타임에 `userfront/assets/translations/*.toml`을 직접 읽습니다.
    *   따라서 `locales/ko.toml`, `locales/en.toml`, `locales/template.toml`을 수정한 뒤에는 반드시 아래 동기화 스크립트를 실행해야 합니다.
    *   실행 명령:
        ```bash
        ./scripts/sync_userfront_locales.sh
        ```
    *   이 단계가 누락되면 루트 SoT와 UserFront 실제 표시 문구가 어긋날 수 있습니다.
3.  **CI 검증 (Verification)**:
    *   **Level 1: 리소스 동기화 검사 (`template` vs `lang`)**
        *   `template.toml`에 있는 모든 키가 `ko.toml`, `en.toml`에 존재하는지 재귀적으로 검사합니다.
        *   누락 시 빌드 실패.
    *   **Level 2: 코드 사용성 검사 (`code` vs `template`)**
        *   전체 프론트엔드 소스코드(`src/**/*.{ts,tsx}`, `lib/**/*.dart`)를 스캔하여 번역 함수(`t('key')`, `'key'.tr()`)에 사용된 키를 추출합니다.
        *   **Missing Key**: 코드에는 있는데 `template.toml`에 없는 키를 검출하여 경고 또는 에러를 발생시킵니다.
        *   **Unused Key**: `template.toml`에는 있는데 코드 어디에서도 쓰이지 않는 키를 리포트하여 정리할 수 있게 합니다.

#### 5.2.3 React (Admin/Dev) 구현 가이드
*   **패키지 설치**:
    ```bash
    npm install i18next react-i18next i18next-browser-languagedetector
    npm install -D vite-plugin-toml
    ```
*   **Vite 설정 (`vite.config.ts`)**:
    ```ts
    import { defineConfig } from 'vite';
    import { plugin as toml } from 'vite-plugin-toml';

    export default defineConfig({
      plugins: [toml], // .toml 파일을 모듈로 import 가능하게 함
    });
    ```
*   **i18n 초기화 (`src/i18n.ts`)**:
    ```ts
    import i18n from 'i18next';
    import { initReactI18next } from 'react-i18next';
    import LanguageDetector from 'i18next-browser-languagedetector';

    // TOML 파일 직접 import (JSON 객체로 변환됨)
    import ko from './locales/ko.toml';
    import en from './locales/en.toml';

    i18n
      .use(LanguageDetector)
      .use(initReactI18next)
      .init({
        resources: {
          ko: { translation: ko },
          en: { translation: en },
        },
        fallbackLng: 'en',
        interpolation: {
          escapeValue: false,
        },
      });

    export default i18n;
    ```

#### 5.2.4 Flutter (User) 구현 가이드
Flutter는 런타임에 TOML을 파싱하기 위해 `toml` 패키지와 `easy_localization`의 커스텀 로더를 사용합니다.

#### 5.2.5 UserFront 에러 표시 정책 (Production)
UserFront(`/error`)는 프로덕션에서 다음 규칙으로 에러를 표시합니다.

1. **Internal whitelist 코드**
   - `msg.userfront.error.whitelist.{code}` 메시지를 노출합니다.
2. **ORY bypass 코드**
   - `msg.userfront.error.ory.{code}` 메시지를 노출합니다.
3. **그 외 코드**
   - `unknown_error`로 처리하고 일반 안내 문구(`msg.userfront.error.detail_contact`)를 노출합니다.

코드 집합은 `userfront/lib/core/constants/error_whitelist.dart`를 단일 기준으로 유지합니다.

*   **패키지 추가 (`pubspec.yaml`)**:
    ```yaml
    dependencies:
      easy_localization: ^3.0.0
      toml: ^0.14.0
    
    flutter:
      assets:
        - assets/translations/
    ```
*   **Custom AssetLoader 구현 (`lib/core/i18n/toml_asset_loader.dart`)**:
    ```dart
    import 'dart:ui';
    import 'package:easy_localization/easy_localization.dart';
    import 'package:flutter/services.dart';
    import 'package:toml/toml.dart';

    class TomlAssetLoader extends AssetLoader {
      const TomlAssetLoader();

      @override
      Future<Map<String, dynamic>> load(String path, Locale locale) async {
        final assetPath = '$path/${locale.languageCode}.toml';
        
        try {
          // 1. Asset 파일 읽기
          final String content = await rootBundle.loadString(assetPath);
          
          // 2. TOML 파싱
          final TomlDocument document = TomlDocument.parse(content);
          
          // 3. Map으로 변환하여 반환
          return document.toMap();
        } catch (e) {
          // 로깅 또는 빈 맵 반환
          print('Error loading TOML asset: $assetPath, error: $e');
          return {};
        }
      }
    }
    ```
*   **초기화 (`main.dart`)**:
    ```dart
    void main() async {
      WidgetsFlutterBinding.ensureInitialized();
      await EasyLocalization.ensureInitialized();

      runApp(
        EasyLocalization(
          supportedLocales: const [Locale('en'), Locale('ko')],
          path: 'assets/translations', 
          fallbackLocale: const Locale('en'),
          assetLoader: const TomlAssetLoader(), // 커스텀 로더 적용
          child: const MyApp(),
        ),
      );
    }
    ```

---

## 6. 작업 체크리스트

1.  [ ] **공통**: `locales/template.toml` 정의 및 초기 키셋 구성.
2.  [ ] **CI**: `template.toml` vs `*.toml` 키 동기화 검증 스크립트 작성 (`scripts/verify-i18n.js` or `py`).
3.  [ ] **Admin/DevFront**: Vite TOML 플러그인 설정 및 `react-i18next` 연동.
4.  [ ] **UserFront**: TOML -> JSON 변환 스크립트 추가 및 `easy_localization` 연동.

### 6.1 UserFront 번역 수정 체크포인트

UserFront 번역을 수정할 때는 아래 순서를 기본 절차로 사용합니다.

1. `locales/*.toml` 수정
2. `./scripts/sync_userfront_locales.sh` 실행
3. UserFront 회귀 테스트 실행
   - 예: `cd userfront && flutter test test/english_locale_placeholder_test.dart`
4. 전체 키 정합성 점검
   - 예: `node tools/i18n-scanner/index.js`
