2015년 6월 27일 토요일

소프트웨어 번역을 효율적으로 하는 방법 (23)

국제화 개발팀에서 근사한 번역함수를 만들어 준다고 해도 번역이 잘 되도록 번역함수를 사용하는 것이 쉬운 것은 아니다. 특히 주니어 개발자들이 소프트웨어 번역의 원리를 이해하지 못하고 번역함수를 잘못 사용해서 번역가가 아무리 번역을 잘해도 이상하게 되는 경우가 있다. 

그래서 번역이 효율적으로 되도록 하고 번역함수가 제대로 동작하게 하려면 어떻게 해야 하는지 알아보자.


1. 용어 사전을 만든다.

번역에 있어서 용어사전(Dictionary)를 만드는 것은 매우 중요한 활동이다. 잘 정리된 용어사전은 번역이 일관되게 하며 여러 번역가가 협업을 할 때 같은 용어를 사용할 수 있도록 한다. 

영어에는 같은 뜻이지만 다른 단어가 엄청나게 많다. 따라서 용어사전에는 해당 뜻일 경우 어떤 단어를 사용하라는 지침이 포함된다. 이렇게 영어를 기준으로 용어사전을 만들면 번역가들은 각 로케일별로 각각 용어 사전을 만들어 달라고 요청을 하는 것이 좋다. 여기서 언어별이 아니고 로케일별인 이유는 스페인의 스페인어와 멕시코의 스페인어의 용어사전이 다를 수 있기 때문이다.
이렇게 용어 사전을 만들어 놓으면 나중에 번역회사를 교체해도 일관된 번역을 유지하기 좋다. 

2. 번역의 기준이 되는 영어 문장은 최대한 간결하고 번역이 쉬운 용어를 사용한다.

소프트웨어는 문학작품이 아니고 문법 시험이 아니다. 소프트웨어에서 사용하는 용어는 소프트웨어를 이해할 수 있는 정도로 최대한 간결하고 번역이 쉬워야 한다. 올바른 문법을 주장하다가 문어체 문장으로 도배를 하면 오히려 어색한 경우도 많다. 

용어 선택도 주의를 해야 한다. "Hard"라는 단어보다는 "Difficult"가 좋다. 예를 들어 실생활에서는 "Hard"를 더 많이 쓴다고 하는 경우라도 번역에 오해가 없는 "Difficult"를 쓰는 것이 더 좋다. 

3. 대소문자 번역을 효율적으로 한다.

Open, OPEN, open을 각각 번역하면 3번 번역을 해야 하고 비용도 3배 더 나간다. 한국어로는 모두 "열기"다. 이런 경우 "open"하나만 번역을 하고 대소문자 변환 함수를 이용해서 변환하여 사용하는 것이 효율적이다. 대소문자를 처리하는 로케일 카테고리 표준은 LC_CTYP이다. LC_CTYPE의 영향을 받는 대소문자 변환 함수를 사용하면 로케일에 따라서 적절히 대소문자를 변환해준다.

C언어에서는 strlwr, strupr, wcslwr, wcsupr, stricmp, wcsicmp 등이 있다. 내가 바퀴를 다시 만들 필요는 없고 있는 함수를 쓰면 된다.

4. Broken sentence를 피한다.

대표적인 Broken sentence는 한 문장을 통째로 번역하지 않고 단어들을 쪼개서 합치는 것이다. 이렇게 쪼개진 단어들을 번역하기도 어려울 뿐만 아니라 어쨌든 번역을 해도 언어별로 어순이 달라서 이상한 문장이 되게 된다. "Leg of dog"을 쪼개서 번역하면 "다리의 개"가 될 수도 있다.

문장은 온전한 문장을 통째로 번역해야 하며 문장을 분리하는 기준은 각 언어의 특성을 어느 정도 알아야 효과적으로 정할 수 있다.

반대로 쪼개야할 문장을 하나로 연결해서 번역을 할 경우 번역해야 할메시지가 엄청나게 늘어나는 경우가 있다. 번역가는 이렇게 반복되는 패턴을 발견해도 이상 여부를 알리지 않고 기계적으로 번역하는 경우가 많다. 번역가는 오히려 이런 현상을 환영한다. 번역은 쉽고 수입은늘기 때문이다. 번역할 문장을 적당히 자르는 것은 개발자의 몫이다.

5. 메시지 표준을 정한다. 

보통 여러 개발자가 함께 소프트웨어를 개발하기 때문에 개발자들이 작성한 영어 문장이 서로 상당히 다른 경우가 있다. 용어사전이 있다고 하더라도 일관되게 사용하기는 쉽지 않다. 몇몇 회사는 소프트웨어에서 사용하는 모든 메시지를 전문팀에서 정해주곤 하는데 100% 커버하기는 쉽지 않다. 개발자가 문장을 결정해야 하는 경우도 많다. 이럴 때 지켜야 할 규칙을 정해야 한다.

예를 들어 문장의 끝에는 마침표를 찍을지 말지, Cannot을 쓸지 can’t를 쓸지? 등과 같은 사소한 규칙들의 집합일 수도 있다. 이런 표준은 발견될 때마다 조금씩 보강해나가면 된다.

6. 번역 제외 메시지 표시 함수를 정한다. 

가끔은 번역을 하면 안되는 메시지들이 있을 수 있다. 그런 메시지는 번역을 하지 않아도 되니 번역함수를 사용하지 않고 그냥 놔두는 경우가 있다. 이렇게 해도 소프트웨어는 문제없이 동작한다. 하지만 이 소스코드를 본 어떤 개발자가 번역함수가 누락된 것으로 착각을 하고 번역함수로 메시지를 감싸는 경우가 발생한다.

예를 들어 "IT"라는 단어가 소프트웨어에 있고 절대 번역을 하면 안되는데 이를 본 개발자가 번역함수("IT")로 바꿔놨다고 하자. 그러면 또 이 단어를 수십개의 언어로 번역을 해야 한다. 비용은 비용대로 들고 이 단어가 번역이 돼서 문제가 발생하기도 한다.

번역을 하면 안되는 단어는 화면에는 출력이 되지만 번역을 하면 안되는 경우와 화면에는 출력이 안되고 소프트웨어 내부적으로만 사용을 하는 경우가 있다. 소프트웨어 내부적으로 사용하는 단어를 번역해버리면 소프트웨어가 동작하지 않기도 한다. 또한 로그 등 번역을 할 필요가 없는 경우도 있다.

