gettext — 다국어 국제화 서비스

소스 코드: Lib/gettext.py


gettext 모듈은 파이썬 모듈과 응용 프로그램을 위한 국제화(I18N)와 현지화(L10N) 서비스를 제공합니다. GNU gettext 메시지 카탈로그 API와 파이썬 파일에 더 적합한 고수준 클래스 기반 API를 모두 지원합니다. 아래 설명된 인터페이스를 사용하면 모듈과 응용 프로그램 메시지를 하나의 자연어로 작성하고, 다른 자연어로 실행하기 위해 번역된 메시지 카탈로그를 제공할 수 있습니다.

파이썬 모듈과 응용 프로그램을 현지화하는 데 대한 힌트도 제공됩니다.

GNU gettext API

gettext 모듈은 GNU gettext API와 매우 유사한 다음 API를 정의합니다. 이 API를 사용하면 전체 응용 프로그램의 번역에 전역적으로 영향을 미칩니다. 응용 프로그램이 단일 언어라면 사용자의 로케일에 따라 언어를 선택할 수 있는 것과 함께 종종 이것이 여러분이 원하는 것입니다. 파이썬 모듈을 현지화하거나, 응용 프로그램에서 언어를 실행 중에 전환해야 한다면, 아마도 클래스 기반 API를 대신 사용하고 싶을 것입니다.

gettext.bindtextdomain(domain, localedir=None)

domain을 로케일 디렉터리 localedir에 바인드합니다. 보다 구체적으로, gettext는 경로 (유닉스에서) localedir/language/LC_MESSAGES/domain.mo를 사용하여 지정된 도메인(domain)에 대한 바이너리 .mo 파일을 찾습니다. 여기서 language는 환경 변수 LANGUAGE, LC_ALL, LC_MESSAGESLANG에서 각각 검색됩니다.

localedir이 생략되거나 None이면, domain에 대한 현재 바인딩이 반환됩니다. [1]

gettext.textdomain(domain=None)

현재 전역 도메인을 변경하거나 조회합니다. domainNone이면, 현재 전역 도메인이 반환되고, 그렇지 않으면 전역 도메인이 domain으로 설정되어 반환됩니다.

gettext.gettext(message)

Return the localized translation of message, based on the current global domain, language, and locale directory. This function is usually aliased as _() in the local namespace (see examples below).

gettext.dgettext(domain, message)

gettext()와 비슷하지만, 지정된 domain에서 메시지를 찾습니다.

gettext.ngettext(singular, plural, n)

gettext()와 비슷하지만, 복수형(plural forms)을 고려합니다. 번역이 발견되면, 복수 공식을 n에 적용하고, 결과 메시지를 반환합니다 (일부 언어는 복수형이 두 개 이상입니다). 번역이 없으면, n이 1이면 singular를 반환합니다; 그렇지 않으면 plural을 반환합니다.

복수 공식은 카탈로그 헤더에서 취합니다. 자유 변수 n을 갖는 C나 파이썬 표현식입니다. 이 표현식은 카탈로그에서 복수의 인덱스로 평가됩니다. .po 파일에 사용되는 정확한 문법과 다양한 언어의 공식은 GNU gettext 설명서를 참조하십시오.

gettext.dngettext(domain, singular, plural, n)

ngettext()와 비슷하지만, 지정된 domain에서 메시지를 찾습니다.

gettext.pgettext(context, message)
gettext.dpgettext(domain, context, message)
gettext.npgettext(context, singular, plural, n)
gettext.dnpgettext(domain, context, singular, plural, n)

접두사에 p가 없는 해당 함수(즉, gettext(), dgettext(), ngettext(), dngettext())와 유사하지만, 번역은 지정된 메시지 context로 제한됩니다.

버전 3.8에 추가.

Note that GNU gettext also defines a dcgettext() method, but this was deemed not useful and so it is currently unimplemented.

이 API의 일반적인 사용 예는 다음과 같습니다:

import gettext
gettext.bindtextdomain('myapplication', '/path/to/my/language/directory')
gettext.textdomain('myapplication')
_ = gettext.gettext
# ...
print(_('This is a translatable string.'))

클래스 기반 API

The class-based API of the gettext module gives you more flexibility and greater convenience than the GNU gettext API. It is the recommended way of localizing your Python applications and modules. gettext defines a GNUTranslations class which implements the parsing of GNU .mo format files, and has methods for returning strings. Instances of this class can also install themselves in the built-in namespace as the function _().

gettext.find(domain, localedir=None, languages=None, all=False)

