/
gettext 둜 κ΅­μ œν™”(i18n)와 지역화(L10N) 된 SW κ°œλ°œν•˜κΈ°

gettext 둜 κ΅­μ œν™”(i18n)와 지역화(L10N) 된 SW κ°œλ°œν•˜κΈ°


SW λ‚˜ μ„œλΉ„μŠ€λ₯Ό κ°œλ°œν•˜λ‹€λ³΄λ©΄ μ‚¬μš©μžμ˜ ν™˜κ²½μ— 따라 λ‹€λ₯Έ locale 둜 μ²˜λ¦¬ν•΄μ•Ό ν•˜λŠ” ν•˜λŠ” κ²½μš°κ°€ μƒκΈΈμˆ˜ μžˆμŠ΅λ‹ˆλ‹€.

Unix 운영 체제(Linux 포함)λŠ” 이런 μš”κ΅¬ 사항을 λ§Œμ‘±ν•˜κΈ° μœ„ν•΄ 이미 μ˜€λž˜μ „λΆ€ν„° κ΅­μ œν™”μ™€ 지역화 κΈ°λŠ₯을 μ§€μ›ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.


μ΅œκ·Όμ—λŠ” Java λ₯Ό 많이 μ‚¬μš©ν•˜λ―€λ‘œ Java 의 Resource bundle 을 μ΄μš©ν•˜μ—¬ λ©”μ‹œμ§€λ₯Ό λ²ˆμ—­ν•˜λŠ” 방식을 μš°μ„ μ μœΌλ‘œ μƒκ°ν• μˆ˜ μžˆμ§€λ§Œ λ©”μ‹œμ§€ λ²ˆμ—­μ€ κ΅­μ œν™”λœ SW 의 일뢀일뿐 ν†΅ν™”λ‚˜ μ‹œκ°„ ν‘œμ‹œ, 숫자 ν‘œκΈ° 방법, λ‹¨μˆ˜/볡수, λͺ…μ‚¬μ˜ 성별등 λ‹€μ–‘ν•œ ν•­λͺ©μ„ κ³ λ €ν•΄μ•Ό ν•©λ‹ˆλ‹€.


gettext λŠ” 였래된 κΈ°μˆ μ΄μ§€λ§Œ κ΅­μ œν™”μ™€ 지역화에 λŒ€ν•œ κ°œλ…μ„ μ΄ν•΄ν•˜κ³  μ μš©ν•˜κΈ° μ’‹κ³  PHP λ‚˜ Python λ“±μ—μ„œλ„ μ§€μ›ν•˜λ―€λ‘œ λ°°μ›Œλ‘λ©΄ μœ μš©ν•œ 지식인데 마침 gettext λ₯Ό μ‚¬μš©ν•˜λŠ” μ•„μ£Ό 였래된 μ‹œμŠ€ν…œμ„ μˆ˜μ •ν•  일이 μƒκ²¨μ„œ 정리해 λ΄…λ‹ˆλ‹€.

유λͺ…ν•œ PHP CMS인 wordpress 도 κ΅­μ œν™” 뢀뢄을 gettext 둜 μ²˜λ¦¬ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.


gettext λž€?

gettext λŠ” Unix 계열 μš΄μ˜μ²΄μ œμ—μ„œ κ΅­μ œν™”(Internalization; μ€„μ—¬μ„œ i18n), 지역화(Localization; μ€„μ—¬μ„œ l10n) λ₯Ό μ§€μ›ν•˜λŠ” SW λ₯Ό λ§Œλ“€κΈ° μœ„ν•œ μ‹œμŠ€ν…œμœΌλ‘œ μ΄μ œλŠ” μ˜€λΌν΄μ— 인수된 Sun Micros ystems κ°€ κ°œλ°œν–ˆμŠ΅λ‹ˆλ‹€.

gettext λŠ” λ²ˆμ—­κ³Ό ν”„λ‘œκ·Έλž˜λ°μ„ λΆ„λ¦¬ν•΄μ„œ SW λ‚΄ λ©”μ‹œμ§€ λ²ˆμ—­μ„ μœ„ν•΄ κ°œλ°œμžκ°€ μ†ŒμŠ€λ₯Ό μˆ˜μ •ν•  ν•„μš”κ°€ μ—†μœΌλ©° λ²ˆμ—­μžλŠ” μ†ŒμŠ€ μ½”λ“œκ°€ 없어도 λ©”μ‹œμ§€λ₯Ό λ²ˆμ—­ν•  수 μžˆμŠ΅λ‹ˆλ‹€.


gettext 의 runtime library 은 libintl 이며 python μ΄λ‚˜ PHP μ‚¬μš©μžλŠ”Β  libintl 이 ν•„μš”ν•œ μ™ΈλΆ€ νŒ¨ν‚€μ§€λ₯Ό 경우λ₯Ό λ³Έ 적이 μžˆμ„κ²λ‹ˆλ‹€.