이런 일을 방지하기 위해서는 번역 제외 표시 함수를 사용하는 것이 좋다. 번역제외("IT") 이렇게 해놓으면 어느 누구도 실로 번역을 하게 만들지는 않는다. 물론 번역제외 함수는 아무 일도 하지 않고 원래 메시지 그대로 넘겨줄 뿐이다.

번역 제외 함수는 번역 라이브러리나 프레임워크에 따라서 다르며 지원하지 않는 경우도 있다. 이런 경우는 번역제외함수를 직접 만들어서사용하면 된다.

7. 번역가에게 번역가이드를 전달한다.

지난번에 얘기를 했듯이 메시지 별로 번역가에게 어떻게 번역을 해야 하는지 번역 가이드를 전달해야 한다.

8. 메시지 파일은 UTF-8으로 인코딩한다.

국제화가 잘된 소프트웨어는 멀티바이트보다 유니코드를 지원하도록 개발하는 것이 여러모로 편리하고 자잘한 문제도 적다. 번역가에게 메시지 파일을 보내서 번역을 요청할 때 최초의 파일은 ASCII 인코딩일 가능성이 높다. 메시지키가 영어이기 때문이다. 이때 번역가들은 자국의 인코딩을 이용해서 번역을 해 오는 경우가 종종 있다. 이때 UTF-8으로 인코딩을 해달라고 요청하는 것이 좋다. 이때 지난번에 언급했듯이 Windows의 Notepad를 이용하면 BOM이 따라 붙어서 낭패를 보는 경우가 가끔 있다. BOM이 없는 UTF-8 파일이 필요한 번역 함수를 사용하고 있다면 Notepad는 사용하지 말라고 가이드를 해야 한다.

소프트웨어 국제화를 오래 하다 보면 이런 여러가지 노하우가 쌓이게 된다. 이런 노하우를 꾸준히 쌓아가면서 회사의 자산이 되도록 해야 한다.

소프트웨어 번역이 어색하게 되는 이유 (22)

한국어로 번역된 소프트웨어를 쓰다 보면 코웃음이 나오는 정도로 어색하게 번역이 되는 경우를 종종 본다. 물론 전문 번역가를 활용하지 않고 구글번역기를 이용해서 번역을 할 경우는 웃기는 경우가 많이 벌어진다. 하지만 전문 번역가가 번역을 하더라도 제대로 번역이 안되는 경우가 많다.



예를 들어 "Pan"이라는 말을 번역하려고 한다. "Pan"이라는 단어를 전세계 수십 나라의 번역가에게 보내면 어떻게 번역을 해올까? 많은 나라에서 ""이라고 번역을 할 것이다. 그리고 "프라이팬"으로 번역하는 번역가도 있을 것이다. 이렇듯 번역할 메시지만 보고 번역을 한다는 것은 거의 불가능에 가깝다. 그래서 번역 가이드가 필요한 것이다. 번역할 메시지만 번역가에게 전달하는 것이 아니라 어떻게 번역해야 하는지 방법도 전달해야 한다.




"Open"이라는 메시지를 번역해야 한다고 생각하자. 번역가에게 무엇을 알려줘야 번역을 제대로 할 수 있을까? 한국어로는 어떻게 번역이 될지 생각해보자. "열기"? "열어라"? 어떤 투로 번역을 해야 하는지 알려줘야 할 것이다. "명사입니다."라고 가이드를 주면 "열기"로 번역을 할 수 있을 것이다. 물론 번역 가이드는 전세계 수많은 나라의 번역가가 봐야 하므로 영어로 기록을 해야 한다. "It is a noun"과 같이 남기면 된다. 또한 "첫 글자는 대문자를 유지해주세요."와 같은 가이드도 남길 수 있다. 



그럼 번역 가이드를 남기는 방법과 어떤 가이드를 남겨야 하는지 알아보자.

번역 가이드는 별도의 파일이 따로 남기는 것이 아니라 소스코드의 메시지와 같이 기록해야 한다. 별도의 문서에 번역 가이드를 남기는 것은 관리가 너무 어렵기 때문이다. 프로그래머가 소스코드에 메시지를 기록할 때 번역 가이드를 동시에 적고 메시지를 추출할 때 자동으로 번역 가이드도 같이 추출되도록 해야 한다.

소스코드에 번역 가이드를 남기는 방법은 크게 3가지가 있다. 물론 번역 라이브러리에 따라서 지원하는 방법이 다르고 번역 가이드를 지원하지 않는 것도 있다. 번역 가이드를 지원하는 번역 라이브러리를 사용하는 것이 좋다.

첫째, 메시지 앞에 주석으로 남기는 방법이다. TRGUIDE라는 키워드는 번역 라이브러리마다 다를 수 있으며 사용자가 표준을 따로 정해서 지정해줄 수도 있다.
/* TRGUIDE: It is a noun. */
번역함수("Open");

둘째, 메시지 뒤에 주석으로 남기는 방법이다.
번역함수("Open");  // TRGUIDE: It is a noun.

셋째, 번역함수의 인자로 가이드를 추가하는 방법이 있다.
번역함수("Open", "It is a noun.");

첫 번째와 두 번째 방법은 메시지를 연속으로 번역해야 할 때 약간 까다롭다. 세 번째 방법이라면 아래와 같이 번역함수를 사용할 것이다.

StringFormat함수(번역함수("%1 of %2", "%1, %2 will be replaced any word. Please consider the order in your language"), 번역함수("Leg", "It is a leg"), 번역함수("dog", "It is a big dog"));

하지만 첫 번째 방법이라면 아래와 같이 사용해야 한다. 
/* TRGUIDE: %1, %2 will be replaced any word. Please consider the order in your language */
StringFormat함수(번역함수("%1 of %2"), 
/* TRGUIDE: It is a leg */
번역함수("Leg"), 
/* TRGUIDE: It is a big dog */
번역함수("dog"));

두 번째 방법이라면 다음과 같이 사용한다.
StringFormat함수(번역함수("%1 of %2"), // TRGUIDE: %1, %2 will be replaced any word. Please consider the order in your language
번역함수("Leg"), // TRGUIDE: It is a leg
번역함수("dog")); // TRGUIDE: It is a big dog



