Android siena cover

안드로이드 글로벌화와 현지화 할때 꼭 알아야할 것들

많은 개발자가 현지화(l10n)와 글로벌화(i18n)를 어려워하지만, 안드로이드는 이미 글로벌 시장 장악을 돕기 위한 훌륭한 도구들을 제공하고 있습니다. 본 발표에서 Siena Aguayo는 유연한 레이아웃 디자인이나 지역화에 특화된 리소스, Smartling과 같은 통역 서비스 등의 도구들을 사용하여 앱을 현지화하는 방법을 알려줍니다. 이런 현지화 요소들을 통해 어떻게 세계 사용자들을 타게팅하고 시장 규모를 현저히 늘릴 수 있는지 배워 보세요. 🌏👥


소개 (0:00)

안녕하세요. 저는 샌프란시스코에 있는 Indiegogo사의 소프트웨어 엔지니어입니다. “대담무쌍한” 이라는 단어를 이번 제목으로 선택한 이유는 실수가 아니라, 이 것이 우리 회사가 추구하는 가치 중 하나이기 때문입니다. 저는 여러분이 글로벌화를 실행할 때 보다 대담무쌍해질 수 있도록 역량을 강화해드리고 싶어요. 이번 발표에서, l10n과 i18n에 대해 말하고, 글로벌화에 관심 가져야 하는 이유와 어떻게 여러분의 안드로이드 앱을 글로벌화할 것인지, 그리고 여러분이 시도할 수 있는 여러 훌륭한 것들에 대해 살펴볼 겁니다.

저에 대해 말씀드리자면, 앞서 말한 대로 Indiegogo사에서 일하고 있으며, 저희는 크라우드펀딩 플랫폼을 제공합니다. Indiegogo사에는 샌프란시스코에 있는 Hackbright 아카데미라는 여성 프로그래밍 부트캠프를 통해 입사했어요. Hackbright 아카데미는 더 많은 여성이 소프트웨어 엔지니어가 될 수 있도록 돕는 멋진 기관입니다. 저는 Indiegogo사에서 안드로이드 앱을 개발하고 있고, 저희 앱은 영어, 스페인어, 프랑스어와 독일어를 지원합니다. 제 경험은 여기에서 기반을 뒀는데요. 이 중 왼쪽에서 오른쪽으로 쓰는 언어는 없고, 모든 언어가 라틴 알파벳 기반이고 있기 때문에, 만약 반대의 언어에 대한 인사이트를 찾고 계신다면, 제 이야기가 적합하지는 않을 것 같습니다.

l10n과 i18n란? (1:42)

기초적인 이야기로 내려가 보죠. l10n과 i18n에 대해 “아!” 하는 깨달음의 순간을 아직 얻지 못한 분을 위해 설명을 하겠습니다. 이 숫자들은 사실 “localization”(현지화)와 “internationalization”(글로벌화)라는 단어의 처음 알파벳과 마지막 알파벳 사이의 글자 수에요. 재밌지 않나요?

저는 여러분이 앱을 다른 언어로 출시하려할 때 생각하실만한 것으로 “현지화”를 정의하겠습니다. 실제적으로는 콘텐츠를 번역해야 하겠죠. 영어로 된 스트링들을 예를 들어 스페인어로 번역해야 한다는 뜻입니다. 하지만 현지화하기 위해서는 타겟한 언어에 기반을 두어 데이터를 다르게 포맷해야 하기도 합니다. 어떤 언어들은 날짜, 숫자나 화폐 등의 표현 방식이 영어와는 다릅니다.

현지화는 또한 회사의 브랜드를 목표 시장의 문화에 맞게 조정하는 것을 포함합니다. 이는 엔지니어링의 영역을 뛰어넘는 것이지요. 사업 영역에 보다 초점을 맞춘 일이므로, 여러분이 어떤 언어를 골라야 할지 선택하는 법에 대해서 안내 드리지는 않겠습니다.