이 함수는 표준 .mo 파일 검색 알고리즘을 구현합니다. textdomain()이 취하는 것과 동일한 domain을 취합니다. 선택적 localedirbindtextdomain()에서와 같습니다. 선택적 languages는 문자열 리스트이며, 각 문자열은 언어 코드입니다.

localedir이 제공되지 않으면, 기본 시스템 로케일 디렉터리가 사용됩니다. [2] languages가 제공되지 않으면, 다음과 같은 환경 변수가 검색됩니다: LANGUAGE, LC_ALL, LC_MESSAGESLANG. 비어 있지 않은 값을 반환하는 첫 번째 것이 languages 변수에 사용됩니다. 환경 변수는 콜론으로 구분된 언어 목록을 포함해야 하며, 콜론에서 분할되어 예상되는 언어 코드 문자열 리스트를 생성합니다.

그런 다음 find()는 언어를 확장하고 정규화한 다음, 다음 구성 요소로 구성된 기존 파일을 검색하면서, 이들을 이터레이트 합니다:

localedir/language/LC_MESSAGES/domain.mo

존재하는 첫 번째 파일 이름이 find()에 의해 반환됩니다. 그러한 파일이 없으면, None이 반환됩니다. all이 제공되면, 언어 리스트나 환경 변수에 나타나는 순서대로 모든 파일 이름의 리스트를 반환합니다.

gettext.translation(domain, localedir=None, languages=None, class_=None, fallback=False)

Return a *Translations instance based on the domain, localedir, and languages, which are first passed to find() to get a list of the associated .mo file paths. Instances with identical .mo file names are cached. The actual class instantiated is class_ if provided, otherwise GNUTranslations. The class’s constructor must take a single file object argument.

여러 파일이 발견되면, 이후 파일은 이전 파일에 대한 폴 백으로 사용됩니다. 폴 백을 설정하는 것을 허락하기 위해, copy.copy()를 사용하여 캐시에서 각 번역 객체를 복제합니다; 실제 인스턴스 데이터는 여전히 캐시와 공유됩니다.

.mo 파일이 없으면, 이 함수는 fallback이 거짓(기본값)이면 OSError를 발생시키고, fallback이 참이면 NullTranslations 인스턴스를 반환합니다.

버전 3.3에서 변경: IOError used to be raised, it is now an alias of OSError.

버전 3.11에서 변경: codeset parameter is removed.

gettext.install(domain, localedir=None, *, names=None)

This installs the function _() in Python’s builtins namespace, based on domain and localedir which are passed to the function translation().

names 매개 변수에 대해서는, 번역 객체의 install() 메서드에 대한 설명을 참조하십시오.

As seen below, you usually mark the strings in your application that are candidates for translation, by wrapping them in a call to the _() function, like this:

print(_('This string will be translated.'))

For convenience, you want the _() function to be installed in Python’s builtins namespace, so it is easily accessible in all modules of your application.

버전 3.11에서 변경: names is now a keyword-only parameter.

NullTranslations 클래스

번역 클래스는 원본 소스 파일 메시지 문자열을 번역된 메시지 문자열로 실제로 구현합니다. 모든 번역 클래스에서 사용하는 베이스 클래스는 NullTranslations입니다; 여러분 자신의 특수화된 번역 클래스를 작성하는 데 사용할 수 있는 기본 인터페이스를 제공합니다. NullTranslations의 메서드는 다음과 같습니다:

class gettext.NullTranslations(fp=None)

베이스 클래스에서 무시되는, 선택적인 파일 객체 fp를 취합니다. 파생 클래스에 의해 설정되는 “보호되는” 인스턴스 변수 _info_charset 뿐만 아니라 add_fallback()을 통해 설정되는 _fallback을 초기화합니다. 그런 다음 fpNone이 아니면 self._parse(fp)를 호출합니다.

_parse(fp)

베이스 클래스에서 아무런 일도 하지 않는 이 메서드는 파일 객체 fp를 취하고, 이 파일에서 데이터를 읽고, 메시지 카탈로그를 초기화합니다. 지원되지 않는 메시지 카탈로그 파일 형식이 있으면, 이 메서드를 재정의하여 형식을 구문 분석해야 합니다.

add_fallback(fallback)

현재 번역 객체의 폴 백 객체로 fallback을 추가합니다. 주어진 메시지에 대한 번역을 제공할 수 없으면 번역 개체는 폴 백을 참조해야 합니다.

gettext(message)