그럼 번역 가이드에는 어떠한 내용들이 추가되어야 할까?

1. 대문자 또는 소문자를 유지해주세요. 첫글자만 대문자를 유지해주세요. 이 경우 대소문자가 없는 언어라면 무시를 할 것이고, 대소문자가 있는 언어라면 가이드대로 번역이 될 것이다.

2. 메뉴 또는 버튼에 쓰이는 단어이니 명사로 번역해주세요. 명령어로 번역을 해주세요.

3. 이 단어는 번역하지 말아주세요. 고유명사입니다.
예를 들어 골프 선수 이름인 "Tiger Woods"를 번역해야 한다고 하자. 아무런 가이드가 없다면 "호랑이 나무"로 번역이 될 수도 있다. "사람 이름입니다."라는 가이드를 주면 "타이거 우즈" 정도로 번역을 할 것이다. 이렇듯 회사 이름, 지역 명 등 고유 명사는 적절한 가이드가 필요하다.
한화(당시 한국화약)의 고위임원이 1990년에 중국 정부를 방문했는"환영 남조선 폭파집단"이라는 환영 플랜카드가 걸린 것을 보고 회사이름을 "한화"로 바꾼 계기가 되었다는 일화도 있다.



4. %1, %2는 다른 단어로 교체가 될 수 있으니 순서를 고려해주세요.

5. 이 단어는 반도체 관련 단어입니다. 번역을 하지 말거나 용도에 맞게 번역해주세요.
같은 단어라고 하더라도 분야별로 의미가 다른 경우가 많다. 따라서 어떤 분야의 단어인지 또는 그 뜻을 정확하게 설명해주는 것이 좋다. 개발자는 단어를 적을 때 번역가가 헷갈려 할 수 있다는 것을 생각해야 한다. 물론 그런 판단을 하는 것이 쉬운 것은 아니다.

6. 약어인 경우 원래 의미를 적어주는 것이 좋다. 그렇지 않으면 약어는 정말 엉뚱하게 번역이 될 수도 있다. 전세계 표준 약어라고 하더라도 영어로 된 약어를 받아들이지 않는 나라도 많다. 따라서 약어의 줄이기 전 원래 단어를 알려줘야 제대로 번역이 될 수 있다.



7. 문화의 차이에 따라서 잘 알지 못할 수 있는 단어는 의미를 자세히설명해주거나 해당 단어를 잘 설명하고 있는 웹사이트 주소를 같이 적어주는 것도 좋다. 이때 Wikipedia 주소를 추가하기도 한다.

대부분의 번역가는 번역 가이드가 없어도 최대한 번역을 한다. 하지만이러한 번역가의 자세가 번역을 엉터리로 되게 하기도 한다. 대부분의번역가는 번역회사와 계약 관계에 있는 계약직 번역가이며 외국어를 전공한 학생이기도 하고 외국에서 살다온 사람일수도 있고 전문 번역가인 경우에도 있다. 번역가는 번역 단어수로 수입이 결정되기 때문에번역이 애매할 경우 적극적으로 해결하려는 경우는 그리 많지 않다. 그래서 소프트웨어 개발사에서 적극적으로 번역의 품질을 높이기 위한 정보를 제공해야 한다.

우리가 번역 라이브러리 또는 프레임워크를 사용할 경우 번역 가이드를 지원하는 것을 선택해야 한다. 또한 전 과정은 자동화가 되도록 해야 한다. 프로그래머가 영어 실력이 부족하여 번역 가이드와 영어 메시지를 문법에 맞고 자연스럽게 적는 것이 어려운 경우라면 개발 과정에서 이를 감수해서 수정해주는 프로세스를 적용해서 개발이 끝나고 번역을 위해서 메시지를 추출할 때는 소스코드에 제대된 영어 문장이 존재하게 하면 된다.

필자가 설명하는 하나하나의 과정은 소프트웨어 국제화 품질을 올려주는 과정이고 이런 것들이 모여서 소프트웨어가 해외에서 더욱 인정받는 길이 될 것이다. 아직 가야 할 길이 많이 남아 있다.

언어마다 다른 순서를 소프트웨어에 적용하는 방법 (21)

소프트웨어 번역 시 흔히 경험하는 문제 중 하나는 언어별 어순이 다르다는 것이다.

예를 들어 "Leg of dog"라는 문장을 번역해야 한다고 하자. 관사가 빠져서 어색하지만 무시하자. 대소문자단수/복수 문제도 있지만 무시하자. 한국어로 변역 하면 "개의 다리"가 된다. 여기서 "Leg"는 "Eye", "Ear"로 대체가 가능하고 "dog"는 "cat", "cow"등 여러 동물로 바뀔 수 있다고 하자.




먼저 소프트웨어의 소스코드에서 아래와 같이 사용하면 번역 시 어순에서 문제가 발생한다.

번역함수("Leg") + 번역함수(" of ") + 번역함수("dog")

이런 경우 " of "는 각 언어의 번역가들이 어떻게 번역을 할 수 있을까? 번역이 불가능하다. 이런 식으로 문장으로 작은 단위로 쪼개서 조합한 문장을 쪼개진 문장(Broken sentence)이라고 부르고 국제화된 소프트웨어를 개발할 때는 중요한 금기사항이다. 쪼개진 문장(Broken sentence)의 처리 방법에 대해서는 추후 좀더 자세히 알아보자.

" of "를 "의 "로 번역한다고 해도 "다리의 개"가 된다. 또한 "눈의 소", "귀의 고양이"가 된다.



문장은 온전하게 한 문장으로 이루어져 있어야 제대로 번역이 가능하다. 그래서 보통 마침표를 기준으로 문장을 나누고 ":"나 ";’로 문장을 나누기도 한다. ","로 문장을 나누는 것은 보통 위험하다.

따라서 위 "Leg of dog"은 소프트웨어에서 아래와 같이 코딩을 해야 한다.

StringFormat함수(번역함수("%1 of %2"), 번역함수("Leg"), 번역함수("dog"))

그래서 "%1 of %2", "Leg", "dog" 각각 따로 변역을 해서 조합하게 된다.
이 문장을 한국어로 번역하면 "%2의 %1", "다리", "개"가 된다.
조합하면 "개의 다리"가 된다. 또한 "소의 눈", "고양이의 귀" 이렇게 번역이 될 수 있다.