그렇다면 “글로벌화”는 사실은 콘텐츠를 현지화하고 다른 숫자 형식과 문장들을 조정하도록 여러분의 앱을 준비하는 것입니다. 이는 대체할 리소스 파일을 정의하고, 숫자와 날짜 서식 formatter를 사용하고, 유연한 레이아웃을 디자인하는 것을 포함합니다. 이런 작업들이 아마 엔지니어의 업무와 유사하게 들리겠지요.

공감 (3:19)

왜 이런 것들에 관심을 가져야 할까요? 현지화와 글로벌화는 단순한 통역뿐만이 아닙니다. 이는 공감으로 해야 하는 일입니다. 제 생각에는 바로 이 것이 우리가 모두 엔지니어로서 더 이야기해봐야할 주제입니다. 우리는 모두 근본적으로 사람입니다. 제가 안드로이드를 정말 좋아하는 이유는 어디에나 있기 때문입니다. 안드로이드를 통해 전 세계에 걸친 모든 인종의 사람들에게 접근할 수 있다는 사실, 그 자체가 안드로이드 엔지니어로서 우리가 관심 가져야 할 일 중의 하나라고 저는 생각합니다.

이런 개발 뉴스를 더 만나보세요

만약 여러분이 “나는 로봇이고, 사람들을 신경 쓰지 않아” 하는 생각을 가지고 있어서 앞서 말한 것들이 여러분에게 와 닿지 않는다면, 기술적인 이점들이 있습니다. 콘텐츠를 글로벌화하여 앱을 준비하는 것은 실상 관심을 분리하는 것에 대한 일이 될 것입니다. 여러분을 보다 체계적이게 할 것이며, 가독성이 높은 코드를 작성하도록 돕겠지요. 만약 여러분이 “그래요, 나는 내 앱 전체에 걸쳐 하드코딩된 영문 스트링들을 읽는 걸 사랑하니까 무엇을 할지 잘 알고 있어요.”라고 생각한다면 안드로이드 스튜디오를 사용하는 것이 좋습니다. 안드로이드 스튜디오는 아래 ID를 강조하는데, 왜냐하면 여러분이 이런 하드코딩된 스트링을 놓치지 않도록 하고, 이를 사용하는 것도 원치 않기 때문입니다.

