locale — 국제화 서비스

locale — 국제화 서비스

locale 모듈은 POSIX 로케일 데이터베이스와 기능에 접근을 열어 줘요. POSIX 로케일 메커니즘은 프로그래머가 소프트웨어가 실행되는 각 국가의 세부 사항을 모두 알 필요 없이, 애플리케이션에서 특정 문화적 문제를 다룰 수 있게 해 줘요.

locale 모듈은 _locale 모듈 위에 구현되고, _locale은 (가능하면) ANSI C 로케일 구현을 사용해요.

출처: Python 표준 라이브러리

본문

locale 모듈은 다음 예외와 함수를 정의해요.

exception locale.Error

setlocale()에 전달된 로케일을 인식하지 못할 때 일으키는 예외.

locale.setlocale(category, locale=None)

locale이 주어지고 None이 아니면, setlocale()은 해당 category의 로케일 설정을 수정해요. 사용 가능한 카테고리는 아래 데이터 설명에 나열돼 있어요. locale은 문자열일 수도 있고, 언어 코드와 인코딩의 쌍일 수도 있어요. 빈 문자열은 사용자의 기본 설정을 지정해요. 로케일 수정이 실패하면 Error 예외가 나요. 성공하면 새 로케일 설정이 반환돼요.

locale이 쌍이면 로케일 별칭 엔진을 사용해 로케일 이름으로 변환돼요. 언어 코드는 로케일 이름과 같은 형식이지만 인코딩과 @-수정자가 없어요. 언어 코드와 인코딩은 None일 수 있어요.

locale을 생략하거나 None이면 category의 현재 설정이 반환돼요.

예:

>>> import locale
>>> loc = locale.setlocale(locale.LC_ALL)  # get current locale
# use German locale; name and availability varies with platform
>>> locale.setlocale(locale.LC_ALL, 'de_DE.UTF-8')
>>> locale.strcoll('f\xe4n', 'foo')  # compare a string containing an umlaut
>>> locale.setlocale(locale.LC_ALL, '')   # use user's preferred locale
>>> locale.setlocale(locale.LC_ALL, 'C')  # use default (C) locale
>>> locale.setlocale(locale.LC_ALL, loc)  # restore saved locale

setlocale()은 대부분의 시스템에서 스레드 안전하지 않아요. 애플리케이션은 보통 이런 호출로 시작해요:

import locale
locale.setlocale(locale.LC_ALL, '')

이것은 모든 카테고리의 로케일을 사용자의 기본 설정(보통 LANG 환경 변수에 지정)으로 설정해요. 그 후 로케일을 바꾸지 않으면 멀티스레딩 사용은 문제를 일으키지 않아요.

locale.localeconv()

로컬 관례의 데이터베이스를 딕셔너리로 돌려줘요. 이 딕셔너리는 다음 문자열을 키로 가져요:

카테고리 의미
LC_NUMERIC 'decimal_point' 소수점 문자.
'grouping' 'thousands_sep'가 기대되는 상대 위치를 지정하는 숫자의 시퀀스. 시퀀스가 CHAR_MAX로 끝나면 더 이상의 그룹화가 수행되지 않아요. 0으로 끝나면 마지막 그룹 크기가 반복 사용돼요.
'thousands_sep' 그룹 사이에 사용되는 문자.
LC_MONETARY 'int_curr_symbol' 국제 통화 기호.
'currency_symbol' 로컬 통화 기호.
'p_cs_precedes/n_cs_precedes' 통화 기호가 값 앞에 오는지 여부(양수·음수 값에 대해).
'p_sep_by_space/n_sep_by_space' 통화 기호가 값에서 공백으로 분리되는지 여부(양수·음수 값에 대해).
'mon_decimal_point' 통화 값에 사용되는 소수점.
'frac_digits' 통화 값의 로컬 형식화에 사용되는 소수 자릿수.
'int_frac_digits' 통화 값의 국제 형식화에 사용되는 소수 자릿수.
'mon_thousands_sep' 통화 값에 사용되는 그룹 구분자.
'mon_grouping' 통화 값에 사용되는 'grouping'과 동등.
'positive_sign' 양수 통화 값을 표시하는 기호.
'negative_sign' 음수 통화 값을 표시하는 기호.
'p_sign_posn/n_sign_posn' 부호의 위치(양수·음수 값에 대해), 아래 참고.