StringFormat함수는 개발 언어별로 프레임워크나 라이브러리 별로 다르다. 대표적으로 C언어의 sprintf 함수를 사용하면 다음과 같이 쓰면 된다.

sprintf(번역함수("%1$s of %2$s"), 번역함수("Leg"), 번역함수("dog"))



sprintf에서 인자를 받는 기호는 %d, %s 등이 있는데 여기에 $ 표시를 추가하면 순서를 지정할 수 있게 된다. 대부분의 개발툴이나 라이브러리 별로 각각 파라미터의 순서를 조절할 수 있는 포맷 지원 문자열 처리 함수들이 있으니 적절하게 사용하면 된다. 만약에 현재 개발 환경에서 그런 함수를 지원하는 않는다면 직접 만들거나 외부 라이브러리를 사용해야 한다. 오픈소스 중에도 이런 함수가 있으니 필요 시 사용할 수 있다.

"Error code : 1234"를 번역하기 위해서 아래 두 가지 방법이 있다.
첫 번째, StringFormat함수(번역함수("Error code : %1"), 1234)
두 번째, 번역함수("Error code") + " : " + 1234

둘 다 큰 무리는 없지만 첫 번째 방법이 좀더 안전하다. 우리는 "에러코드 : 1234"와 같은 순서를 자연스럽게 생각하지만 어떤 다른 언어에서 순서가 뒤바뀌는 것이 더 나은지는 전혀 알 수 없다. 또한 두 단어를 구분하기 위해서 ":"를 쓸지 다른 기호를 쓸지는 모르는 것이다.설령 우리가 지원하는 모든 언어에서 이런 변수가 없다고 하더라도 일단 안전하게 첫 번째 방법을 선택하는 것이 더 좋다.

100%, 120m 등을 번역하는 것도 동일하게 StringFormat함수(번역함수("%dm"), 120)과 같이 쓰는 것이 조금 더 안전하다. 전세계 어떤 언어에서 %와 m도 번역을 해야 하는지는 알기 어렵다. 한두개의 언어를 안다고 해도 모든 언어를 알 수는 없다. 제대로 번역하는 것은 번역가의 몫이고 개발자는 제대로 번역이 가능하도록 코딩을 해야 한다. 물론 영어, 한국어, 독일어 정도 지원하는 경우라면 어떻게 해도 아무 문제가 없다. 소프트웨어 판매 전략에 따라서 미래에 문제가 될 수도 있고 안될 수도 있다. 당장 문제가 안된다고 이런 것까지 언급하느냐고 불평을 하지 말고 원리는 알고 넘어가고 판단은 스스로 하면 된다.


국제화가 잘 된 소프트웨어를 개발하려면 문장의 모든 단어가 과연 우리의 상식으로 이해되는 순서로 배열이 될지 의심을 해봐야 한다. 만약에 의심이 된다면 안전한 방법을 선택하는 것이 좋다. 미래에 내가 만든 소프트웨어가 지금보다 10배, 100배 커지고 전세계 수백개의 나라에서 판매를 할지 그렇지 않을지는 모르는 것이다. 당장은 아니라도 10년 후에 그런 기회가 나에게 찾아 올 수 있다. 그 때를 위해서 필자가 설명하는 원칙과 원리를 이해해보자. 물론 당장도 이익이기는 하지만 이익이 눈에 띄게 보이지는 않을 수도 있다.

왜 소프트웨어 번역의 기준은 영어가 되어야 하는가? (20)

이전 글에서 소프트웨어 번역 프로세스의 제 1원칙은 메시지 키는 영어 자체여야 한다고 했다. 즉, 번역의 기준은 영어여야 한다는 것이다. 
 


 현재 시중에 나와 있는 수많은 번역 함수들은 이 제 1원칙에 어긋나있다. 개발자들도 제 1원칙에 어긋난 수많은 방법을 이용해서 소프트웨어 번역을 하면서 수많은 문제가 봉착하고 있다. 

대규모 프로젝트들이 
MS개발툴, Java 등 널리 쓰이는 개발툴에서 제공하는 번역 함수들을 그대로 이용해서 번역을 하고 있다고 착각하고 있는 개발자들이 매우 많다. RC파일을 이용해서 메시지가 10,000개,지원하는 언어(로케일)가 30개인 프로젝트를 수행해 낼 수 있다고 착각한다. 할 수도 있겠지만 너무 비효율적이고 거의 불가능하다. 대부분의 대규모 프로젝트에서는 오픈소스 또는 내부에서 개발한 번역 함수와 자동화된 번역 프로세스를 통해서 해결하고 있다. 이를 위해서 매우 전문적인 소프트웨어 국제화 담당자들이 활약하고 있다.

우리나라에서도 대규모 프로젝트에서는 스스로 개발한 번역 함수와 번역 프로세스를 사용하곤 하지만 원칙에 어긋나거나 완전 자동화에 실패를 해서 빠져 나오기 어려운 문제에 봉착하곤 한다. 

그럼 번역의 기준이 되는 메시지의 키는 어떤 것들이 있으며 영어가 아니면 왜 문제가 되는 것일까? 번역 함수에 “메시지 키”를 넘겨주면 “번역된 메시지”가 넘어 온다.
 
번역된 메시지 = 번역함수(메시지 키)
"번역함수"에는 여러가지고 있고 물론 자동으로 번역을 해주는 함수가 아니고 번역가가 번역한 메시지를 소프트웨어서 보관하고 있다가 사용자가 사용하는 언어로 변경 해주는 함수를 말하는 것이다.

개발자들이 번역을 위해서 사용하는 메시지의 키는 대략 4가지 정도가 있다.

1. 숫자
2. 심볼
3. 한국어
4. 영어
 