gettext 의 유λͺ…ν•œ κ΅¬ν˜„λ¬Όμ€‘ ν•˜λ‚˜λŠ” GNU gettext 이며 이 λ¬Έμ„œλ„ GNU gettext λ₯Ό κΈ°μ€€μœΌλ‘œ μ„€λͺ…ν•©λ‹ˆλ‹€.


life cycle

gettext λ₯Ό μ‚¬μš©ν•΄μ„œ κ΅­μ œν™”/지역화λ₯Ό κ΅¬ν˜„ν•˜λŠ” work flow λŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.



1. λ¨Όμ € μ†ŒμŠ€ μ½”λ“œλ‚΄μ— κ΅­μ œν™”κ°€ ν•„μš”ν•œ 뢀뢄을 gettext ν•¨μˆ˜λ‘œ κ°μ‹Έμ„œ ν‘œμ‹œν•΄ μ€λ‹ˆλ‹€.

#define THIS_PACKAGE_NAME "hello" 

char* locale_dir = "locale"; // locale νŒŒμΌμ„ μ½μ–΄μ˜¬ 디렉터리

setlocale (LC_ALL, "");

// νŒ¨ν‚€μ§€ 이름. locale μ΄λ¦„μ˜ 폴더 밑에 hello.mo κ°€ μžˆμ–΄μ•Ό 함.
bindtextdomain (THIS_PACKAGE_NAME, locale_dir);
textdomain (THIS_PACKAGE_NAME);

// λ²ˆμ—­λ˜μ„œ λ³΄μ—¬μ§ˆ 뢀뢄은 gettext 둜 κ°μ‹Έμ€Œ
printf(gettext("My name is %s.\n"), my_name);

// gettext λŒ€μ‹  _ macro μ‚¬μš©ν•΄λ„ 됨.
printf(_("my age is %d.\n"), my_age);


2. μ†ŒμŠ€ μˆ˜μ •μ΄ λλ‚¬μœΌλ©΄ μ†ŒμŠ€μ—μ„œ gettext 둜 ν‘œμ‹œν•œ 뢀뢄을 μΆ”μΆœν•΄ μ£ΌλŠ” xgettext λ₯Ό μ΄μš©ν•΄μ„œ λ©”μ‹œμ§€λ₯Ό μΆ”μΆœν•œν›„μ— μ €μž₯ν•©λ‹ˆλ‹€. μ•„λž˜λŠ” μΆ”μΆœν•œ λ©”μ‹œμ§€ νŒŒμΌμ„ hello.pot 둜 μ €μž₯ν•˜λŠ” μ˜ˆμ œμž…λ‹ˆλ‹€.

xgettext -c hello.c hello.h -o hello.pot


3. pot νŒŒμΌμ„ μ—λ””ν„°λ‘œ 열어보면 μ•„λž˜μ™€ 같이 메타 정보가 있고 λ§ˆμ§€λ§‰μ— msgid 와 msgstr 이 λ³΄μΌκ²λ‹ˆλ‹€.

# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-29 22:23-0400\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"

#: hello.c:18
#, c-format
msgid "my name is %s.\n"
msgstr ""

msgid κ°€ μ†ŒμŠ€ μ½”λ“œμ— μžˆλŠ” λ©”μ‹œμ§€μ΄κ³  msgstr 은 λ²ˆμ—­ν•  λ©”μ‹œμ§€μž…λ‹ˆλ‹€.


4. msginit λͺ…령을 μ΄μš©ν•΄μ„œ λ²ˆμ—­ν•  μ–Έμ–΄μ˜ locale 에 맞게 λ©”μ‹œμ§€ μΉ΄νƒˆλ‘œκ·Έ νŒŒμΌμ„ λ§Œλ“­λ‹ˆλ‹€. λ©”μ‹œμ§€ μΉ΄νƒˆλ‘œκ·ΈλŠ” .po ν™•μž₯자λ₯Ό 가지며 po λŠ” Portable Object μž…λ‹ˆλ‹€.
po νŒŒμΌμ€ λ¬Έμžμ—΄λ‘œ κ΅¬μ„±λ˜μ–΄ μžˆμ–΄μ„œ μ„ ν˜Έν•˜λŠ” μ—λ””ν„°λ‘œ μˆ˜μ •ν•΄λ„ λ˜μ§€λ§Œ μ•„λž˜μ—μ„œ μ„€λͺ…ν•  μ „λ¬Έ 에디터λ₯Ό μ‚¬μš©ν•˜λŠ” 게 μ’‹μŠ΅λ‹ˆλ‹€.Β  μ•„λž˜λŠ” ν•œκΈ€κ³Ό 독일어 둜캘용 po νŒŒμΌμ„ λ§Œλ“œλŠ” μ˜ˆμ‹œμž…λ‹ˆλ‹€.

$ msginit -i hello.pot -o ko.po -l ko_KR.utf8
$ msginit -i hello.pot -o de.po -l de_DE