모든 숫자 값은 이 로케일에 지정된 값이 없음을 나타내도록 CHAR_MAX로 설정될 수 있어요.

'p_sign_posn''n_sign_posn'의 가능한 값은 아래와 같아요:

설명
0 통화와 값이 괄호로 둘러싸임.
1 부호가 값과 통화 기호 앞에 와야 함.
2 부호가 값과 통화 기호 뒤에 와야 함.
3 부호가 값 바로 앞에 와야 함.
4 부호가 값 바로 뒤에 와야 함.
CHAR_MAX 이 로케인에 지정된 게 없음.

숫자나 통화 문자열이 비-ASCII이고 로케일이 다르면, 이 함수는 LC_CTYPE 로케일을 LC_NUMERIC 로케일 또는 LC_MONETARY 로케일로 일시적으로 설정해요. 이 임시 변경은 다른 스레드에 영향을 줘요.

버전 3.7에서 변경: 함수가 이제 어떤 경우에 LC_CTYPE 로케일을 LC_NUMERIC 로케일로 일시적으로 설정함.

locale.nl_langinfo(option)

로케일 특정 정보 일부를 문자열로 돌려줘요. 이 함수는 모든 시스템에서 사용 가능한 건 아니고, 가능한 옵션 집합도 플랫폼마다 다를 수 있어요. 가능한 인자 값은 숫자인데, locale 모듈에 기호 상수가 있어요.

nl_langinfo() 함수는 다음 키 중 하나를 받아요. 대부분 설명은 GNU C 라이브러리의 해당 설명에서 가져왔어요.

  • locale.CODESET — 선택된 로케일에서 사용되는 문자 인코딩의 이름을 가진 문자열.
  • locale.D_T_FMTtime.strftime()의 형식 문자열로 써서 날짜와 시간을 로케일 특정 방식으로 나타내는 문자열.
  • locale.D_FMTtime.strftime()의 형식 문자열로 써서 날짜를 로케일 특정 방식으로 나타내는 문자열.
  • locale.T_FMTtime.strftime()의 형식 문자열로 써서 시간을 로케일 특정 방식으로 나타내는 문자열.
  • locale.T_FMT_AMPMtime.strftime()의 형식 문자열로 써서 시간을 am/pm 형식으로 나타내는 것.
  • locale.DAY_1 ~ locale.DAY_7 — n번째 요일의 이름. 참고: 이것은 DAY_1이 일요일인 미국 관례를 따르지, 월요일이 주의 첫날인 국제 관례(ISO 8601)를 따르지 않아요.
  • locale.ABDAY_1 ~ locale.ABDAY_7 — n번째 요일의 약어 이름.
  • locale.MON_1 ~ locale.MON_12 — n번째 달의 이름.
  • locale.ABMON_1 ~ locale.ABMON_12 — n번째 달의 약어 이름.
  • locale.RADIXCHAR — 기수 문자(소수점, 소수 쉼표 등).
  • locale.THOUSEP — 천 단위(세 자리 그룹) 구분 문자.
  • locale.YESEXPR — yes/no 질문에 긍정 응답을 인식하는 데 regex 함수로 쓸 수 있는 정규식.
  • locale.NOEXPR — yes/no 질문에 부정 응답을 인식하는 데 regex(3) 함수로 쓸 수 있는 정규식. 참고: YESEXPRNOEXPR의 정규식은 C 라이브러리의 regex 함수에 적합한 문법을 사용하는데, re에서 사용하는 문법과 다를 수 있어요.
  • locale.CRNCYSTR — 통화 기호. 기호가 값 앞에 와야 하면 "-"가, 뒤에 와야 하면 "+"가, 기호가 기수 문자를 대체해야 하면 "."가 앞에 붙어요.
  • locale.ERA — 각 시대(era)에 대해 연도가 어떻게 세고 표시되는지 설명하는 문자열. 대부분 로케일은 이 값을 정의하지 않아요. 정의하는 로케일의 예는 일본어예요. 일본에서 전통적 날짜 표현은 당시 천황의 재위에 해당하는 시대 이름을 포함해요. 보통 이 값을 직접 사용할 필요는 없어요. 형식 문자열에 E 수정자를 지정하면 time.strftime() 함수가 이 정보를 사용해요. 반환 문자열의 형식은 The Open Group Base Specifications Issue 8, 7.3.5.2절 LC_TIME C-Language Access에 지정돼 있어요.
  • locale.ERA_D_T_FMTtime.strftime()의 형식 문자열로 써서 날짜와 시간을 로케일 특정 시대 기반 방식으로 나타내는 것.
  • locale.ERA_D_FMTtime.strftime()의 형식 문자열로 써서 날짜를 로케일 특정 시대 기반 방식으로 나타내는 것.
  • locale.ERA_T_FMTtime.strftime()의 형식 문자열로 써서 시간을 로케일 특정 시대 기반 방식으로 나타내는 것.
  • locale.ALT_DIGITS — 값 0부터 99를 로케일 특정 방식으로 나타내는 데 쓰는 최대 100개의 세미콜론으로 구분된 기호로 구성된 문자열. 대부분 로케일에서 이것은 빈 문자열이에요.