첫 번째 숫자부터 알아보자. 번역의 기준을 숫자로 사용하는 방법은 매우 오래된 방법이다.
번역함수(1) => 사과
번역함수(2) => 딸기
이렇게 사용하는 방법이다. 이 방법의 문제점은 
숫자를 잘 관리해야 한다는 점이다. 번역이 필요한 메시지를 새로 추가할 때 기존의 숫자들과 중복되지 않는 숫자를 찾아야 하고 삭제할 경우에는 해당 메시지가 더 이상 정말로 사용되지 않는지 면밀히 검토를 해야 한다. 이런 번거로운 절차를 자동화하는 툴들도 있지만 잘 동작하지 않는 경우가 많았다. 또한 숫자를 보고 바로 번역할 수 없으므로 영어 메시지를 전달해야 하고 영어 번역이 바뀌면 다른 언어도 번역을 변경해야 하는데 이를 관리하는 것이 너무 어렵다. 숫자를 기준으로 번역하는 것은 함수는 간단해 보이지만 관리가 너무 어려워서 이제는 거의 쓰이지 않는다.

두 번째 
심볼을 쓰는 방법은 가장 널리 사용되고 있다.
번역함수(MSG_CLOSE) => 닫기
번역함수(BUTTON_OPEN) => 열기
이렇게 사용하는 방법이다. 이 방법은 숫자에 비하여 심볼을 보면 대충 무슨 의미인지 알 수 있어서 개발자들이 메시지 파일을 뒤지지 않고도 심볼을 기억해서 사용할 수도 있고 엉뚱한 메시지를 사용할 위험성이 줄어든다. 하지만 이 방법에도 치명적인 몇 가지 문제가 있다. 먼저 매번 새로운 메시지가 추가될 때마다 
심볼을 정해야 한다. 이때 다른 동료와 동시에 같은 심볼을 정해서 충돌이 날 수도 있고, 기존에 사용된 심볼을 없는 줄 알고 다시 쓰는 문제도 발생할 수 있다. 이 또한 삭제할 메시지를 정하는 것이 매우 어렵다. 실수로 삭제하는 위험성 때문에 삭제는 영원히 안하는 회사도 있다. 그러면 새로 지원하는 언어가 늘어 날 때마다 사용도 안하는 메시지를 번역하는 일도 생긴다.
 



번역가에게는 영문 메시지파일을 전달해서 여러 언어로 번역을 요청하지만 나중에 영어가 바뀌면 바뀐 메시지만 다시 각 언어별로 번역을 요청하는 것이 너무 어렵다. 바뀐 영어 메시지를 찾고 관리하는 것도 어렵지만 번역가에게 몇 개의 메시지만 번역을 변경해달라고 요청하기도 힘들다.
어떤 소프트웨어가 v1.0에서 3,000개의 메시지를 번역했다고 하자. 그런데 v1.1에서 500개의 메시지가 삭제되고 500개는 영어 메시지가 수정되었고, 1,000개의 메시지가 추가되어서 최종 3,500개의 메시지가 되었다고 하자. 그럼 번역가에게는 1,000개는 새로 번역을 요청하고 500개는 번역 수정을 요청해야 한다.
어떤 메시지가 
수정할 메시지이고, 삭제될 메시지, 추가된 메시지를 버전 별로 관리하고, 번역가에게 보내고, 번역된 메시지를 메시지 파일과 통합하는 일련의 프로세스가 얼마나 복잡한지 대규모 프로젝트의 번역 프로세스를 담당해본 개발자라면 잘 알 것이다
이런 과정에서 문제가 없이 번역 프로세스를 처리하는 것은 거의 불가능하며 수많은 버그를 포함하게 된다. 번역이 누락된 경우 “MSG_CLOSE”와 같은 심볼이 출력되기도 한다.
메시지 키에 심볼을 사용하는 이상 이런 복잡한 프로세스를 피해가기 어렵다.

세 번째는 
한국어를 메시지 키로 사용하는 방법이다.
번역함수(닫기) => Close
번역함수(열기) => Open
이 방법은 먼저 영어로 번역을 한 후에 다른 언어로 번역을 해야 하기 때문에 번역 프로세스가 더 오래 걸린다. 그렇게 널리 쓰이는 방법은 아니다.

이 방법이 좋다고 생각하는 경우 한국어에서 영어, 한국어에서 일본어와 같이 우리와 친숙한 언어들을 위주로 지원하고 한국어를 잘아는 번역가를 활용을 하기 때문이다. 이렇게 한국어를 기준으로 변역해도 별 문제가 없는 경우도 있지만 대부분의 번역가는 영어와 해당언어의 전문가들이다. 한국어는 그 중에 하나의 언어인 경우가 많다.

이 방법은 지구가 우주의 중심이라고 생각하는 것과 같다. 그렇다고 영어가 우주의 중심이라는 사대주의적인 얘기는 아니다. 전세계 번역가를 충분히 활용하려면 그 중심은 영어라는 얘기다.

마지막으로 메시지 키로 
영어를 쓰는 방법이다.
번역함수(Close) => 닫기
번역함수(Open) => 열기
이와 같이 개발자는 소스코드에 번역할 영어 메시지를 그대로 사용하는 것이다. 이 방법의 장점은 
개발자가 소스코드에 영어 메시지를 적는 것 외에 아무 것도 할 것이 없다는 것이다. 심볼 이름을 정하려고 고민할 필요가 없고 심볼 이름이 중복될 까봐 고민할 필요도 없다. 다른 개발자가 동시에 Open이라는 메시지를 번역하려고 소스코드에 추가를 해도 충돌이 나지는 않는다. 삭제된 메시지를 개발자가 수동으로 찾아서 삭제를 할 필요도 없다. 이를 자동으로 찾아주는 툴이 있기 때문이다. 그리고 번역이 누락되면 화면에 영어로 출력된다. 숫자나 심볼로 출력된 것보다는 소프트웨어를 사용 할만 하게 된다. 이 방법의 가장 큰 특징은 번역을 제외한 전 과정이 완전 자동화가 가능하다는 것이다.
이 방법의 유일한 단점이 우리나라 개발자들이 영어 메시지를 제대로 만들어 내지 못한다는 것이다. 개발자가 영어를 너무 못하고 영어에 거부감이 커서 한국어를 키로 사용한다면 장기적으로는 여러 문제에 봉착한다. 그보다는 
이를 해결하기 위해서 별도의 프로세스가 추가해서라도 영어를 키로 쓰는 것이 장기적으로는 낫다.
이 글에 의심을 품고 이의를 제기할 개발자가 많다는 것을 잘 알고 있다. 흔히 RC 파일 등에서 자주 쓰이는 심볼 방식이 무엇이 문제인지, 지금까지 문제 없이 잘 쓰고 있다고 주장하는 사람도 많다. 전에도 얘기를 했지만 아주 작은 소프트웨어, 적은 지원 언어를 처리하는 상황이라면 무슨 방법을 써도 문제가 안 된다. 또한 기존 방법의 수동 프로세스를 당연하게 생각하고 있다면 어떠한 조언도 귀에 들어오지 않을 수 있다. 필자는 20년 넘게 소프트웨어를 개발하면서 대부분의 독자들이 경험한 국제화 방법을 거의 경험해 봤고 문제를 다 겪어봤다. 
 