폴 백이 설정되었으면, gettext()를 폴 백으로 전달합니다. 그렇지 않으면, message를 반환합니다. 파생 클래스에서 재정의됩니다.

ngettext(singular, plural, n)

폴 백이 설정되었으면, ngettext()를 폴 백으로 전달합니다. 그렇지 않으면, n이 1이면 singular를 반환합니다; 그렇지 않으면 plural을 반환합니다. 파생 클래스에서 재정의됩니다.

pgettext(context, message)

폴 백이 설정되었으면, pgettext()를 폴 백으로 전달합니다. 그렇지 않으면, 번역된 메시지를 반환합니다. 파생 클래스에서 재정의됩니다.

버전 3.8에 추가.

npgettext(context, singular, plural, n)

폴 백이 설정되었으면, npgettext()를 폴 백으로 전달합니다. 그렇지 않으면, 번역된 메시지를 반환합니다. 파생 클래스에서 재정의됩니다.

버전 3.8에 추가.

info()

Return a dictionary containing the metadata found in the message catalog file.

charset()

메시지 카탈로그 파일의 인코딩을 반환합니다.

install(names=None)

이 메서드는 gettext()를 내장 이름 공간에 설치하여, _에 연결합니다.

If the names parameter is given, it must be a sequence containing the names of functions you want to install in the builtins namespace in addition to _(). Supported names are 'gettext', 'ngettext', 'pgettext', and 'npgettext'.

Note that this is only one way, albeit the most convenient way, to make the _() function available to your application. Because it affects the entire application globally, and specifically the built-in namespace, localized modules should never install _(). Instead, they should use this code to make _() available to their module:

import gettext
t = gettext.translation('mymodule', ...)
_ = t.gettext

This puts _() only in the module’s global namespace and so only affects calls within this module.

버전 3.8에서 변경: 'pgettext''npgettext'를 추가했습니다.

GNUTranslations 클래스

The gettext module provides one additional class derived from NullTranslations: GNUTranslations. This class overrides _parse() to enable reading GNU gettext format .mo files in both big-endian and little-endian format.

GNUTranslations parses optional metadata out of the translation catalog. It is convention with GNU gettext to include metadata as the translation for the empty string. This metadata is in RFC 822-style key: value pairs, and should contain the Project-Id-Version key. If the key Content-Type is found, then the charset property is used to initialize the “protected” _charset instance variable, defaulting to None if not found. If the charset encoding is specified, then all message ids and message strings read from the catalog are converted to Unicode using this encoding, else ASCII is assumed.

Since message ids are read as Unicode strings too, all *gettext() methods will assume message ids as Unicode strings, not byte strings.

The entire set of key/value pairs are placed into a dictionary and set as the “protected” _info instance variable.

.mo 파일의 매직 번호가 유효하지 않거나, 주 버전 번호가 예상치 못한 값이거나, 파일을 읽는 동안 다른 문제가 발생하면 GNUTranslations 클래스를 인스턴스 화할 때 OSError가 발생할 수 있습니다.

class gettext.GNUTranslations

베이스 클래스 구현에서 다음 메서드가 재정의되었습니다:

gettext(message)

카탈로그에서 message id를 찾아 해당 메시지 문자열을 유니코드 문자열로 반환합니다. 카탈로그에 message id에 대한 항목이 없고, 폴 백이 설정되었으면, 조회는 폴 백의 gettext() 메서드로 전달됩니다. 그렇지 않으면, message id가 반환됩니다.

ngettext(singular, plural, n)

메시지 id의 복수형 조회를 수행합니다. singular는 카탈로그에서 찾기 위해 메시지 id로 사용되는 반면, n은 사용할 복수형을 결정하는 데 사용됩니다. 반환된 메시지 문자열은 유니코드 문자열입니다.

카탈로그에서 메시지 id를 찾을 수 없고, 폴 백이 지정되었으면, 요청은 폴 백의 ngettext() 메서드로 전달됩니다. 그렇지 않으면, n이 1이면 singular가 반환되고, 다른 모든 경우에는 plural이 반환됩니다.

예를 들면 다음과 같습니다:

n = len(os.listdir('.'))
cat = GNUTranslations(somefile)
message = cat.ngettext(
    'There is %(num)d file in this directory',
    'There are %(num)d files in this directory',
    n) % {'num': n}
pgettext(context, message)

카탈로그에서 contextmessage id를 찾아 해당 메시지 문자열을 유니코드 문자열로 반환합니다. 카탈로그에 message id와 context에 대한 항목이 없고, 폴 백이 설정되었으면, 조회는 폴 백의 pgettext() 메서드로 전달됩니다. 그렇지 않으면, message id가 반환됩니다.