요청된 값을 결정하는 카테고리(LC_TIME, LC_NUMERIC, LC_MONETARY 또는 LC_MESSAGES)의 로케일과 로케일이 다르고 결과 문자열이 비-ASCII면, 이 함수는 LC_CTYPE 로케일을 그 카테고리 로케일로 일시적으로 설정해요. 이 임시 변경은 다른 스레드에 영향을 줘요.

버전 3.14에서 변경: 함수가 이제 어떤 경우에 LC_CTYPE 로케일을 일시적으로 설정함.

locale.getdefaultlocale([envvars])

기본 로케일 설정을 결정하려 시도하고 (language code, encoding) 형태의 튜플로 돌려줘요.

POSIX에 따르면 setlocale(LC_ALL, '')을 호출하지 않은 프로그램은 이식 가능한 'C' 로케일로 실행돼요. setlocale(LC_ALL, '')을 호출하면 LANG 변수로 정의된 기본 로케일을 쓰게 해요. 현재 로케일 설정을 방해하고 싶지 않으므로 위에서 설명한 방식으로 동작을 에뮬레이트해요.

다른 플랫폼과의 호환성을 유지하기 위해 LANG 변수만 테스트하는 게 아니라 envvars 매개변수로 주어진 변수 목록을 테스트해요. 처음으로 정의된 걸 찾은 변수가 사용돼요. envvars는 GNU gettext에서 사용하는 검색 경로를 기본값으로 하고, 항상 'LANG' 변수 이름을 포함해야 해요. GNU gettext 검색 경로는 'LC_ALL', 'LC_CTYPE', 'LANG', 'LANGUAGE' 순서를 포함해요.

언어 코드는 로케일 이름과 같은 형식이지만 인코딩과 @-수정자가 없어요. 값을 결정할 수 없으면 언어 코드와 인코딩이 None일 수 있어요. "C" 로케일은 (None, None)으로 표현돼요.

locale.getlocale(category=LC_CTYPE)

주어진 로케일 카테고리의 현재 설정을 언어 코드와 인코딩을 담은 튜플로 돌려줘요. categoryLC_ALL을 제외한 LC_* 값 중 하나일 수 있어요. 기본값은 LC_CTYPE이에요. 언어 코드는 로케일 이름과 같은 형식이지만 인코딩과 @-수정자가 없어요. 값을 결정할 수 없으면 언어 코드와 인코딩이 None일 수 있어요. "C" 로케일은 (None, None)으로 표현돼요.

locale.getpreferredencoding(do_setlocale=True)

사용자 선호에 따라 텍스트 데이터에 사용되는 로케일 인코딩을 돌려줘요. 사용자 선호는 시스템마다 다르게 표현되고 어떤 시스템에서는 프로그래밍적으로 사용할 수 없을 수 있으므로, 이 함수는 추측만 돌려줘요.

어떤 시스템에서는 사용자 선호를 얻기 위해 setlocale()을 호출해야 하므로 이 함수는 스레드 안전하지 않아요. setlocale 호출이 필요 없거나 원치 않으면 do_setlocaleFalse로 설정해야 해요.

Android에서나 Python UTF-8 Mode가 활성화되면 항상 'utf-8'을 돌려주고, 로케일 인코딩과 do_setlocale 인자는 무시돼요. Python 사전 초기화는 LC_CTYPE 로케일을 구성해요. 파일시스템 인코딩과 오류 핸들러도 참고하세요.