필자는 분명히 말할 수 있다. 나중에 대규모 프로젝트에서 소프트웨어 국제화를 적용할 때 이 원칙을 무시하면 소프트웨어 국제화 때문에 프로젝트에 실패하는 일이 발생할 수 있다. 지금 마음을 열고 소프트웨어 국제화의 원칙을 이해하려고 노력해야 한다. 대규모 프로젝트를 수행하게 될 기회는 개발자에게 언제든지 올 수 있다. 그때를 위해서 몸에 익혀놔야 한다.
 
작은 프로젝트라고 하더라도 원칙을 지키고 번역 프로세스를 완전 자동화하는 것이 훨씬 효율적이다. 그런 과정을 통해서 제 1원칙의 원리를 몸에 익히는 것이 필요하다.

소프트웨어 번역 프로세스의 절대 원칙 (19)

소프트웨어를 국제화하려면 수많은 요소를 국제화해야 하지만 그 중에서 절대 빠지지 않는 부분이 번역이다. 또한 가장 중요하면서 어렵다. 필자가 얘기하는 주제는 번역 그 자체를 제외한 번역을 위한 모든 활동을 말한다. 소스코드에서는 어떠한 함수를 사용하고, 메시지는 어떻게 추출해서 어디에 저장하고 번역가에게는 어떻게 보내고, 번역된 메시지는 소프트웨어에서 어떻게 읽어 들여서 출력할 것인지에 대한 아키텍처와 프로세스 전반을 말하는 것이다. 


소프트웨어를 어느 정도 개발해 본 경험이 있는 개발자라면 소프트웨어를 번역해서 해외에 출시해 본 경험을 누구나 가지고 있다. 또한 별 문제를 겪지 않아서 소프트웨어 번역이 별거 아니라고 생각하는 개발자도 많다.

그럼에도 불구하고 필자는 왜 자꾸 어렵다고 얘기를 하는 것일까? 과장을 하는 것은 아닐까? 아니다. 소프트웨어 번역에서 별 문제를 겪어보지 못한 개발자들은 아직 심각한 상황을 경험해보지 못해서 어려움을 모르는 것일 가능성이 높다다. 아래 5가지 상황 중에서 2가지 정도만 닥쳐도 소프트웨어 번역은 심각한 문제가 된다.




10개 이상의 언어(로케일)을 지원
100명 이상의 개발자가 공동 개발
10000개 이상의 메시지를 번역
100,000명 이상의 고객이 사용한다.
적어도 한 달에 2번 이상 업데이트

한두 명의 개발자가 몇 백 개의 메시지를 번역해야 하는 상황이라면 어떤 메시징 아키텍처를 사용하던지 완전 수동화된 프로세스를 사용해도 거의 문제가 안 된다. 그래서 거의 모든 개발자들이 큰 고민 없이 여러 개발툴들이 제공하는 메시징 함수를 그냥 사용하고 수동에 의존한 프로세스에 익숙해져 있다.

그렇게 성장을 하다가 큰 제품을 만들 기회가 생기고 큰 조직에서 수십 명의 개발자와 같이 개발을 하게 되면 번역은 10배, 100배 비용이 들게 된다. 




예를 들어 전세계 백만 명이 사용하는 제품을 20개 언어(로케일)로 번역을 해야 하는 거대 프로젝트에 참여를 했다고 생각하자. 소프트웨어에 추가할 메시지들을 RC에 추가한다던지 별도의 파일에 추가하는 것은 쉽지가 없다. 메시지도 수만 개에 이를 뿐만 아니라 수십 명의 개발자들이 동시에 RC파일을 고쳐댄다. 똑 같은 메시지가 서로 다른 심볼로 중복 저장되기도 하고 반대로 의미가 다른 동일한 메시지를 사용하기도 한다. 더 이상 사용하지 않는 메시지를 삭제 했더니 다른 개발자는 사용하고 있는 경우도 있다. 그래서 메시지를 삭제 않다 보니 사용하지 않는 메시지가 넘치게 된다.

번역가에게 번역을 의뢰할 때도 처음에는 문제가 안되는데 두 번째 버전부터는 바뀐 메시지만 의뢰를 해야 한다. 번역된 결과물을 첫 번째 번역과 합치는 것도 문제다. 이렇게 복잡한 수동 프로세스에 의존하다 보니 누락된 메시지가 생긴다. 이를 개선하고자 자동화툴을 만들어보지만 깔끔하게 해결하는 것이 쉽지는 않다.

이런 거대 프로젝트에 참여를 해서 소프트웨어 국제화의 어려움을 실제로 겪어본 개발자는 그렇게 많지 않다. 하지만 모든 개발자는 언제든지 이런 상황을 겪게 될 것이고 지금의 국제화 지식만 가지고는 남들이 겪은 국제화 실패를 고스란히 다시 겪게 될 것이다.

그럼 소프트웨어 번역은 어떻게 해야 할까? 앞으로 여러 차례에 걸쳐서 설명을 하게 될 것이고 오늘은 대원칙 2가지를 알아보자.

첫째, 메시지의 키는 영어 자체를 사용해야 한다.
둘째, 번역 자체를 제외한 모든 프로세스는 완전 자동화 되어야 한다.

위 두 원칙에는 많은 의미를 포함하고 있고 적용하기 쉽지도 않다. 또한 기존에 수많은 개발자들이 소프트웨어 번역을 위해서 사용하고 있는 방법의 대부분의 뒤 두가지 원칙에 위배되고 있다.

다음 시간에는 왜 메시지의 키가 영어가 되어야 하는지 설명을 시작으로 소프트웨어 번역에 대해서 좀더 깊이 들어가보자.

국제화된 소프트웨어에서 날짜와 시간을 다루는 방법 (18)