버전 3.8에 추가.

npgettext(context, singular, plural, n)

메시지 ID의 복수형 조회를 수행합니다. singular는 카탈로그에서 찾기 위해 메시지 id로 사용되는 반면, n은 사용할 복수형을 결정하는 데 사용됩니다.

context의 메시지 id가 카탈로그에 없고, 폴 백이 지정되었으면, 요청은 폴 백의 npgettext() 메서드로 전달됩니다. 그렇지 않으면, n이 1이면 singular가 반환되고, 다른 모든 경우에는 plural이 반환됩니다.

버전 3.8에 추가.

Solaris 메시지 카탈로그 지원

Solaris 운영 체제는 자체 바이너리 .mo 파일 형식을 정의하지만, 이 형식에 대한 설명서를 찾을 수 없어서, 현재 지원되지 않습니다.

Catalog 생성자

GNOME은 James Henstridge의 gettext 모듈 버전을 사용하지만, 이 버전은 API가 약간 다릅니다. 설명된 사용법은 다음과 같습니다:

import gettext
cat = gettext.Catalog(domain, localedir)
_ = cat.gettext
print(_('hello world'))

For compatibility with this older module, the function Catalog() is an alias for the translation() function described above.

이 모듈과 Henstridge 버전의 한 가지 차이점: 그의 카탈로그 객체는 매핑 API를 통한 액세스를 지원했지만, 사용되지 않는 것으로 보여서 현재 지원되지 않습니다.

프로그램과 모듈의 국제화

국제화(I18N)는 프로그램이 여러 언어를 인식하도록 하는 작업을 말합니다. 현지화(L10N)는 일단 국제화된 프로그램이 현지 언어와 문화적 습관에 적응하는 것을 말합니다. 파이썬 프로그램에 다국어 메시지를 제공하려면, 다음 단계를 수행해야 합니다:

  1. 번역 가능한 문자열을 특별히 표시하여 프로그램이나 모듈을 준비합니다

  2. 표시된 파일에 대해 도구 모음을 실행하여 원시 메시지 카탈로그를 생성합니다

  3. 메시지 카탈로그의 언어별 번역을 만듭니다

  4. 메시지 문자열이 올바르게 번역되도록 gettext 모듈을 사용합니다

In order to prepare your code for I18N, you need to look at all the strings in your files. Any string that needs to be translated should be marked by wrapping it in _('...') — that is, a call to the function _. For example:

filename = 'mylog.txt'
message = _('writing a log message')
with open(filename, 'w') as fp:
    fp.write(message)

이 예에서, 문자열 'writing a log message'는 번역 후보로 표시되지만, 문자열 'mylog.txt''w'는 그렇지 않습니다.

There are a few tools to extract the strings meant for translation. The original GNU gettext only supported C or C++ source code but its extended version xgettext scans code written in a number of languages, including Python, to find strings marked as translatable. Babel is a Python internationalization library that includes a pybabel script to extract and compile message catalogs. François Pinard’s program called xpot does a similar job and is available as part of his po-utils package.

(파이썬에는 pygettext.pymsgfmt.py라고 하는 이러한 프로그램의 순수 파이썬 버전도 포함되어 있습니다; 일부 파이썬 배포판은 이 프로그램들을 설치합니다. pygettext.pyxgettext와 유사하지만, 파이썬 소스 코드만 이해하며 C나 C++ 와 같은 다른 프로그래밍 언어를 처리할 수 없습니다. pygettext.pyxgettext와 유사한 명령 줄 인터페이스를 지원합니다; 사용에 대한 자세한 내용을 보려면, pygettext.py --help를 실행하십시오. msgfmt.py는 GNU msgfmt와 바이너리 호환됩니다. 이 두 프로그램을 사용하면, 파이썬 응용 프로그램을 국제화하기 위해 GNU gettext 패키지가 필요하지 않을 수 있습니다.)

xgettext, pygettext 및 유사한 도구는 메시지 카탈로그인 .po 파일을 생성합니다. 이 파일은 소스 코드에 표시된 모든 문자열과 이러한 문자열의 번역된 버전에 대한 자리를 포함하는 사람이 읽을 수 있는 파일입니다.