@Override int getTitleId() {
    return "Explore";
    return R.string.menu_explore;

YAGNI (4:25)

Indiegogo사에서 많이 언급되는 YAGNI라는 개념에 대해 말씀드리고 싶네요. YAGNI는 “Ya Ain’t Gonna Need It(그렇게까진 필요 없어)”의 약자로, 오버엔지니어링을 막기 위해 사용하는 개념입니다. 제 생각에 글로벌화에 따르는 저항들은 “뭐, 내가 정 필요하단 걸 알기 전에는 하고 싶지 않은 일감 덩어리일 뿐이야.”하고 느낄만한 부류인 것 같습니다. 바라건대 이 발표의 끝에 가서는 여러분이 글로벌화를 잘하기 위해서 필요한 것들이 아주 조금이라는 것을 믿게 되면 좋겠습니다. 그렇게 되면 글로벌화가 필요한 순간이 닥칠 때 정말 즐겁게 실행할 수 있겠지요.

안드로이드 앱을 글로벌화하는 방법 (4:57)

여기까지 함께 하셨다면, 이 제목이 바꿔 말하면 “어떻게 내 안드로이드 앱을 현지화된 콘텐츠로 준비할 수 있을까?”임을 아실 겁니다. 어떤 콘텐츠가 현지화 가능한지에 대한 것부터 시작해보겠습니다.

당연히 스트링들을 현지화하는 것뿐만 아니라 숫자와 화폐 단위, 날짜와 시간, 텍스트가 있는 이미지, 오디오와 비디오 파일 등을 현지화할 수 있습니다. 이미지들을 포함한 대체 리소스를 관리하는 것이 너무 힘들어서 가능한 한 텍스트가 있는 이미지는 피하고 싶을 겁니다. 만약 디자이너가 텍스트가 가득한 이미지를 가지고 온다면 “저기, 그 이미지는 스트링으로 해야 하고, 번역도 해야 해요.”라고 말해줘야겠지요.

이런 일들은 두 영역으로 나눌 수 있습니다. 리소스 파일과 관련해서 해야 하는 일과 Java로 해야 하는 일입니다. 이제 이런 데이터들이 스트링들에 발생할 때 어떻게 대처해야 하는지 알아보겠습니다.

Resource Files (6:04)

모두 기본 리소스 집합은 꼭 있어야 한다는 걸 알고 있고, 보통 값 스트링의 XML로 관리할 것입니다. 그렇다면 필요 때문에 앞으로 사용할 대체 언어들을 정의할 수 있습니다. 이 작업은 기본 리소스보다 양이 적을 수 있으므로 기본 리소스를 정확히 복사한 모든 대체 리소스를 반드시 만들어야 할 필요는 없습니다. 즉, 미국식 영어와 영국식 영어를 예를 들자면 영국식 영어 철자에 u가 추가로 들어갈 때처럼 작업이 필요할 때만 몇몇 철자를 바꾸기만 하면 될뿐, 모든 요소의 복사본을 일일이 만들 필요는 없습니다.

스트링들 (6:37)

사용자의 면전에 스트링을 하드코딩하지 마세요. 이 것이 아마 글로벌화의 기초 원칙이겠지만, 다시 한 번 강조하고 싶습니다. 대신 리소스 파일에 넣으세요.

다른 팁이라면 동적인 값을 스트링에 넣어야 할 때 %s 대신 %1$s처럼 실용적인 위치 placeholder를 사용하라는 것입니다. placeholder에 위치를 지정해서 넣을 수 있어요. 만약 문법적으로 다른 언어에서는 두 번째 위치에 들어갈 값이 첫 번째 위치 앞에 나와야 할 경우도 있지만, 문제가 되진 않을 겁니다, 자바는 이런 동적인 값들이 어디에 위치해야 하는지 알고 있으니까요. 마지막으로 번역자들을 위해 문맥을 제공한다면 그들의 업무에 많은 도움이 될 겁니다.

안드로이드는 우리 편 (7:25)

제가 개발을 처음 시작했을 때 왜 안드로이드를 좋아하게 되었는지 말씀드리고 싶어요. 저는 iOS 프로그램을 개발해왔고, 초반에는 현지화를 위한 일을 그다지 수행하지 않았어요. 다시 되돌아가 그 모든 일을 다시 해야 했던 작업은 정말 고통스러웠습니다. 반면 안드로이드 개발의 초반에서부터 저는 “저기, 이건 i18n 하드코딩 스트링에 대한 경고인데요. 이건 @string 리소스에 써야 해요.” 라는 텍스트뷰에 하드코딩된 스트링에 대한 경고를 맞닥뜨렸어요. 저는 이 경고가 정말 멋지다고 생각합니다. 왜냐면 안드로이드는 실제로 여러분을 올바른 방향으로 이끌어주고, 우리가 베스트 프렉티스가 아닌 일을 하지 않도록 경고하려고 노력하기 때문이에요. 하지만 자바 코드에서 단순히 “setText”를 부를 때는 이런 경고가 뜨지 않으니 잘 살펴보셔야 할 겁니다.

스트링에 대한 또 다른 점 (8:16)

우리가 스트링 파일에 더할 수 있는 또 다른 것은 translatable="false" 속성입니다.

<string name="app_name" translatable="false">
    Indiegogo
</string>

이 속성은 프로그램적으로도 사용될 수 있고 동시에 번역자에게 “이건 번역할 필요가 없어요.”라고 알려주는 용도로도 사용할 수 있어요. Indiegogo사에서의 사례를 보면, 우리는 절대 우리 회사명을 번역하지 않으므로 여기에 translatable="false"속성을 쓸 수 있죠. 이는 또한 번역하지 말아야 할 것을 번역했다든지, 번역해야할 것을 빼먹었다든지 같이 유용한 lint 에러를 부르기도 합니다. 또한 번역자를 위한 문맥적인 노트에 초점을 둘 수도 있어요. 어떤 컨텍스트에 대해 특정 ID들을 던질 수 있는 XLIFF이란 툴도 있는데, 번역자들이 작업하는 데 큰 도움이 됩니다. 회사의 사업 로직 내에서 매일 일하는 사람인 당신이라면 특정 날짜가 의미하는 바를 정확히 알 수 있지만, 매일 그 안에서 일하지 않는 사람은 “여기 웬 스트링값이 있는데 무슨 뜻이지?” 하고 생각할 수 있기 때문입니다.

<string name="campaign ended label"
        note="example: Ended on January 21, 2014">
    Ended on %s
</string>

이는 통합을 위해 유용합니다.

숫자와 화폐 (9:34)

재밌는 주제죠! 숫자를 위해 Numberformat을 한 번도 사용한 적 없는 분을 위해 이 작업이 정말 쉽고, 하나도 두려워할 필요가 없다고 말씀드리고 싶어요.

import java.text.NumberFormat;

NumberFormat numberFormat = NumberFormat.getInstance();
textView.setText(numberFormat.format(36965));

영어와 스페인어로 마무리 짓는다면 이런 모습입니다.

Contributions 36,965 & Contribuciones 36.965

영어에서는 이런 쉼표가 덧붙는 것을 볼 수 있고, 스페인어에서는 정정하는 구획 문자, 실제로는 마침표를 사용했어요. 결과가 잘 적용됐고, 한 번 더 강조하자면 구현하기 쉽답니다.

복수로 만드는 것도 꽤 쉬운 데다가, 만약 Rails로 일해본 적이 있다면 비슷하게 작동합니다. 다른 언어에서 한 두 가지 특별 사례가 있어서 필요한 경우에만 그 양을 정의하면 됩니다. 복수에 대한 문서를 보면 여러 다른 언어의 재미있는 예시를 많이 찾아볼 수 있어요. 아마 영어로만 사고하던 분들에겐 평소에 전혀 생각도 하지 못했던 부분일 겁니다.

strings.xml
<plurals name="number_of_fb_friends">
 <item quantity="one"%s friend</item>
 <item quantity="other"%s friends</item>
</plurals>
res.getQuantityString(
    R.plurals.number_of_fb_friends,
    count,
    numberFormat.format(count)
);

보통 숫자에 대해 NumberFormat을 사용하게 되고 NumberFormatter는 스트링을 반환하므로, 정수 대신 스트링 사용 시의 보간법에 대해 약간의 PSA를 드릴까 합니다. getQuantityString 메서드는 양 선택을 위해 정수를 두 번째 인자로 받고, 이는 실제로 변환되는 숫자입니다. NumberFormat이 스트링을 반환하므로, 스트링 placeholder를 사용해야 합니다. Stack Overflow에서 “count count”와 비슷한 예제를 많이 봤는데요, 이는 스트링 안에 d나 i를 썼다는 뜻이죠. NuberFomatting이 되고 있지 않다는 뜻이므로 충분히 주의하시기 바랍니다.

날짜와 시간 (11:27)

별도의 설치나 구성이 필요 없이 바로 사용할 수 있는 dateFomatter는 여러분이 바로 사용할 수 있는 날짜 형식들을 제공하고, 이는 short, medium, long의 세 가지 기본 형식만으로 이뤄집니다. NuberFormatter와 유사하게 작동하죠. 객체화하고 .format만 부르면 됩니다.

// DateFormat.getDateFormat (short)
// DateFormat.getMediumDateFormat
// DateFormat.getLongDateFormat

DateFormat dateFormatter = DateFormat.getDateFormat(context);
Date now = new Date();
dateFormatter.format.(now);
en_US                       ja_JP
short:  8/23/2015               short:  2015/08/23
medium: Aug 23, 2015                medium: 2015/08/23
long:   August 23, 2015             long:   2015年8月23日

여기 영어와 일본어로 된 날짜 형식이 있어요. 일본어의 short 형식과 medium 형식은 같다는 걸 보실 수 있습니다. 일본어에서 이 두 형식에는 의미상으로 다른 점이 없으므로, 어떤 경우에는 짧게 만드는 것에 별 의미가 없을 수도 있다는 점을 알아 두셨으면 합니다.

월/일/년을 모두 쓰는 것 대신 연도와 달만을 갖는 것처럼 커스텀 날짜 형식을 만들고 싶다면 어떨까요? API 18 이상에서 작업한다면 getBestDatetimePattern이라는 정말 훌륭한 메서드가 있어 locale 정보만 넘기면 되므로 아주 편합니다. 컴포넌트 스트링이므로 순서를 고민하지 않아도 됩니다. 이 날짜를 format하면, 알아서 올바른 데이터가 됩니다.

String formatString = DateFormat.getBestDateTimePattern(
  Locale.getDefault(), "MMMMyyyy"
);
SimpleDateFormat dateFormatter = new SimpleDateFormat
(formatString);
Date now = new Date();
String dateString = dateFormatter.format(now);
en_US: August 2015
ja_JP: 2015年8月

일본어에서는 연도가 앞에 나오는 것을 확인할 수 있는데, API가 자동으로 해준 것입니다.

하지만 여러분이 Indiegogo사 사람들 같은 분이고 API 18 이하를 타겟하고 있다면 좀 흥미로워집니다. ¯\_(ツ)_/¯

우리 회사에서는 날짜 형식이 필요함에 따라 이를 수집했고, 리소스에 보관했습니다. 완벽한 해결책은 아니죠. 만약 우리가 지원하지 않는 언어로 Indiegogo사의 안드로이드 앱을 사용하는 사람이 있다고 가정해 봅시다. 안드로이드는 마치 우리가 해당 지역에 있는 것처럼 날짜 형식을 format하려고 할테니 그 사람에겐 그 결과가 좀 이상해 보일거에요. 만약 아주 많은 날짜를 꼭 format 해야 한다면, Joda-Time 같은 라이브러리를 살펴보시는게 좋을 겁니다. 이미 당신을 위해 그런 작업들을 다 해놓았으니까요. 하지만 또 라이브러리를 추가해야 하는 부담이 있습니다.

SpannableString 회피책 (13:44)

여러 가지를 style 해야 해서 SpannableString을 사용하려 해봤다면, 정말 사용하기 힘든 녀석임을 알 겁니다. style을 달리하려는 것이 어느 인덱스에서 시작하고 끝나는지 꼭 알아야 하니까요. locale에 의존하는 앞으로 변화할 스트링을 다루면서 이를 안다는 게 얼마나 어려운 일인지 쉽게 상상할 수 있을 겁니다. 쉽고 간단한 해결책이 아니죠. 만약 가능하시다면 이 어려운 SpannableString을 모두 HTML로 바꾸기를 추천합니다. 분명 HTML이 모든 태그를 지원하지는 않지만, 그럭저럭 사용할 만 하고, HTML에서 텍스트를 set할 수도 있습니다.

아래처럼 사용하지 마세요.

public void setSpan (Object what, int start, int end, int flags)

대신, strings.xml의 HTML로 바꾸세요.

textView.setText(Html.fromHtml(
    res.getString(R.string.fixed_funding))
);

유연한 레이아웃 구성 (14:35)

멋진 컨퍼런스 발표 주제에 대한 아이디어를 찾는 사람이 있다면, 유연한 레이아웃 구성이 꼭 맞는다고 생각합니다. 사실 정말 광범위한 주제입니다.

wrap_content에게 정말 감사하고 싶어요, 제 말 맞죠? 웹 프로그래밍을 해봤다면 때론 콘텐츠에 맞게, 혹은 부모에게 맞게 크기를 확장하는 것이 정말 어렵다는 것을 아실 겁니다. wrap_content는 정말 멋진 속성이에요.

말한 대로, 라인 길이를 유념하세요. 때때로 다른 언어에서 스트링은 더 길어질 수 있는데, 여러분이 이를 고려하지 못할 수도 있습니다. 대부분의 경우 안드로이드 UI 프레임은 이런 부분에서 훌륭한 유연성을 가지고 있지만, 여러분이 기대했던 것보다 더 많이 혹은 적게 이동하게 될 수도 있습니다.

Indiegogo사를 예를 들자면, 펀딩 미터 아래의 퍼센트 카운터가 “41% 펀딩됨” 같은 수치를 나타내고 있는데요, 만약 다른 언어에서 이 퍼센트와 언어를 바꾼다면 어떤 일이 일어날까요?

“뭐, 그래. TextView가 두 개 있고 하나는 bold체고 하나는 아니지.” 라고 생각할 수도 있겠죠. 그런데 갑자기 이 두 가지 데이터를 바꿔야 하는 엄청난 문제에 맞닥뜨리게 되는 겁니다. 이러한 곤경에 처하는 대신, HTML로 된 하나의 스트링에 데이터를 넣는다면 순식간에 모든 문제가 사라지게 됩니다.

다른 훌륭한 것들 (15:43)

안드로이드 스튜디오에는 Translations Editor라는 훌륭한 기능이 있습니다. 스트링 파일을 열면 아마 작은 알림 바가 팝업되는 것을 볼 수 있을 겁니다. “Open ditor”를 클릭하면 번역 결과를 나란히 볼 수 있어요. 만약 통역에 관련된 많은 일을 빠르게 수행해야 한다면, 이 기능을 사용하는 게 꽤나 유용할 겁니다. 또한 “Untranslatable” 체크 박스를 활용해서 모든 파일에 대해 이 작업을 수행할 수도 있습니다.

구글 플레이 앱 번역 서비스로 링크되는 “Order a translation” 버튼도 있어요. Droidcon NYC에서 이 이야기를 했더니, 누군가 머신 번역을 많이 써서 퀄리티가 그다지 좋지 않지 않다고 지적한 바 있으니, 상황에 따라 쓰시면 좋겠어요. 다만 이런 링크를 안드로이드 스튜디오에 통합한 점, 그래서 앱을 어떻게 현지화할 것인지에 대한 생각을 시작하게 한 점은 멋지다고 생각합니다.

또 다른 멋진 기능은 구글 플레이 개발자 콘솔입니다. 이곳에서 여러분의 사용자가 어떤 언어를 사용하고 어떤 국가에 있는지 알려주고, 같은 카테고리 내 전 세계의 앱을 여러분의 앱과 비교하는 통계들을 볼 수 있어요. 이 통계들을 여러분의 PM에게 가져가서 “저기, 우리 이 언어로 지역화하는 게 좋을 것 같아요.”하고 말할 수 있겠죠. 여러분이 관심이 있다면 이 같은 사업 사례를 구축하는 데 도움이 될 겁니다.

마지막으로 저희 Indiegogo사에서 사용하는 Smartling에도 찬사를 보내고 싶어요. 유료 번역 서비스로, 실제 사람들이 번역해준답니다. API와 자바 SDK를 보유하고 있어요. 우리는 영어가 아닌 스트링들을 업데이트하기 위해 Smartling을 사용해서 그저 커맨드 라인에서 학습한대로 push하고 pull 하기만 하는데, 정말 정말 쉽답니다. Bash script로 push해서 올리고 pull해서 내려받기만 하면 돼요. 다만 한 가지 단점이라면 우리의 모든 영어가 아닌 스트링들을 완전히 덮어씌우기 때문에, 지역적으로 뭔가 수정하고 명령어를 실행하면 모든 변경 사항을 잃어버리게 돼요. 즉 Smartling 인터페이스를 직접 통해서 영어가 아닌 스트링들을 모두 편집해야 한다는 겁니다. 그래도 쉬운 업데이트 기능을 위해서는 감내할 만한 가치가 있다고 생각해요.

더 읽을거리(17:55)

본 주제에 대한 안드로이드 공식 문서가 상당히 좋고 이해가 쉬운 데다 몇몇 경우에는 꽤 흥미로우므로 한편 살펴보시길 추천해요. Smartling도 아주 멋집니다.

이제 나가서 대담 무쌍해질 때입니다!

컨텐츠에 대하여

이 컨텐츠는 저자의 허가 하에 이곳에서 공유합니다.

Siena Aguayo

Siena is a mobile software engineer at Indiegogo in San Francisco, where she has developed apps on iOS, Android, and Rails/Angular. A Los Angeles native and Pokémon master, Siena began her career as a software engineer by attending Hackbright Academy, a programming bootcamp for women in San Francisco. In addition to building Android apps, Siena enjoys playing video games, knitting, jamming on the guitar, and studying Japanese. You can find her on Twitter @sienatime.

4 design patterns for a RESTless mobile integration »

close