개발자들이 소프트웨어를 개발하면서 가끔 하는 실수 중 하나가 현지 시간을 저장했다가 나중에 소프트웨어가 확장되면서 꼬이는 것이다. 보통의 소프트웨어라면 시간에 그렇게 민감하지 않다. 하지만 일정관리, 항공권 예약, 배송 시스템 등 시간에 민감한 소프트웨어들이 있다. 이 외에도 시간을 신경 써서 다뤄야 하는 소프트웨어가 많다. 이런 소프트웨어에서 시간의 기록과 처리를 현지 시간을 기준으로 처리를 하다가는 문제가 발생하고 꼬이게 된다. 



여기서 현지 시간이란 날짜와 시각을 모두 의미한다. 또 현지 시각은 그레고리력을 기준으로 하는 날짜 일 수도 있고 해당 나라에서 사용하는 독특한 달력을 기준으로 한 날짜일 수도 있다. 개인 혼자서 쓰는 시스템, 예를 들어 일기장과 같은 소프트웨어라면 문제가 안되지만 전세계 여러 사용자가 공유하는 시스템이라면 시간의 표시가 지역마다 다르게 표시되어야 한다. 또한 혼자 사용하더라도 일정 관리 소프트웨어 같은 경우에는 지역을 옮겨 다닐 때마다 현지 시간에 맞게 보정해서 보여줘야 한다.

그래서 시스템에 시간을 저장할 때는 절대 시간을 기록해야 한다. 소프트웨어에서 절대 시간이란 유닉스 시간을 말한다. POSIX 시간이라고도 부른다. 유닉스 시간은 영국 그리니치 천문대에서 그레고리력으로1970년 1월 1일 0시 0분 0초에 0으로 시작된 시간 표시로 1초에 1씩 증가한다. 예를 들어 한국에서 2015년6월5일 오전2시16분37초의 유닉스 시간은 1433524597이다. 거의 모든 OS와 Database에서는 유닉스 시간을 기준으로 시간을 처리한다. 그리고 화면에 출력할 때 타임존을 고려하여 변환하여 출력한다. 

따라서 시간을 기록할 때는 유닉스 시간을 얻어와서 DB나 파일에 저장을 하고 입출력 시 사용자의 입맛에 맞게 변환을 해야 한다. 출력 시에는 예를 들어 아래와 같은 변환 과정을 거쳐야 하고, 입력 시는 반대 과정을 거친다.


(유닉스 시간 10억초 달성 기념)

1. 유닉스 시간 : 1433524597
2. 협정 세계시 : 2015-06-05 17:16:37 UTC
3. 달력 변환 : 2015-06-05 17:16:37 UTC
4. 한국의 타임존 : 2015-06-06 02:16:37+09:00(or PDT)
5. 한국의 시간포맷 : 2015년 6월 6일,오전 2시 16분 37초

1~4번까지는 소프트웨어 내부에서 일어나는 변환 과정이고 5번이 사용자에게 보여지게 된다. 또한 형식은 언어(로케일)별로 다르다.

달력 변환에서 그레고리력일 경우에는 변환할 필요가 없지만 사용자가 그레고리력이 아닌 다른 달력을 사용하는 경우에는 변환이 필요하다. 예를 들어 일본 일왕의 연호나 불기, 음력을 사용한다면 변환이 필요하다.

개발 환경 즉, 개발 프레임워크나 라이브러리에서 제공하는 시간 함수들은 여러가지가 있고 위 변환 과정을 거의 모두 지원하는 것도 있고 일부만 지원해서 개발자가 추가로 개발을 해야 하는 경우도 있다. 대부분은 유닉스 시간과 타임존 변환은 제공하지만 달력 변환을 지원하는 경우는 거의 없고 로케일별 시간 포맷 제공도 프레임워크마다 제각각이다. 개발자가 상당한 세심한 신경을 써야 하는 이유다.


(타임존 지도)

그 외에 일정관리나 항공권예약 시스템에서는 시간과 관련해서 좀더 많은 정보를 보관해야 한다. 매 시간마다 위치나 타임존을 기록해서 하나의 시간에 대해서도 현지 시간과 현재 사용자가 있는 위치의 시간을 동시에 보여주기도 한다. 국제화가 잘된 소프트웨어에서는 시간 처리에 여러 가지 신경을 써야 한다.

그럼 국가별, 로케일별로 시간 표시 형식은 어떨까? 우선 로케일별 국가 이름을 보자

ar_SA : 사우디아라비아
de_DE : 독일
en_US : 미국
es_ES : 스페인
fr_FR : 프랑스
id_ID : 인도네시아
it_IT : 이탈리아
ja_JP : 일본
ko_KR : 대한민국
pl_PL : 폴란드
pt_PT : 포르투갈
ru_RU : 러시아
vi_VN : 베트남
zh_CN : 중국
zh_TW : 타이완
de_LI : 리히텐슈타인
th_TH : 태국

로케일(국가)별 시간 형식의 예는 다음과 같다. 

ar_SA : " ٣:٠٢:٢٧ م"
de_DE : "15:02:27"
en_US : "3:02:27 PM"
es_ES : "15:02:27"
fr_FR : "15:02:27"
id_ID : "15.02.27"
it_IT : "15:02:27"
ja_JP : "15時02分27秒"
ko_KR : "오후 3시 2분 27초"
pl_PL : "15:02:27"
pt_PT : "15:02:27"
ru_RU : "15:02:27"
vi_VN : "15:02:27"
zh_CN : "下午3时02分27秒"
zh_TW : "下午3時02分27秒"
de_LI : "15:02:27 "
th_TH : "15 นาฬิกา 2 นาที 27 วินาที "

위 시간의 포맷은 특정 Framework를 사용한 한 예다. 

물론 모든 소프트웨어가 위에서 언급한 타임존, 특수한 달력, 지역에 맞는 형식을 모두 지원하는 것은 아니다. 또한 우리가 만드는 소프트웨어도 모두 지원해야 하는 것은 아니다. 소프트웨어 성격과 판매 지역, 전력에 따라서 지원해야 하는 범위를 개발 초기에 결정해야 한다. 그래야 소프트웨어 국제화가 성공적으로 진행될 수 있다.

많은 소프트웨어에서 단수/복수 번역이 엉터리인 이유 (17)