버전 3.7에서 변경: 함수가 이제 Android나 Python UTF-8 Mode가 활성화되면 항상 "utf-8"을 돌려줌.

locale.getencoding()

현재 로케일 인코딩을 가져와요:

  • Android와 VxWorks에서는 "utf-8"을 돌려줘요.
  • Unix에서는 현재 LC_CTYPE 로케일의 인코딩을 돌려줘요.
  • nl_langinfo(CODESET)이 빈 문자열을 돌려주면(예: 현재 LC_CTYPE 로케일이 지원되지 않을 때) "utf-8"을 돌려줘요.
  • Windows에서는 ANSI 코드 페이지를 돌려줘요.

Python 사전 초기화는 LC_CTYPE 로케일을 구성해요. 파일시스템 인코딩과 오류 핸들러도 참고하세요. 이 함수는 이 함수가 Python UTF-8 Mode를 무시한다는 점 빼고 getpreferredencoding(False)와 비슷해요.

버전 3.11에 추가됨.

locale.normalize(localename)

주어진 로케일 이름에 대해 정규화된 로케일 코드를 돌려줘요. 반환된 로케일 코드는 setlocale()과 함께 쓰도록 형식화돼 있어요. 정규화가 실패하면 원래 이름이 그대로 반환돼요. 주어진 인코딩을 알 수 없으면 setlocale()과 마찬가지로 로케일 코드의 기본 인코딩으로 기본 설정돼요.

locale.strcoll(string1, string2)

현재 LC_COLLATE 설정에 따라 두 문자열을 비교해요. 다른 비교 함수처럼, string1string2보다 앞에 오는지 뒤에 오는지 같은지에 따라 음수, 양수 또는 0을 돌려줘요.

locale.strxfrm(string)

로케일 인식 비교에 사용할 수 있는 문자열로 변환해요. 예를 들어 strxfrm(s1) < strxfrm(s2)strcoll(s1, s2) < 0과 동등해요. 이 함수는 같은 문자열을 반복 비교할 때(예: 문자열 시퀀스를 collate할 때) 쓸 수 있어요.

locale.format_string(format, val, grouping=False, monetary=False)

현재 LC_NUMERIC 설정에 따라 숫자 val을 형식화해요. 형식은 % 연산자의 관례를 따라요. 부동소수점 값은 적절하면 소수점이 수정돼요. groupingTrue면 그룹화도 고려해요. monetary가 참이면 변환에 통화 천 단위 구분자와 그룹화 문자열을 사용해요. format % val처럼 형식 지정자를 처리하지만 현재 로케일 설정을 고려해요.

버전 3.7에서 변경: monetary 키워드 매개변수 추가.

locale.currency(val, symbol=True, grouping=False, international=False)

현재 LC_MONETARY 설정에 따라 숫자 val을 형식화해요. symbol이 참(기본값)이면 반환 문자열에 통화 기호가 포함돼요. groupingTrue(기본값 아님)이면 값으로 그룹화가 수행돼요. internationalTrue(기본값 아님)이면 국제 통화 기호가 사용돼요.

참고: 이 함수는 'C' 로케일에서 동작하지 않으므로, 먼저 setlocale()으로 로케일을 설정해야 해요.

locale.str(float)

내장 함수 str(float)와 같은 형식으로 부동소수점 수를 형식화하지만 소수점을 고려해요.

locale.delocalize(string)

LC_NUMERIC 설정에 따라 문자열을 정규화된 숫자 문자열로 변환해요. 버전 3.5에 추가됨.

locale.localize(string, grouping=False, monetary=False)

LC_NUMERIC 설정에 따라 정규화된 숫자 문자열을 형식화된 문자열로 변환해요. 버전 3.10에 추가됨.

locale.atof(string, func=float)

LC_NUMERIC 설정에 따라 문자열을 숫자로 변환해요. stringdelocalize()를 호출한 결과에 func를 호출해서요.

locale.atoi(string)

LC_NUMERIC 관례에 따라 문자열을 정수로 변환해요.

locale.LC_CTYPE