-i : μž…λ ₯ μ†ŒμŠ€λ₯Ό μ„€μ •ν•©λ‹ˆλ‹€.
-o : μ €μž₯ν•  po 파일 λͺ…을 μ„€μ •ν•˜λ©° 일반적으둜 파일λͺ…은 LOCALE_NAME.po ν˜•μ‹μœΌλ‘œ μ μ–΄μ€λ‹ˆλ‹€.
-l : locale 을 μ§€μ •ν•©λ‹ˆλ‹€.


msginit λ₯Ό μ‹€ν–‰ν•˜λ©΄ μ•„λž˜μ™€ 같이 μƒˆλ‘œμš΄ λ©”μ‹œμ§€ μΉ΄νƒˆλ‘œκ·Έμ— 넣을 email μ£Όμ†Œλ₯Ό λ¬Όμ–΄λ³΄λŠ”λ° ν™•μΈν•˜κ³  μ—”ν„°λ₯Ό 치면 

The new message catalog should contain your email address, so that users can
give you feedback about the translations, and so that maintainers can contact
you in case of unexpected technical problems.

Is the following your email address?
  lesstif@gmail.com
Please confirm by pressing Return, or enter your email address.


5. 이제 Po Edit 같은 도ꡬλ₯Ό μ‚¬μš©ν•΄μ„œ locale 별 파일(예: ko.po)을 μˆ˜μ •ν•˜κ³  μ €μž₯ν•΄ μ€λ‹ˆλ‹€.


6. .po μˆ˜μ •μ΄ λλ‚¬μœΌλ©΄ 이제 mo(Machine Object) 둜 μ»΄νŒŒμΌν•΄μ•Ό gettext μ—μ„œ run time 에 locale 별 μ–Έμ–΄ νŒŒμΌμ„ λ‘œλ”©ν•  수 μžˆμŠ΅λ‹ˆλ‹€. Po edit 의 경우 파일 Β β†’ "Mo둜 컴파일" λ©”λ‰΄μ—μ„œ .mo 파일둜 μ»΄νŒŒμΌν•΄μ€λ‹ˆλ‹€.

λ¦¬λˆ…μŠ€ μ»€λ§¨λ“œμ—μ„œλŠ” msgfmt λͺ…λ ΉμœΌλ‘œ po λ₯Ό mo 둜 μ»΄νŒŒμΌν•  수 μžˆμŠ΅λ‹ˆλ‹€.

$ mkdir -p locale/ko/LC_MESSAGES/
$ msgfmt ko.po -o locale/ko/LC_MESSAGES/hello.mo

locale 용 데이터가 μ‹€ν–‰ 파일(hello)이 μžˆλŠ” ν΄λ”μ˜ ν•˜μœ„μ— locale/LOCALE/LOCALE_TYPE/ 경둜둜 μžˆμ„ κ²½μš°μž…λ‹ˆλ‹€.


이제 locale 을 변경없이 hello λ₯Ό μ‹€ν–‰ν•˜λ©΄ κΈ°λ³Έ 섀정인 μ˜μ–΄λ‘œ ν‘œμ‹œλ©λ‹ˆλ‹€.

$ ./hello john 50 

my name is john.
my age is 50.


μ œλŒ€λ‘œ λ™μž‘ν•˜λŠ”μ§€ ν™•μΈν•˜κΈ° μœ„ν•΄ locale 을 ko_KR 둜 μ„€μ •ν•˜κ³  locale λͺ…λ Ήμ–΄λ‘œ 확인해 λ΄…λ‹ˆλ‹€.

$ export LANG=ko_KR
$ locale

LANG=ko_KR
LC_CTYPE="ko_KR"
LC_NUMERIC="ko_KR"
LC_TIME="ko_KR"
LC_COLLATE="ko_KR"
LC_MONETARY="ko_KR"
LC_MESSAGES="ko_KR"
LC_PAPER="ko_KR"
LC_NAME="ko_KR"
LC_ADDRESS="ko_KR"
LC_TELEPHONE="ko_KR"
LC_MEASUREMENT="ko_KR"
LC_IDENTIFICATION="ko_KR"
LC_ALL=


λ‹€μ‹œ hello λ₯Ό μ‹€ν–‰ν•˜λ©΄ λ©”μ‹œμ§€κ°€ ν•œκΈ€λ‘œ ν‘œμ‹œλ˜λŠ” κ±Έ 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

$ ./hello 똠방 50

λ‚΄ 이름은 λ˜ λ°©μž…λ‹ˆλ‹€.
λ‚΄ λ‚˜μ΄λŠ” 50μž…λ‹ˆλ‹€.

C μ–Έμ–΄ μ˜ˆμ‹œ

C 둜 λ§Œλ“  gettext μ‚¬μš© 예제λ₯Ό https://github.com/lesstif/gettext-example 에 μ˜¬λ €λ†¨μœΌλ‹ˆ μ°Έκ³ ν•˜μ„Έμš”.


같이 보기

Ref

Related content