앞으로 번역에 대해서 다룰 여러 주제 중에서 오늘은 단수/복수에 대해서 알아보자. 

국제화/지역화가 잘 된 소프트웨어라고 하더라도 단수/복수를 제대로 처리하고 있는 소프트웨어는 찾아보기 쉽지 않다. 우리는 단수/복수에 대한 구분을 하지 않는 언어를 사용하고 있다. 그래서 단수/복수 처리에 대해서 민감하지 않다. 하지만 단수/복수를 구분하는 언어권에서는 이를 제대로 처리하지 않으면 어색한 표현이 된다. 물론 의미는 알아보겠지만 그만큼 소프트웨어의 품질은 떨어지게 된다.




많은 소프트웨어에서 단수/복수 처리가 어렵기 때문에 꼼수를 부리곤 한다. 예를 들어 “%d file has been deleted”라는 문장을 사용해서 %d는 파일의 개수, 즉 숫자로 교체가 가능하다고 보자. 파일이 한 개라면 “1 file has been deleted”이 되지만 파일이 5개라면 “5 file has been deleted”가 되어서 어색한 문장이 된다.

그래서 “5 file(s) has been deleted”라고 하곤 하는데 has도 어색하다. 결국 “%d file(s) has(have) been deleted”라는 포맷을 이용하여 “5 file(s) has(have) been deleted”라고 할 수는 있지만 몹시 보기 지저분한 문장이 아닐 수 없다. 그래서 처음부터 “The number of deleted files: 5” 이와 같이 단수/복수와 무관한 문장들을 만들어 내지만 부자연스러운 것은 여전하다.

그리고 file(s)와 같은 꼼수를 쓰는 것은 영어나 몇몇 언어에서만 통한다. 우리나라 대부분의 개발자들은 한국어와 영어에 익숙하기 때문에 전세계 대부분의 언어가 그 틀에 크게 벗어나지 않으려니 추측하지만 우리가 상상할 수 없는 언어는 매우 많다.




예를 들어 폴란드어를 보자. 폴란드어에서 file은 plik이다. 이것은 단수 표현이다. 즉 file이 하나면 plik다. 그리고 2개면 pliki다. 3,4개도 pliki다. 하지만 5개가 되면 pliko’w로 바뀐다. 게다가 21개까지만 pliko’w다. 말로 설명하면 복잡하니 아래를 보라.

1 plik
2,3,4 pliki
5-21 pliko'w
22-24 pliki
25-31 pliko'w
32-34 pliki
35-41 pliko'w

복수의 형태가 두 가지인데 애매한 구간에서 바뀌며 끝에 ‘s’를 붙이는 형태도 아니다. 그래서 영어에서나 사용하던 끝에 (s)를 붙이는 형태로는 번역이 안된다. 

우리와 같이 단수/복수 구분을 하지 않는 언어는 일본어, 베트남어가 있다. 그리고 영어처럼 2개부터 복수로 처리를 하는 언어는 독일어, 스페인어, 이탈리아어, 그리스어, 터키어 등 가장 많은 언어가 여기에 속한다. 하지만 라트비아, 루마니아, 리투아니아, 러시아, 우크라이나, 크로아티아, 체코, 슬로베니아 등은 제각각 다른 단수/복수 체계를 가지고 있다. 

이런 상황에서 file(s)와 같이 쓰던가 단수/복수 처리를 별 것 아니라고 모두 단수로 처리해서 소프트웨어를 사용하라고 하는 것은 소프트웨어의 품질을 떨어뜨리는 결과가 된다. 현재도 여전히 많은 소프트웨어가 단수/복수 처리를 제대로 하고 있지는 않다.

그럼 소프트웨어에서 단수/복수 처리를 어떻게 할 수 있을까? 구체적인 방법은 추후 소개를 하도록 하겠다.



(을를이가 문제를 피해간 사례)

이와 유사한 문제로는 “을를이가은는” 문제가 있다. 한국어에만 있는 이슈인데 소프트웨어에서 “%s을 삭제했습니다.”라는 형식을 이용해서 %s를 “파일” 또는 “이미지”로 교체를 해서 출력한다고 보자. 이때 “이미지”로 교체를 하면 “이미지을”이라는 어색하는 문장이 된다. 그래서 “%s을(를) 삭제했습니다.”이라고 꼼수를 쓰던가 “파일을 삭제했습니다.”와 “이미지를 삭제했습니다.” 두 문장을 각각 번역하기도 한다. 하지만 %s에 들어가야 할 단어의 개수가 많고 문장이 조금만 더 복잡하면 번역해야 할 문장이 기하급수로 늘어서 좋은 방법은 아니다. 

단수/복수 문제는 전세계적인 문제라서 이를 연구하는 사람이 많지만 “을를이가은는”문제는 우리가 스스로 해결해야 한다. 

단수/복수 문제는 사소해 보이지만 무시할 수 없는 이슈이고 이러한 수준의 이슈들은 소프트웨어 번역에서 수 많이 존재한다. 급한 개발자나 경영자들은 언제 이런 이슈까지 신경을 쓰면서 소프트웨어를 개발하고 국제화를 하겠냐고 하소연을 하거나 스스로 해결을 해보고자 연구하고 라이브러리를 만들고 있을 수도 있다. 



하지만 소프트웨어 국제화는 이미 이전의 개발자들이 많이 연구를 해놓았고 해결책을 제시해 놓았기 때문에 대부분의 문제는 이미 제시된 해결책을 잘 찾아서 사용하기만 하면 된다. 스스로 해결해야 하는 문제도 있지만 이미 나와 있는 솔루션을 모르고 또 만드는 경우도 많다.물론 그렇게 재탄생한 자체 솔루션의 99%는 함정에 빠져서 문제를 완벽하게 해결하지 못한다. 이런 현상은 집을 만들기 위해서 망치가 없다고 망치를 직접 만드는 것과 비견된다. 

그래서 소프트웨어 국제화에는 방대한 지식이 필요한 것이다. 무엇이 필요하고 누가 이미 만들어 놓은 것들에는 어떤 것들이 있는지 알려면단편적인 지식보다는 소프트웨어 국제화 전반에 걸쳐 광범위한 지식이 필요하다. 

본 시리즈의 포스트를 통해서 조금씩 알아나가도록 하자.