문자 타입 함수를 위한 로케일 카테고리. 가장 중요하게, 이 카테고리는 텍스트 인코딩을 정의해요. 즉 바이트가 유니코드 코드 포인트로 어떻게 해석되는지예요. 이 변수가 컨테이너의 잘못된 설정이나 원격 SSH 연결로 전달된 호환되지 않는 설정으로 생긴 문제를 피하기 위해 C.UTF-8로 자동 강제 변환될 수 있는 방법은 PEP 538과 PEP 540을 참고하세요.

Python은 내부적으로 ctype.h의 로케일 종속 문자 변환 함수를 사용하지 않아요. 대신 pyctype.hPy_TOLOWER 같은 로케일 독립 동등 기능을 제공해요.

locale.LC_COLLATE

문자열 정렬을 위한 로케일 카테고리. locale 모듈의 strcoll()strxfrm() 함수가 영향을 받아요.

locale.LC_TIME

시간 형식화를 위한 로케일 카테고리. time.strftime() 함수가 이 관례를 따라요.

locale.LC_MONETARY

통화 값 형식화를 위한 로케일 카테고리. 사용 가능한 옵션은 localeconv() 함수에서 사용할 수 있어요.

locale.LC_MESSAGES

메시지 표시를 위한 로케일 카테고리. Python은 현재 애플리케이션 특정 로케일 인식 메시지를 지원하지 않아요. os.strerror()가 돌려주는 것 같은 운영체제가 표시하는 메시지가 이 카테고리의 영향을 받을 수 있어요. 이 값은 POSIX 표준을 따르지 않는 운영체제(특히 Windows)에서는 사용할 수 없을 수 있어요.

locale.LC_NUMERIC

숫자 형식화를 위한 로케일 카테고리. locale 모듈의 format_string(), atoi(), atof(), str() 함수가 이 카테고리의 영향을 받아요. 다른 모든 숫자 형식화 연산은 영향을 받지 않아요.

locale.LC_ALL

모든 로케일 설정의 조합. 이 플래그를 로케일 변경 시 사용하면 모든 카테고리에 대해 로케일 설정을 시도해요. 어떤 카테고리든 실패하면 아무 카테고리도 변경되지 않아요. 이 플래그로 로케일을 검색하면 모든 카테고리의 설정을 나타내는 문자열이 반환돼요. 이 문자열은 나중에 설정을 복원하는 데 쓸 수 있어요.

locale.CHAR_MAX

localeconv()가 돌려주는 다양한 값에 사용되는 기호 상수.

배경, 세부 사항, 힌트, 팁과 주의사항

C 표준은 로케일을 변경하기에 비교적 비싼 프로그램 전체 속성으로 정의해요. 게다가 일부 구현은 자주 로케일을 바꾸면 코어 덤프가 날 수 있을 만큼 손상돼 있어요. 그래서 로케일을 올바르게 사용하기가 다소 고통스러울 수 있어요.

처음 프로그램이 시작될 때 사용자 선호 로케일과 무관하게 로케일은 C 로케일이에요. 한 가지 예외가 있어요: LC_CTYPE 카테고리는 시작 시 바뀌어 현재 로케일 인코딩을 사용자의 선호 로케일 인코딩으로 설정해요. 프로그램은 다른 카테고리에 대해 setlocale(LC_ALL, '')을 호출해 사용자 선호 로케일 설정을 원한다고 명시적으로 말해야 해요.

일반적으로 어떤 라이브러리 루틴에서 setlocale()을 호출하는 건 나쁜 생각이에요. 부작용으로 프로그램 전체에 영향을 주기 때문이에요. 저장하고 복원하는 것도 거의 그만큼 나빠요. 비싸고, 설정이 복원되기 전에 실행되는 다른 스레드에 영향을 주기 때문이에요.

일반용 모듈을 코딩할 때 로케일의 영향을 받는 연산(예: time.strftime()과 쓰는 특정 형식)의 로케일 독립 버전이 필요하면, 표준 라이브러리 루틴을 쓰지 않고 하는 방법을 찾아야 해요. 더 나은 것은 로케일 설정을 써도 괜찮다고 스스로 확신하는 거예요. 모듈이 non-C 로케일 설정과 호환되지 않는다고 문서화하는 건 최후의 수단으로만 해야 해요.

로케일에 따라 숫자 연산을 수행하는 유일한 방법은 이 모듈이 정의한 특수 함수 atof(), atoi(), format_string(), str()을 사용하는 거예요.

