로컬라이제이션

키-텍스트 조회, 런타임 언어 전환, 그리고 플레이어가 언어를 바꾸면 실시간으로 갱신되는 UI 텍스트.

UI 텍스트를 하드코딩하지 않고 게임을 여러 언어로 출시하세요.

인터페이스ILocalizationService
끄기 스위치NullLocalization
어셈블리CommonGameSystem.Localization (선택적 — Unity 내장 uGUI 패키지 필요)
시작부팅 시 자동 등록 — 번역 테이블은 직접 로드합니다

주요 기능

메뉴에 "Start"를 직접 쓰는 대신 "menu.start" 같은 안정적인 식별자 — 를 씁니다. Localization 서비스가 런타임에 그 키를 현재 언어의 텍스트로 해석합니다. 플레이어가 설정 메뉴에서 언어를 바꾸면 화면 위의 모든 LocalizedText 컴포넌트가 즉시 갱신됩니다 — 씬 리로드도, 재시작도 없습니다.

누락된 번역이 게임을 크래시시키는 일은 없습니다. 현재 언어에서 실패한 조회는 폴백 언어(기본값은 영어)로 폴백하고, 그것마저 실패하면 키 자체가 반환됩니다. UI에는 항상 무언가가 표시됩니다.

번역은 LocalizationTableAsset ScriptableObject에 담깁니다 — Inspector에서 채우거나 스프레드시트에서 생성하는 평범한 데이터 에셋입니다. 언어는 표준 BCP-47 언어 태그로 식별합니다. 브라우저와 운영체제가 쓰는 것과 같은 짧은 코드입니다: "en", "ko", "ja", "zh-CN" 등.

모듈이 자체 선택적 어셈블리에 들어 있으므로, 프로젝트에서 uGUI를 제거하면 이 어셈블리만 제외됩니다 — 나머지 프레임워크는 여전히 컴파일되고 부팅합니다.

빠른 시작

시작 스크립트에서 서비스를 리졸브하고 테이블을 한 번 로드하세요:

using CommonGameSystem.Core;
using UnityEngine;

[DefaultExecutionOrder(100)] // Run after the framework has started.
public class GameStartup : MonoBehaviour
{
    [SerializeField] private LocalizationTableAsset[] tables; // assign in the Inspector

    private ILocalizationService _loc;

    private void Awake()
    {
        _loc = ServiceLocator.Resolve<ILocalizationService>();
        _loc.LoadTables(tables);

        // Optional: start in the player's OS language.
        _loc.SetLocale(LocaleId.FromSystemLanguage(Application.systemLanguage));
    }
}

이후에는 어떤 스크립트든 Get으로 텍스트를 가져올 수 있습니다:

string title = _loc.Get("menu.title");

API 레퍼런스

조회

  • string Get(string key) — 키를 해석합니다: 현재 언어 → 폴백 언어 → 키 자체. 절대 예외를 던지지 않습니다.
  • string Get(string key, params object[] args) — 같은 조회를 수행한 뒤 invariant culture로 string.Format을 적용합니다. 잘못된 형식 문자열은 예외를 던지는 대신 형식이 적용되지 않은 텍스트를 반환합니다.
  • bool TryGet(string key, out string value) — 부작용 없는 조회. 키가 없으면 false를 반환합니다(value에는 키가 들어감). 아무것도 로그하지 않습니다 — 경고 스팸 없이 키를 탐색할 때 유용합니다.

언어 전환

  • void SetLocale(string localeId) — 활성 언어를 전환합니다. Event Bus에 LocaleChanged 이벤트를 발행하고, Configuration 서비스를 통해 선택을 저장하므로 세션 간에 유지됩니다.
  • string CurrentLocale { get; } — 활성 로케일 id.
  • IReadOnlyList<string> AvailableLocales { get; } — 로드된 테이블에서 발견된 로케일 id 목록. 언어 선택 UI를 이 목록으로 구동하세요.
  • string GetLocaleDisplayName(string localeId) — 해당 언어 자체로 표기한 언어 이름(예: 한국어라면 "한국어") — 언어 선택 UI용입니다. 설정되지 않았으면 id로 폴백합니다.

