GnuSmalltalkUsersGuide:AdditionalChapter5 14
- I18N.LcMessagesMoFileVersion0
I18N.LcMessagesMoFileVersion0
네임스페이스 I18N에 정의됨
슈퍼클래스: I18N.LcMessagesCatalog
범주: i18n-메시지
- 이 객체는 GNU gettext MO 파일로부터 해석된 문자열을 검색하는 구체적 클래스이다. 클래스 메서드 #fileFormatDescription은 파일 포맷에 대한 설명을 포함한다.
I18N.LcMessagesMoFileVersion0 클래스: 문서
fileFormatDescription
GNU MO 파일의 Format (GNU gettext 지침서에서 발췌)
생성된 MO 파일의 포맷은 아래 그림으로 가장 잘 설명된다.
첫 두 단어는 파일의 식별 역할을 한다. 매직 넘버(magic number)는 항상 GNU MO 파일을 시그널링할 것이다. 숫자는 생성하는 머신의 바이트 순서로 보관되므로 매직 넘버는 사실상 두 개의 숫자다: '0x950412de'와 '0xde120495'. 두 번째 단어는 파일 포맷의 최근 revision을 설명한다. 현재 revision은 0이다. 향후 버전에선 이것이 변경되어 MO 파일을 읽는 사용자가 새로운 포맷과 기존 포맷을 구별하여 두 가지를 올바르게 처리할 수 있을 것이다. 다른 포맷마다 다른 매직 넘버를 사용하는 대신 버전은 매직 넘버와 구별해 유지되는데, '/etc/magic'이 자주 업데이트되지 않는다는 것이 주 이유다. 매직 넘버를 내부 포맷 버전 식별과 구별하는 편이 나을지도 모른다.
파일 내 측면 테이블에 대한 다수의 포인터를 따르면 MO 파일을 읽는 프로그램을 재컴파일할 필요 없이 MO 파일의 접두사 부분의 확장자를 허용할 수 있다. 이는 사용된 charset, 새 데이블, 또는 다른 내용에 관해 표시하는 플래그 비트 몇 개를 추후에 삽입 시 유용해질 수 있다.
이후 그림의 오프셋 O와 오프셋 T에서 문자열 기술자의 테이블 두 개가 발견된다. 두 테이블에는 각 문자열 기술자가 두 개의 32비트 정수를 이용하는데, 하나는 문자열 길이를 위한 것이고 나머지 하나는 MO 파일 내 문자열의 오프셋을 위한 것으로, 파일의 시작부터 시작해 바이트로 계수한다. 첫 번째 테이블은 원본 문자열을 위한 기술자를 포함하고, 원본 문자열이 사전식 순서(increasing lexicographical order)로 되어 있다. 두 번째 테이블은 해석된 문자열에 대한 기술자를 포함하며, 첫 번째 테이블과 마찬가지다: 해당하는 해석을 찾기 위해서는 같은 색인으로 된 두 번째 배열 내 배열 슬롯으로 접근해야 한다.
원본 문자열을 정렬하면 MO 파일이 해싱 테이블을 포함하지 않을 때나 MO 파일에 제공된 해싱 테이블을 사용하기가 실용적이지 않을 때 단순 2진 검색을 사용할 수 있게 된다. 뿐만 아니라 PO 파일 GNU 'gettext'가 주로 그 특정 MO 파일에 부착된 일부 시스템 정보로 *해석*되며, 빈 문자열은 원본 테이블에서든 해석된 테이블에서든 꼭 첫 번째가 되어 시스템 정보가 쉽게 검색된다는 장점도 있다.
해시 테이블의 S 크기는 0이 되기도 한다. 이런 경우 해시 테이블 자체는 MO 파일에 포함되지 않는다. 어떤 사람들은 이 방법을 선호하기도 하는데, 미리 계산된(precomputed) 해싱 테이블의 경우 디스크 공간을 차지하여 *그다지* 빠른 속도를 성취하지 못하기 때문이다. 해시 테이블은 MO 파일 내 문자열의 정렬된 배열에 대한 색인을 포함한다. 이 때는 이중 해싱(double hashing)으로 갈등을 해소할 수 있다. 사용되는 정밀한 해싱 알고리즘은 GNU 'gettext' 코드에 꽤 의존해 사용되지만 본문에서는 다루지 않겠다.
문자열 자체와 관련해 언급하자면, 문자열은 해시 파일을 따르고, 각각은 <NUL>로 종료되며, <NUL>은 문자열 기술자에 표시되는 길이에 포함되지 않는다. 'msgfmt' 프로그램은 MO 파일 문자열에 대한 정렬(alignment)을 선택하는 옵션을 포함한다. 이 옵션을 이용하면 각 문자열이 따로 정렬되어, 정렬값을 곱한 값에 해당하는 오프셋에서 시작한다. 일부 RISC 머신에서는 올바른 정렬 시 속도를 증가시킬 것이다.
MO 파일이 문자열에 내장된 <NUL>을 갖지 못하도록 막을 수 있는 방법은 없다. 하지만 최근 사용된 프로그램 인터페이스는 문자열이 이미 <NUL> 종료되었을 것으로 추정하므로 내장된 <NUL>은 다소 무용지물이다. 하지만 MO 파일 포맷은 충분히 일반적이어서 추후에 다른 인터페이스가 가능한데, 가령 <NUL> 바이트가 우연히 나타날 수 있는 MO 파일에 wide character를 올바로 구현하고 싶은 때를 예로 들 수 있겠다.
이 특정 문제는 GNU 'gettext' 개발 포럼에서 뜨거운 논쟁의 대상이 되어왔는데, MO 파일 포맷이 시간이 지나면서 진화하거나 변경될 것으로 기대된다. 추후에는 어쩌면 많은 포맷을 동시에 지원하는 것도 가능할지 모른다. 하지만 분명한 건 어딘가에선 시작을 해야 하고, 본문에 설명된 MO 파일이 훌륭한 시작점이 된다는 사실이다. 어떤 것도 확실하지는 않고, 포맷은 향후에 비교적 쉽게 발전할 수도 있으므로 현재 접근법에 편해져야 한다.
byte +--------------+ 0 | 매직 넘버 = 0x950412de | | |
4 | 파일 포맷 수정 = 0 | | | 8 | 문자열 수 | == N | | 12 | 원본 문자열이 있는 테이블의 오프셋 | == O | | 16 | 해석 문자열이 있는 테이블의 오프셋 | == T | | 20 | 해싱 테이블의 크기 | == S | | 24 | 해싱 테이블의 오프셋 | == H | | . . . (추후 더 많은 엔트리 가능) . . . | | O | 길이 & 0번째 문자열의 오프셋------. O + 8 | 길이 & 1 번째 문자열의 오프셋------.
... ... | | O + ((N-1)*8)| 길이 & offset (N-1)번째 문자열 | | | | | | | T | 길이 & 0번째 해석 오프셋-----. T + 8 | 길이 & 1 번째 해석 오프셋 ------. ... ... | | | | T + ((N-1)*8)| 길이 & (N-1) 번째 해석 오프셋 | | | | | | | | | | | H | 해시 테이블 시작하기 | | | | | ... ... | | | | H + S * 4 | 해시 테이블 끝내기 | | | | | | | | | | | | NUL 종료된 0번째 문자열 <------' | | | | | | | | | NUL 종료된 1번째 문자열 <------' | | | | | | ... ... | | | | | | | NUL 종료된 0번째 해석 <-----' | | | | | NUL 종료된 1번째 해석 <------' | | ... ... | | +--------------+
메시지 카탈로그 파일 위치시키기----------
다양한 패키지에 대한 다양한 언어를 보관해야 하기 때문에 파일 메시지 카탈로그 파일에 정보를 추가하는 방법이 필요하다. Unix 환경에서 주로 사용되는 방식은 파일명에 이러한 인코딩을 포함시키는 방법이다. 본문에서도 이 방법을 사용했다. 'bindtextdomain'의 두 번째 인자(또는 기본 디렉터리)에 주어진 디렉터리명 다음에 로케일(locale) 값과 이름, 그리고 도메인명이 따른다:
DIR_NAME/LOCALE/LC_CATEGORY/DOMAIN_NAME.mo
DIR_NAME에 대한 기본값은 시스템 특정적이다. GNU 라이브러리와 그 규약을 준수하는 패키지의 경우 /usr/local/share/locale이다.
LOCALE은 이름이 'LC_CATEGORY'인 로케일의 값이다. 'gettext'와 'dgettext'의 경우 로케일은 항상 'LC_MESSAGES'이다.
I18N.LcMessagesMoFileVersion0 클래스: 복수
initialize
대부분 공통 언어에서 복수를 계산하는 표현식으로 된 테이블을 초기화하라.
pluralExpressionFor: locale ifAbsent: aBlock
주어진 언어와 지역에 대한 복수형을 생성하는 RunTimeExpression이 존재하는 경우 이를 응답하고, 그 외의 경우 aBlock을 평가한 후 결과를 응답하라.
I18N.LcMessagesMoFileVersion0: 캐시 비우기
flush
캐시를 비우고 카탈로그의 메타데이터를 다시 읽어라.
shouldCache
파일로부터 해석을 읽어올 경우 항상 캐시 저장하므로 true를 응답하라.