로케일에 따라 대소문자 변환이나 문자 분류를 수행할 방법은 없어요. (유니코드) 텍스트 문자열에 대해서는 문자 값에 따라서만 수행되고, 바이트 문자열에 대해서는 바이트의 ASCII 값에 따라 변환과 분류가 수행되며, 상위 비트가 설정된 바이트(즉 비-ASCII 바이트)는 절대 변환되지 않거나 문자나 공백 같은 문자 클래스의 일부로 간주되지 않아요.

로케일 이름

로케일 이름의 형식은 플랫폼에 따라 다르고, 지원되는 로케일 집합은 시스템 구성에 따라 달라질 수 있어요.

Posix 플랫폼에서는 보통 다음 형식이에요 [1]:

  language ["_" territory] ["." charset] ["@" modifier]

여기서 language는 ISO 639의 두세 글자 언어 코드, territory는 ISO 3166의 두 글자 국가·지역 코드, charset은 로케일 인코딩, modifier는 스크립트 이름, 언어 하위 태그, 정렬 순서 식별자 또는 다른 로케일 수정자(예: "latin", "valencia", "stroke", "euro")예요.

Windows에서는 여러 형식이 지원돼요 [2] [3]. IETF BCP 47 태그의 부분집합:

  language ["-" script] ["-" territory] ["." charset]
  language ["-" script] "-" territory "-" modifier

여기서 languageterritory는 Posix와 같은 의미, script는 ISO 15924의 네 글자 스크립트 코드, modifier는 언어 하위 태그, 정렬 순서 식별자 또는 커스텀 수정자(예: "valencia", "stroke", "x-python")예요. 하이픈('-')과 밑줄('_') 구분자 모두 지원돼요. BCP 47 태그에는 UTF-8 인코딩만 허용돼요.

Windows는 다음 형식의 로케일 이름도 지원해요:

  language ["_" territory] ["." charset]

여기서 languageterritory는 "English", "United States" 같은 전체 이름이고, charset은 코드 페이지 번호(예: "1252") 또는 UTF-8이에요. 이 형식에서는 밑줄 구분자만 지원돼요.

"C" 로케일은 모든 플랫폼에서 지원돼요.

확장 작성자와 Python을 임베드하는 프로그램을 위해

확장 모듈은 현재 로케일이 무엇인지 알아내는 것 외에는 절대 setlocale()을 호출하면 안 돼요. 하지만 반환값은 복원하는 데만 이식 가능하게 쓸 수 있으므로, 그리 유용하지 않아요(로케일이 C인지 아닌지 알아내는 데는 쓸모가 있을지 몰라도).

Python 코드가 locale 모듈로 로케일을 바꾸면 이것도 임베딩 애플리케이션에 영향을 줘요. 임베딩 애플리케이션이 이걸 원치 않으면, 모든 일을 하는 _locale 확장 모듈을 config.c 파일의 내장 모듈 테이블에서 제거하고 _locale 모듈이 공유 라이브러리로 접근 가능하지 않게 해야 해요.

메시지 카탈로그 접근

locale.gettext(msg), locale.dgettext(domain, msg), locale.dcgettext(domain, msg, category), locale.textdomain(domain), locale.bindtextdomain(domain, dir), locale.bind_textdomain_codeset(domain, codeset)

locale 모듈은 이 인터페이스를 제공하는 시스템에서 C 라이브러리의 gettext 인터페이스를 노출해요. gettext(), dgettext(), dcgettext(), textdomain(), bindtextdomain(), bind_textdomain_codeset() 함수로 구성돼요. 이들은 gettext 모듈의 같은 이름 함수와 비슷하지만, C 라이브러리의 이진 형식을 메시지 카탈로그에 사용하고 C 라이브러리의 검색 알고리즘으로 메시지 카탈로그를 찾아요.

Python 애플리케이션은 보통 이 함수들을 호출할 필요를 느끼지 못하고, 대신 gettext를 사용해야 해요. 이 규칙의 알려진 예외는 내부적으로 C 함수 gettextdcgettext를 호출하는 추가 C 라이브러리와 링크하는 애플리케이션이에요. 이런 애플리케이션은 라이브러리가 메시지 카탈로그를 제대로 찾을 수 있도록 텍스트 도메인을 바인딩해야 할 수 있어요.

더 알아보기

  • gettext — 다국어 국제화 서비스.