Localizable.strings를 String Catalog(.xcstrings)로 마이그레이션하기

업데이트 · LocalePilot

Xcode 15부터 Apple은 String Catalog를 쓰도록 권하고 있습니다. 모든 언어를 하나의 JSON 파일에 담고, 복수형과 기기별 변형도 다룹니다. 마이그레이션은 클릭 두 번이면 끝납니다. 주의가 필요한 것은 그 뒤의 diff입니다.

브라우저에서 바로 단독 .strings 파일을 카탈로그로 변환해 보세요. 무료이고 가입이 필요 없으며, 아무것도 업로드되지 않습니다.

마이그레이션 전에

예상치 못한 문제 대부분을 막아 주는 두 가지 준비입니다.

  • 깨끗한 Git 트리와 전용 브랜치. 나중에 확인할 것은 모두 diff에 있으므로, 그 diff만 따로 떼어 두고 싶기 때문입니다.
  • 마이그레이션 전에 빌드가 통과할 것. Xcode는 빌드할 때 코드를 스캔해 카탈로그를 채웁니다. 프로젝트가 컴파일되지 않으면 카탈로그는 불완전한 채로 남고, 그 이유도 알 수 없습니다.

마이그레이션 절차

프로젝트 내비게이터에서 Localizable.strings를 오른쪽 클릭하고 Migrate to String Catalog를 고릅니다. Xcode가 대상 파일과 타깃을 보여 줍니다. 정확한 문구는 Xcode 버전마다 다를 수 있지만 흐름은 같습니다.

fr.lproj/Localizable.strings, de.lproj/Localizable.strings 등의 파일은 모든 언어를 담은 하나의 Localizable.xcstrings로 대체됩니다. .strings 파일의 주석은 각 키의 주석이 됩니다.

파일의 모습

String Catalog는 JSON입니다. 간단한 키의 예:

{
  "sourceLanguage" : "en",
  "strings" : {
    "welcome_user" : {
      "comment" : "Home screen greeting",
      "localizations" : {
        "fr" : {
          "stringUnit" : {
            "state" : "translated",
            "value" : "Bon retour, %@ !"
          }
        }
      }
    }
  },
  "version" : "1.0"
}

복수형은 variations 아래에 저장됩니다.

"items_left" : {
  "localizations" : {
    "fr" : {
      "variations" : {
        "plural" : {
          "one" : { "stringUnit" : { "state" : "translated", "value" : "%lld article restant" } },
          "other" : { "stringUnit" : { "state" : "translated", "value" : "%lld articles restants" } }
        }
      }
    }
  }
}

서식에 주목하세요. 콜론 앞마다 공백이 있으며, 이것이 Xcode의 방식입니다. 파일을 "표준" JSON으로 다시 쓰는 도구를 쓰면 번역은 하나도 바뀌지 않았는데 거대한 diff가 생깁니다.

diff에서 확인할 것

Stale로 표시된 키. 빌드 후 Xcode는 코드에서 더 이상 찾을 수 없는 키를 표시합니다. 반드시 죽은 키는 아닙니다. 런타임에 만들어지는 키(NSLocalizedString(variable, comment: ""), 설정 파일에서 읽는 키)는 스캔으로 보이지 않습니다. 삭제하기 전에 확인하세요.

코드에서 추출된 새 키. 빌드가 Localizable.strings에 넣은 적 없는 문자열을 추가할 수 있습니다. 대표적으로 SwiftUI Text("…") 리터럴이 있으며, 기본적으로 현지화 대상입니다. 이런 문자열은 번역되지 않은 상태로 들어오므로 Xcode가 언어별로 보여 주는 비율이 떨어지는데, 이는 정상입니다.

상태. 모든 값에는 상태가 있습니다: translated, needs_review, 그리고 아직 아무도 번역하지 않은 문자열의 new. 석 달 뒤 무엇이 실제로 검토되었는지 알 수 있는 단서가 됩니다.

형식 지정자. Text("\(count) items left") 같은 SwiftUI 문자열은 %lld items left라는 키를 만듭니다. 예전 .strings에서 같은 문장에 %d를 썼다면 거의 중복된 키 두 개가 생깁니다. 코드가 실제로 쓰는 쪽을 남기세요.

예전 iOS 버전은요?

iOS 17은 필요하지 않습니다. 카탈로그는 소스 형식이며, 빌드할 때 Xcode가 앱 번들 안의 일반 .strings 파일로 컴파일합니다. 배포 대상은 바뀌지 않습니다.

Xcode 밖에서 카탈로그 편집하기

문자열 몇 개라면 Xcode 편집기로 충분합니다. 한 언어 전체를 검토하거나, 한꺼번에 번역하거나, 플레이스홀더를 검사하려면 전용 도구가 도움이 됩니다. LocalePilot은 .xcstrings 파일을 열고 Xcode 스타일로 다시 씁니다. 바뀌지 않은 카탈로그는 바이트 단위로 동일하게 나오고, 편집은 해당 줄만 바꾸며, 편집한 값은 translated 상태가 됩니다. 복수형과 기기별 변형은 편집할 수 있으며, 치환(%#@count@)을 쓰는 키와 shouldTranslate: false가 붙은 키는 그대로 유지되어 Xcode에서 편집합니다.

자주 묻는 질문

String Catalog를 쓰려면 iOS 17이 필요한가요?

아닙니다. Xcode는 빌드할 때 카탈로그를 일반 현지화 파일로 컴파일합니다. 앱의 배포 대상은 바뀌지 않습니다.

String Catalog에서 Stale은 무슨 뜻인가요?

마지막 빌드에서 Xcode가 코드에서 그 키를 찾지 못했다는 뜻입니다. 삭제하기 전에 런타임에 만들어지는 키가 아닌지 확인하세요.

마이그레이션을 되돌리려면 어떻게 하나요?

전용 브랜치에서, 바로 직전에 커밋한 뒤 마이그레이션하세요. Git에서 되돌리면 .strings 파일이 돌아옵니다.