셋업

  • void LoadTables(IReadOnlyList<LocalizationTableAsset> tables) — 번역 테이블을 로드하고 저장된(또는 기본) 언어를 활성화합니다. 시작 시 한 번 호출하세요.

정적 헬퍼

  • LocaleId.FromSystemLanguage(SystemLanguage)Application.systemLanguage를 BCP-47 태그로 매핑합니다(예: SystemLanguage.Korean"ko"). 첫 실행 시 플레이어의 OS 언어를 자동 감지할 때 사용하세요.

LocalizedText 컴포넌트

정적 UI 텍스트라면 코드를 아예 건너뛰세요: 아무 uGUI Text나 TextMeshPro 컴포넌트에 LocalizedText 컴포넌트를 붙이고 Inspector에서 키를 설정하면 됩니다. 컴포넌트는 활성화될 때 텍스트를 가져오고, 언어가 바뀔 때마다 다시 가져옵니다.

값이 들어가는 동적 텍스트에는 SetArgs를 사용하세요:

using CommonGameSystem.Core;
using UnityEngine;

public class ScoreDisplay : MonoBehaviour
{
    private LocalizedText _locText;

    private void Start()
    {
        _locText = GetComponent<LocalizedText>();
    }

    public void SetScore(int newScore)
    {
        // Table entry: "score.current" = "Score: {0}"  →  displays "Score: 42"
        _locText.SetArgs(newScore);
    }
}

예제: 언어 버튼

using CommonGameSystem.Core;
using UnityEngine;

public class LanguageMenu : MonoBehaviour
{
    private ILocalizationService _loc;

    private void Start()
    {
        _loc = ServiceLocator.Resolve<ILocalizationService>();
    }

    // Wire this to a UI button, passing "en", "ko", "ja", ...
    public void OnLanguageButtonClicked(string localeId)
    {
        _loc.SetLocale(localeId); // Every LocalizedText on screen updates live.
    }
}

선택은 자동으로 저장됩니다 — 다음 세션은 플레이어가 고른 언어로 시작합니다.

끄기

ServiceLocator.Replace<ILocalizationService>(new NullLocalization());

Get(key)는 키를 그대로 반환하고, SetLocaleLoadTables는 조용한 no-op이며, 아무것도 로그되지 않습니다. 경량 빌드나 테스트에서 로컬라이제이션을 완전히 걷어낼 때 사용하세요.

흔한 함정

  • 메인 스레드 전용. 워커 스레드에서 Get이나 SetLocale을 호출하면 InvalidOperationException을 던집니다. 모든 MonoBehaviour 콜백은 안전합니다.
  • 서비스를 캐시하세요. Update()에서 리졸브하면 매 프레임 딕셔너리 조회를 반복하게 됩니다. 대신 Awake()Start()에서 참조를 캐시하세요.
  • 로케일 id는 정확히 일치해야 합니다. "EN""en"이 아니고, Application.systemLanguage.ToString()"ko"가 아니라 "Korean"을 냅니다. 자동 감지에는 LocaleId.FromSystemLanguage()를, 테이블에는 소문자 BCP-47 태그를 쓰세요.
  • LocalizedText는 활성화될 때마다 다시 구독합니다. 풀링된 UI 프리팹(데미지 숫자, 토스트)에서는 풀 사이클마다 LocaleChanged에 구독하고 해제합니다. 이것은 올바른 동작입니다 — 재활성화가 현재 언어를 반영합니다 — 하지만 고빈도 풀은 주의하세요. 동적 텍스트 갱신에는 SetArgs를 우선하세요.
  • 종료 순서. 서비스는 시작 순서의 역순으로 종료되므로, Localization 서비스가 Event Bus보다 먼저 dispose될 수 있습니다. 그 뒤에 LocaleChanged 이벤트가 도착하면 LocalizedText가 조용히 삼킵니다 — 텍스트는 갱신되지 않지만 아무것도 크래시하지 않습니다.
  • 커스텀 타입은 IL2CPP에서 link.xml이 필요합니다. 로컬라이제이션 키를 담는 자체 설정 타입을 직렬화한다면 프로젝트의 link.xml이 이를 보존하는지 확인하세요. 출하되는 프레임워크 어셈블리는 자신의 타입을 이미 보존합니다.

관련 페이지