.po 파일의 사본은 지원되는 모든 자연어에 대한 번역을 작성하는 개별 인간 번역가에게 전달됩니다. 완성된 언어별 버전을 <language-name>.po 파일로 다시 보내고, 이는 msgfmt 프로그램을 사용하여 기계가 읽을 수 있는 .mo 바이너리 카탈로그 파일로 컴파일됩니다. .mo 파일은 실행 시간에 실제 번역 처리를 위해 gettext 모듈에서 사용됩니다.

코드에서 gettext 모듈을 사용하는 방법은 단일 모듈을 국제화하는지 또는 전체 응용 프로그램을 국제화하는지에 따라 다릅니다. 다음 두 섹션에서는 각 사례에 관해 설명합니다.

모듈 현지화

모듈을 현지화한다면, 전역적인 변경을 가하지 않도록 주의해야 합니다, 예를 들어, 내장 이름 공간. GNU gettext API 대신 클래스 기반 API를 사용해야 합니다.

모듈이 “spam”이고 모듈의 다양한 자연어 번역 .mo 파일이 /usr/share/locale에 GNU gettext 형식으로 존재한다고 가정해 봅시다. 다음은 모듈 맨 위에 들어갈 내용입니다:

import gettext
t = gettext.translation('spam', '/usr/share/locale')
_ = t.gettext

응용 프로그램 현지화

If you are localizing your application, you can install the _() function globally into the built-in namespace, usually in the main driver file of your application. This will let all your application-specific files just use _('...') without having to explicitly install it in each file.

간단한 경우에는, 응용 프로그램의 메인 드라이버 파일에 다음 코드만 추가하면 됩니다:

import gettext
gettext.install('myapplication')

로케일 디렉터리를 설정해야 하면, install() 함수로 전달할 수 있습니다:

import gettext
gettext.install('myapplication', '/usr/share/locale')

실행 중 언어 변경

프로그램에서 동시에 여러 언어를 지원해야 하면, 다음과 같은 식으로 여러 번역 인스턴스를 만든 다음 명시적으로 전환할 수 있습니다:

import gettext

lang1 = gettext.translation('myapplication', languages=['en'])
lang2 = gettext.translation('myapplication', languages=['fr'])
lang3 = gettext.translation('myapplication', languages=['de'])

# start by using language1
lang1.install()

# ... time goes by, user selects language 2
lang2.install()

# ... more time goes by, user selects language 3
lang3.install()

지연된 번역

대부분의 코딩 상황에서, 문자열은 코딩된 위치에서 번역됩니다. 그러나 때때로, 번역을 위해 문자열을 표시하지만, 실제 번역을 뒤로 연기할 필요가 있습니다. 전형적인 예는 다음과 같습니다:

animals = ['mollusk',
           'albatross',
           'rat',
           'penguin',
           'python', ]
# ...
for a in animals:
    print(a)

여기서, animals 리스트의 문자열을 번역 가능한 것으로 표시하려고 하지만, 실제로 인쇄될 때까지 번역하고 싶지는 않습니다.

이 상황을 처리 할 수 있는 한 가지 방법은 다음과 같습니다:

def _(message): return message

animals = [_('mollusk'),
           _('albatross'),
           _('rat'),
           _('penguin'),
           _('python'), ]

del _

# ...
for a in animals:
    print(_(a))

This works because the dummy definition of _() simply returns the string unchanged. And this dummy definition will temporarily override any definition of _() in the built-in namespace (until the del command). Take care, though if you have a previous definition of _() in the local namespace.

Note that the second use of _() will not identify “a” as being translatable to the gettext program, because the parameter is not a string literal.

이를 처리하는 다른 방법은 다음 예제를 사용하는 것입니다:

def N_(message): return message

animals = [N_('mollusk'),
           N_('albatross'),
           N_('rat'),
           N_('penguin'),
           N_('python'), ]

# ...
for a in animals:
    print(_(a))

In this case, you are marking translatable strings with the function N_(), which won’t conflict with any definition of _(). However, you will need to teach your message extraction program to look for translatable strings marked with N_(). xgettext, pygettext, pybabel extract, and xpot all support this through the use of the -k command-line switch. The choice of N_() here is totally arbitrary; it could have just as easily been MarkThisStringForTranslation().

감사의 말

다음 분들은 이 모듈을 만드는 데 코드, 피드백, 디자인 제안, 이전 구현 및 귀중한 경험을 제공했습니다:

  • Peter Funk

  • James Henstridge

  • Juan David Ibáñez Palomar

  • Marc-André Lemburg

  • Martin von Löwis

  • François Pinard

  • Barry Warsaw

  • Gustavo Niemeyer

각주