
-
코딩 실습: Xcode로 현지화하기
Xcode를 사용하여 앱을 추가 언어로 현지화하는 방법을 알아보세요. String Catalog를 작성하고, 텍스트를 번역하며, 외부 번역 전문가와 파일을 교환하는 과정을 단계별로 안내합니다. 번역 전문가에게 필요한 맥락을 제공하고 Xcode를 이용해 해당 정보를 자동으로 제공하는 모범 사례를 학습할 수 있습니다. 대형 프로젝트에서 타입 안정성이 있는 Swift 코드를 사용하여 복잡성을 관리하고 문자열 관리를 간소화하는 기술도 자세히 살펴보겠습니다.
챕터
리소스
관련 비디오
WWDC23
-
비디오 검색…
안녕하세요 Localization 팀의 Andreas입니다 이번 세션에서는 Xcode에서 현지화을 살펴봅니다
이 세션에는 사전 지식이 필요하지 않습니다 현지화를 위해 앱을 어떻게 설정하는지부터 함께 살펴보고 그다음엔 번역 작업을 하는 분들께 적절한 맥락을 어떻게 제공할지 이야기합니다 마지막으로 프로젝트가 커질수록 생길 수 있는 복잡한 부분들과 이를 관리하는 데 도움이 되는 새로운 기능도 알아봅니다 그럼 시작하겠습니다
이번 세션은 코드 실습 형식입니다 영상에서 보여드리는 과정을 설명란에 있는 샘플 프로젝트에 직접 적용해보실 수 있습니다 Landmarks 프로젝트를 다운로드해 함께 현지화 해 보죠! 저는 Xcode에서 프로젝트를 열어둔 상태입니다 지금 이 Landmarks 버전은 영어로 잘 작동하지만 아직 번역은 없습니다 시작하려면 파일 메뉴에서 문자열 카탈로그를 추가합니다
기본 이름은 Localizable인데 저는 Resources 그룹 안에 넣을 겁니다 애셋 카탈로그도 거기에 있습니다 문자열 카탈로그를 추가했으니 빌드해보겠습니다 문자열 카탈로그가 있는 경우 Xcode는 빌드할 때마다 현지화 가능한 문자열을 자동으로 찾아 카탈로그에 추가합니다 우리가 따로 문자열을 코드와 맞춰줄 필요는 없습니다 Xcode는 어떤 문자열을 현지화 대상으로 인식할까요? 대부분 SwiftUI API는 문자열을 현지화 할 수 있게 하죠 텍스트와 버튼과 같은 보기들이 그렇습니다 나머지 코드에서는 String(localized: )도 문자열을 현지화할 수 있게 합니다
이제 보조 편집기에서 문자열 카탈로그의 문자열이 어떤 코드에서 추출됐는지 보죠
이건 확인 대화상자에서 온 문자열이고
LabeledContent의 제목입니다 이 문자열은 탐색 제목으로 사용됩니다 이건 변수 보간이 포함된 텍스트입니다 SwiftUI 대부분 API는 기본적으로 현지화가 가능합니다 이미 눈치채셨을 수도 있는데 여기 이 문자열은 항목 수를 나타냅니다 %lld 자리에 실행 중에 랜드마크 수가 들어갑니다 개수에 따라 문자열도 달라지게 하고 싶습니다 예를 들면 “1 item” “2 items”처럼요
이럴 땐 빠른 메뉴에서 “복수형에 따라 다름”을 선택하세요
이제 단수와 복수 표현을 각각 입력할 수 있습니다
실행 중 시스템이 숫자에 맞는 표현을 자동으로 선택해줍니다 간단하게 처리됐죠
이제, 현지화는 다른 언어로 번역하는 일이죠 저는 독일어를 할 줄 알아서 문자열 카탈로그에 직접 번역을 입력해보겠습니다 보조 편집기는 닫겠습니다 프로젝트에 언어를 추가하려면 하단 막대의 더하기 버튼을 눌러 독일어를 선택하세요 이렇게만 하면 바로 번역을 시작할 수 있습니다
번역을 추가할수록 상태가 NEW에서 TRANSLATED로 바뀌고 오른쪽에는 초록색 체크표시가 나타납니다
사이드바에 표시되는 전체 번역률도 8%로 올라갔습니다
저는 개발이 더 전문이라 번역 전문가와 함께 작업했습니다 남은 독일어 번역은 전문가가 마무리하는 게 낫겠죠
지금까지 작업한 내용을 넘기려면 Xcode의 제품 메뉴에서 “현지화 내보내기”를 선택하세요 독일어만 번역하고 있으니 독일어만 내보내고 싶습니다
그러면 지금까지 번역된 내용과 아직 번역되지 않은 영어 문자열이 포함된 현지화 카탈로그 파일이 생성됩니다 이 패키지에는 업계 표준 XLIFF 파일이 들어 있어 번역 업체가 쉽게 작업할 수 있습니다 번역이 끝나면 완성된 현지화 카탈로그 파일을 보내 주겠죠
다시 프로젝트로 가져오려면 다시 제품 메뉴로 가서 “현지화 가져오기”를 선택하세요 Xcode 프로젝트를 빌드하는 데 잠시 시간이 걸립니다 모든 문자열이 번역 완료로 표시되고
독일어 번역률은 100%가 됩니다 여러분도 똑같이 하실 수 있습니다 샘플 프로젝트에 완성된 de.xcloc 파일이 포함돼 있어 Xcode에 가져오면 제가 한 것처럼 쓸 수 있습니다 이제 실제로 앱을 실행해 번역이 잘 됐는지 확인해보죠 스키마 편집기를 열고 스키마를 편집합니다 “실행”을 선택하고 “옵션”으로 이동합니다
다음 디버그 실행에서 앱의 언어를 독일어로 변경합니다
이제 Mac에서 앱을 빌드하고 실행하겠습니다
이제 완전히 독일어로 번역된 상태입니다
새 앱을 현지화 설정하는 건 이렇게 간단합니다 이번엔 번역 품질을 높이기 위해 번역자에게 어떤 추가 정보를 줄 수 있는지 살펴보겠습니다 Xcode의 보조 편집기는 문자열 카탈로그와 코드를 나란히 볼 수 있어 유용하지만 번역자들은 보통 코드나 실제 앱을 볼 수 없습니다 더 나은 번역을 위해 추가적인 맥락을 제공해야 합니다
이 맥락은 주석 형식으로 추가할 수 있습니다 코드 안에 직접 입력해도 되고 문자열 카탈로그의 주석 열에 써도 됩니다 주석이 없으면 문자열의 용도를 파악하기 어렵습니다 “Landmarks”라는 단어가 앱 이름을 뜻하는지 지형지물을 뜻하는지 알 수 없죠 “%@는 %@에 포함된다” 같은 문자열 키는 %@ 자리에 무엇이 들어가는지 설명이 필요합니다 그에 따라 번역 방식도 달라지기 때문입니다 좋은 주석은 이 문자열이 어떤 UI 요소에 쓰이는지 알려 줍니다 탭 막대인지, 버튼인지 부제인지 말이죠
주변 인터페이스 요소도 적으면 번역에 큰 도움이 됩니다 예를 들어 첫 번째 문자열이 사이드바 항목이라면 그렇게 적어주고 두 번째 문자열이 리스트 안의 랜드마크 부제라면 그것도 명확히 해야 합니다 또 각 플레이스홀더에 어떤 내용이 들어가는지도 주석에서 설명해야 합니다 첫 번째 플레이스홀더는 랜드마크 이름이고 두 번째는 그 랜드마크가 속한 컬렉션 이름이라면 이걸 주석으로 정확히 적어야 올바르게 번역할 수 있습니다 그래서 좋은 주석을 다는 건 필수입니다
작년부터 문자열 카탈로그는 문자열이 코드의 어디에서 왔는지 추적할 수 있게 되었고 올해는 그 정보를 활용해 더 나은 기능을 제공합니다 바로 Xcode 26의 자동 주석 생성 기능입니다
Xcode는 온디바이스 모델을 사용해 코드를 분석하고 자동으로 주석을 작성해줍니다 직접 확인해보겠습니다 지금까지는 몇몇 문자열에만 직접 주석을 달았는데 주석의 중요성을 알았으니 더 많은 문자열에 맥락을 추가해보죠 여기 주석이 없는 문자열이 있습니다 버튼에 쓰이는 것 같습니다 빠른 메뉴를 열고 “주석 생성”을 선택하세요 Xcode가 문자열의 사용 위치를 분석한 뒤 “컬렉션 삭제를 취소하는 버튼의 텍스트 라벨”이라는 정확한 설명을 생성했습니다
이 문자열도 주석이 없으니 Xcode에게 시켜보겠습니다
“랜드마크 위치 위에 표시되는 라벨” 주석이 생성되었습니다 좋습니다 하나 강조하자면, 생성된 주석도 수정할 수 있습니다 직접 입력한 내용이 항상 우선 적용됩니다 저는 모델을 사용해 추가적인 맥락을 제공하는 것을 좋아합니다 이 문자열이 속성에서 표시된다는 것도 추가하겠습니다
이 기능은 매우 유용하다고 생각합니다 새 문자열이 생길 때 자동으로 주석이 생성되도록 설정하려면 설정을 열고
편집으로 이동합니다
“문자열 카탈로그 주석 자동 생성” 설정을 활성화합니다 이제부터는 코드에 새 문자열이 생기면 Xcode가 자동으로 주석을 생성합니다 덕분에 번역자에게 필요한 맥락을 쉽게 제공합니다
또 번역 도구 개발자들이 이 주석이 Xcode에서 생성된 것인지 알 수 있도록 XLIFF 파일에는 “자동 생성” 주석이 달립니다
다른 도구와의 호환성이나 문자열 카탈로그의 다양한 기능이 궁금하시다면 “String Catalog 소개”를 확인하세요
프로젝트가 커지고 복잡해질수록 Xcode의 추가 기능과 현지화 API들이 정리하는 데 도움이 될 수 있습니다 예를 들어 프로젝트 규모가 커지면 여러 개발자가 함께 작업하게 되고 코드베이스를 확장, 프레임워크 Swift 패키지로 나눕니다 각각은 하나 이상의 문자열 카탈로그를 포함할 수 있습니다 이럴 때는 현지화 API에 번들이라는 매개변수를 추가로 사용해야 합니다 시스템이 실행 중에 문자열을 어디서 찾아야 할지 알려 줍니다 Bundle.main은 항상 메인 앱을 가리킵니다 번들 매개변수를 포함하지 않으면 기본으로 .main이 사용됩니다
올해의 새로운 기능은 #bundle 매크로입니다 현재 타겟의 리소스를 포함하는 번들을 참조할 수 있습니다 코드가 메인 앱에서 실행된다면 메인 앱을 가리키고 아니면 프레임워크나 Swift 패키지의 리소스를 찾습니다 OS 버전이 낮아도 작동하며 알아서 적절하게 처리해줍니다
문자열을 정리하는 다른 방법은 관련 문자열 그룹화입니다 특정 화면, 기능, 사용자 흐름 관련 문자열끼리 묶는 거죠 이런 문자열 그룹을 “테이블”이라고 부릅니다 그리고 각 문자열 카탈로그는 하나의 테이블을 나타냅니다
기본적으로 모든 문자열은 “Localizable” 테이블에 모이는데 이는 문자열 카탈로그를 생성할 때 기본 파일 이름과 일치합니다 물론 이름은 변경 가능합니다 tableName 매개변수로 원하는 카탈로그에 문자열을 넣을 수 있죠 예를 들어, 테이블 이름 “Discover”를 사용하면 “Discover.xcstrings”에 자동으로 문자열이 들어갑니다
Landmarks 앱은 개인 컬렉션 기능도 잘 작동하지만 저는 새로운 콘텐츠를 발견하는 기능을 개발하고 싶습니다 친구들이 공유한 콘텐츠나 큐레이션된 피드를 보여 줍니다 새로운 프레임워크에서 이 기능을 개발해보겠습니다
시작하려면 파일 메뉴를 열고 새 타겟을 추가합니다 “프레임워크”를 검색합니다
이 프레임워크는 새로운 랜드마크를 발견하는 기능이니 이름을 “DiscoverKit”으로 정하겠습니다
새 화면을 처음부터 만들 건데 여기 들어갈 문자열은 별도 테이블로 관리하고 싶습니다 DiscoverKit에 새 파일을 추가하고
“문자열 카탈로그”를 선택해 “Discover”라고 합니다
편의상 오른쪽 편집기에서 코드를 열어 보겠습니다 Shift와 Option 키를 누른 채로 새 Swift 파일을 클릭합니다
그리고 탐색기를 닫아 작업 공간도 확보하겠습니다
이제 모델 레이어에서 열거형으로 기능 개발을 시작하겠습니다 열거형은 콘텐츠가 친구나 또는 엄선된 피드에서 온건지 정합니다 현지화된 제목을 노출하는 속성이 있습니다 구현해 봅시다 String(localized: )로 문자열을 현지화를 위해 노출하고 더 나은 관리를 위해 테이블 인수를 사용하겠습니다 프레임워크에 있기 때문에 번들 인수도 사용해야 합니다 다른 경우에도 같은 방법을 적용해 보겠습니다
열거형이 완성되었습니다 SwiftUI를 가져와 보기를 추가해 화면에 콘텐츠를 표시하겠습니다
아직 비즈니스 로직은 없으니 새로운 게시물 42개가 올라올 것이라는 플레이스홀더만
보여주겠습니다 Xcode가 작업을 하도록 해보죠 스키마를 새로운 프레임워크로 변경하고 빌드하겠습니다
빌드가 완료되면 새 문자열이 카탈로그에 나타납니다
이미 주석도 있죠, 좋네요
나머지 UI 작업의 경우 Xcode 26의 새로운 작업 흐름을 소개하겠습니다 문자열 카탈로그가 도입된 이후 코드에서 문자열을 추출하는 기능이 있었습니다 올해는 여기에 더해 직접 추가한 문자열을 위한 기호도 생성해줍니다 이제 이 새로운 방식으로 보기를 계속 만들어보겠습니다 이번 목표는 탐색 제목과 부제를 추가하는 것입니다 이 보기는 아직 개발 중입니다 문자열 키와 값을 분리해두면 문구를 바꿔도 코드를 수정할 필요가 없습니다
+ 버튼을 클릭해 문자열 카탈로그에 문자열을 추가합니다 많은 프로젝트에서는 키를 대문자로 작성해 의미를 드러냅니다 여기서도 그렇게 하겠습니다 키는 “TITLE”, 값은 “랜드마크 발견”입니다 직접 추가한 문자열이니 주석도 제가 작성해줍니다
속성 검사기가 코드에서 문자열을 사용하는 방법을 알려 줍니다 정말 유용하죠 바로 그렇게 해보겠습니다
탐색 막대에 제목을 표시하려면 .navigationTitle 보기 수정자를 사용합니다
값으로는 마침표를 입력하고 테이블 이름을 입력합니다 Xcode가 테이블 이름을 자동 완성해 주고 해당 테이블의 수동 문자열도 제안해줍니다
정말 간단합니다 번들과 테이블 이름을 수동으로 입력할 필요도 없었습니다
이번엔 부제를 추가해보겠습니다 카탈로그에 새 문자열을 추가하고 “SUBTITLE”이라고 합니다
친구 게시물과 엄선된 피드로부터 온 게시물이 각각 몇 개인지 요약하려는 것입니다 플레이스홀더가 필요하고 형식 지정자가 바로 그 역할이죠 %를 입력하니 Xcode가 다양한 형식 옵션을 제안합니다 숫자를 넣을 거니 정수형 플레이스홀더를 선택합니다
이 플레이스홀더는 친구 게시물 수를 나타내니 “friendsPosts”라고 하겠습니다
엄선된 피드 게시물 수를 위한 플레이스홀더도 추가합니다
마지막으로 주석를 작성하면
이 문자열도 코드에서 쓸 준비가 끝납니다 이번엔 “탐색 부제” 수정자를 사용합니다
`.Discover`라고 입력하면 적절한 테이블이 자동으로 완성됩니다
입력할 일이 훨씬 줄었죠 Xcode가 형식까지 정확하게 제안해 주는 걸 확인하세요 이 새 기능 덕에 직접 입력한 문자열도 쉽게 다룰 수 있고 자동 완성과 컴파일러를 활용해 현지화된 리소스를 안정적으로 불러올 수 있습니다 이제 문자열 내용을 나중에 바꾸고 싶을 때는 문자열 카탈로그에서 값만 수정하면 되고 코드를 건드릴 필요는 없습니다 “OK”를 여러 방식으로 써놓고 한 번에 다 고치고 싶었던 적이 있을 겁니다
Xcode는 문자열의 키와 값을 바탕으로 Swift에 어울리는 기호명을 생성해줍니다 플레이스홀더가 없는 문자열은 다른 정적 속성처럼 접근하고 플레이스홀더가 있는 문자열은 함수로 생성되며 플레이스홀더 이름이 인수 레이블로 사용됩니다 이렇게 생성된 기호는 LocalizedStringResource 타입의 정적 변수나 함수로 제공됩니다 이 기능은 아주 유용합니다 LocalizedStringResource를 사용하는 어디서든 활용합니다 SwiftUI의 텍스트나 버튼 같은 보기 .navigationSubtitle() 같은 보기 수정자도 포함입니다 SwiftUI를 사용하지 않아도 Foundation의 String(localized:)에서 LocalizedStringResource 타입을 사용할 수 있습니다
사용자 설정 보기나 기타 선언에서도 이제 생성된 기호를 활용할 수 있습니다
기본 테이블 이름인 “Localizable”을 쓰면 기호는 LocalizedStringResource 에서 바로 접근할 수 있고 다른 테이블 이름을 사용할 경우 해당 테이블 이름의 네임스페이스 안에 생성됩니다 즉, 코드에서는 테이블 이름을 기준으로 접근하면 됩니다 Xcode 26에서 새 프로젝트는 기호 생성이 활성화되어 있습니다 기존 프로젝트에서 사용하려면 빌드 설정에서 “문자열 카탈로그 기호 생성”을 켭니다
지금까지 살펴본 것처럼 Xcode는 문자열 관리를 위한 2가지 작업 흐름을 지원합니다 코드에서 문자열을 추출하는 방식과 타입 세이프한 API로 참조하는 방식입니다 그렇다면 어떤 작업 흐름을 써야 할까요? 처음에는 문자열 추출 방식을 사용하시길 권장합니다 UI를 작성하면서 바로 문자열을 입력하면 코드를 더 쉽게 읽고 이해할 수 있습니다 이 방식은 Xcode의 자동 주석 생성 기능도 함께 활용할 수 있고 입력할 양도 줄여주면서 번역자에게 맥락도 잘 전달할 수 있습니다 프로젝트가 커지면 문자열을 더 체계적으로 관리하고 싶어질 수 있습니다 그럴 땐 생성된 기호를 사용하는 것이 좋습니다 이를 통해 키와 값을 분리할 수 있고 코드를 바꾸지 않고도 텍스트만 계속 다듬을 수 있습니다 또 Xcode의 자동 완성 기능으로 모든 테이블에서 문자열을 쉽게 참조할 수 있고 마지막으로, 생성된 기호는 프레임워크나 패키지에서도 상용구 코드를 줄일 수 있습니다
두 방식 모두 장점이 있기 때문에 프로젝트에 맞게 자유롭게 선택할 수 있도록 저희는 강력한 리팩토링 기능을 추가했습니다 두 방식 간 전환이 아주 쉽게 가능합니다 DiscoverKit 프레임워크에서 직접 해보겠습니다 탐색 스택 안에 있는 플레이스홀더 텍스트를 기호로 전환해보겠습니다 빠른 메뉴에서 “리팩터링 > 문자열을 기호로 변환”을 선택
미리보기 UI가 열리며 문자열이 기호로 바뀌는 위치를 보여 줍니다 강조된 섹션을 클릭하면 기존 코드와 기호를 비교할 수 있습니다
키 이름을 “feedTitle”로 바꿔 의미를 명확히 하겠습니다 인수 1에도 멋진 이름을 추가할 수 있겠습니다 “newPosts”로 하겠습니다 좋습니다! 리팩토링을 확정합니다
두 방식을 모두 비교해본 뒤 이 테이블은 기호를 쓰는 쪽으로 정했습니다 나머지 두 문자열도 선택해
리팩터링 > 문자열을 기호로 변환을 선택합니다
기호 이름이 마음에 드니 “변환”을 클릭합니다
이렇게 전체 테이블을 한 번에 쉽게 리팩토링할 수 있습니다 여러분도 Xcode의 이런 현지화 기능들을 활용해보세요 처음에는 문자열 추출 방식으로 시작하고 직접 주석을 작성하거나 자동 주석 생성을 이용해 번역자에게 충분한 정보를 제공해 주세요 프로젝트가 복잡해지면 정확한 관리를 위해 생성된 기호 사용을 고려하세요
마지막으로, 문자열 카탈로그에 대한 자세한 내용은 이전 영상 “String Catalog 소개”를 참고하세요 시청해 주셔서 감사합니다 새로운 기능들이 현지화 작업을 더 간편하게 만들어주길 바랍니다
-
-
1:34 - Localizable strings
// import SwiftUI Text("Featured Landmark", comment: "Big headline in the hero image of featured landmarks.") Button("Keep") { } // import Foundation String(localized: "New Collection", comment: "Default name for a new user-created collection.")
-
6:00 - Adding a comment
Text("Delete", comment: "Delete button shown in an alert asking for confirmation to delete the collection.") String(localized: "Shared by Friends", comment: "Subtitle of post that was shared by friends.")
-
9:13 - XLIFF file
// Field for automatically generated comments in the XLIFF <trans-unit id="Grand Canyon" xml:space="preserve"> <source>Grand Canyon</source> <target state="new">Grand Canyon</target> <note from="auto-generated">Suggestion for searching landmarks</note> </trans-unit>
-
9:58 - Localized String in the main app and a Swift Package or Framework
// Localized String in the main app: Text("My Collections", comment: "Section title above user-created collections.") // Localized String in a Swift Package or Framework Text("My Collections", bundle: #bundle, comment: "Section title above user-created collections.")
-
10:56 - Localized String with a tableName parameter
// Localized String in the main app: Text("My Collections", tableName: "Discover", comment: "Section title above user-created collections.") // Localized String in a Swift Package or Framework Text("My Collections", tableName: "Discover", bundle: #bundle, comment: "Section title above user-created collections.")
-
17:31 - Symbol usage
// Symbol usage in SwiftUI Text(.introductionTitle) .navigationSubtitle(.subtitle(friendsPosts: 42)) // Symbol usage in Foundation String(localized: .curatedCollection) // Working with generated symbols in your own types struct CollectionDetailEditingView: View { let title: LocalizedStringResource init(title: LocalizedStringResource) { self.title = title } } CollectionDetailEditingView(title: .editingTitle)
-
-
- 0:00 - 서론
이 코드에서는 모든 기술 수준에 맞는 앱을 현지화하는 방법, 번역가와 협력하는 방법, 프로젝트가 성장함에 따라 복잡성을 관리하는 방법을 알아봅니다.
- 0:38 - 시작하기
Xcode를 사용하여 ‘Landmarks’라는 SwiftUI 앱을 현지화하는 방법을 보여드립니다. 이 프로세스는 각 빌드 후 지역화 가능한 문자열을 자동으로 검색하는 문자열 카탈로그를 추가하는 것으로 시작됩니다. 문자열 카탈로그를 직접 현지화하거나 XLIFF 파일을 통해 전문 번역가와 협업할 수 있습니다.
- 5:33 - 번역 맥락
번역가에게 파일 제공 시, 정확하게 번역하기 위해서는 맥락이 중요합니다. 코드나 문자열 카탈로그에 직접 주석을 추가할 수 있습니다. Xcode 26에서는 온디바이스 모델을 사용하여 코드를 분석하고 주석을 제안하는 자동 주석 생성 기능이 도입됩니다.
- 9:33 - 복잡성 관리하기
프로젝트가 커짐에 따라 이를 번들이나 프레임워크로 구성할 수도 있습니다. 지역화 API를 사용하여 문자열 식별 시, 번들 및 테이블 매개변수를 지정할 수 있습니다. Xcode 26에서는 문자열에 대한 기호를 자동으로 생성하는 새로운 워크플로 또한 도입되어 문자열 키와 값을 분리할 수 있습니다. 두 가지 워크플로를 선택하고 쉽게 전환할 수 있습니다. 바로 코드에서 문자열을 추출하거나 생성된 기호를 사용할 수 있습니다.
- 20:49 - 다음 단계
앱을 현지화하려면 문자열 추출부터 시작하고 프로젝트가 복잡해지면 생성된 기호로 전환하여 문자열 관리를 개선할 수 있습니다.