본문 바로가기
Programming

locale에 관하여

by homecafe 2009. 10. 15.

로케일(Locale)에 관하여...
로케일(Locale)의 의미

세계 여러 나라들은 각자 다른 문화(언어, 날짜, 시간 등)을 갖고 있다. 프로그램의
국제화(Internationalization, 줄여서 i18n)는 사용자로 하여금 프로그램 수행시
로케일이란 것에 의해 입맛에 맞는 환경을 선택할 수 있도록 만든 것을 말한다.
예를 들어 어떤 프로그램의 메시지가 여러가지 언어로 주어져 있는 경우 이중에
어떤 언어의 것을 출력할 것인가를 사용자가 결정할 수 있는 것이다. 그것을
가능하게 해 주는 수단이 바로 로케일이다. 이것은 단순히 메시지 뿐만이 아니고
숫자표현법, 날짜 또는 시간표현법 등 여러가지에 사용될 수 있다. 그것 각각을
우리는 카테고리(category)라고 부른다. 카테고리에는 LC_COLLATE, LC_CTYPE,
LC_MESSAGES, LC_MONETARY, LC_NUMERIC, LC_TIME 가 있다.


로케일 설정방법

로케일을 지원하는 프로그램의 실행 방식을 선택하기 위해서는 환경 변수 설정을
이용한다. (카테고리 각각에 해당하는 환경변수는 카테고리 이름과 동일하다.)
로케일 환경 변수에 관한 정보는 locale이란 명령으로 간단히 얻을 수 있다.
% locale
LANG=ko_KR.eucKR
LC_CTYPE="ko_KR.eucKR"
LC_NUMERIC="ko_KR.eucKR"
LC_TIME="ko_KR.eucKR"
LC_COLLATE="ko_KR.eucKR"
LC_MONETARY="ko_KR.eucKR"
LC_MESSAGES="ko_KR.eucKR"
LC_ALL=
위에서 ko_KR.eucKR은 로케일 값(locale name)이다. 일반적인 로케일 값의 형식은
ll[_CC[.EEEE]][@dddd] 이다. ll은 언어(language)를 지정하는 소문자 두 글자 ISO
639 language code, CC는 지역(territory)를 지정하는 대문자 두 글자 ISO 3166
country code, EEEE는 코드셋(codeset)을 지정하는 문자셋(character set) 또는
인코딩(encoding), dddd는 방언 등의 변종을 구별하기 위한 것(modifier)이다. []로
표시된 내용은 안 쓸수도 있음을 의미한다. 예를 들면 en_US는 미국 영어권, en_CA는
영어권 카나다, de_DE는 독일의 독일어, fr_FR는 프랑스의 프랑스어를 의미한다.
아무 로케일도 설정하지 않았을 때 glibc에서의 기본 로케일은 C 또는 POSIX
(glibc에서는 C 로케일의 alias) 로케일이다.
% locale -a
라는 명령을 이용하면 이외에 사용 가능한 로케일의 이름들을 알 수 있다.
다음은 여러가지 환경변수의 역할(카테고리의 경우에는 동시에 카테고리의 역할)에
관한 설명이다.

  LANG : 모든 카테고리에 대한 로케일 설정을 위한 환경변수이다. 하지만 LC_*
환경변수보다 우선 순위가 낮다. LC_ALL이 설정이 안 되어 있고 LC_* 값들이 설정이
따로 설정이 않된 경우 LANG을 변화시키면 LC_ALL을 제외한 로케일 카테고리들의
값이 변경되지만 LC_ALL이 설정 되어 있는 경우 LANG의 변화는 로케일 카테고리들의
값에 영향을 주지 않는다.
  LC_CTYPE : 문자 분류(알파벳, 숫자, 한글 또는 소문자, 대문자 등등), 변환,
대소문자 비교을 위한 로케일 설정을 의미한다. 이것은 예를 들어 fgetwc(), is*(),
isw*(), mblen(), mbtowc(), wcstombs() 등의 함수에 영향을 줄 수 있다.
  LC_COLLATE : 스트링(string)의 정렬 순서(sort order 또는 collation)를 위한
로케일 설정을 위해 사용된다. 이것은 예를 들어 strcoll(), wcscoll(), strxfrm()
등의 함수에 영향을 줄 수 있다.
  LC_MESSAGES : 메시지 표현을 위한 로케일 설정. 메시지의 국제화를 위한
catopen(), gettext() 등의 함수에 영향을 줄 수 있다.
  LC_NUMERIC : 금액이 아닌 숫자 표현(천단위, 소수점, 숫자 그룹핑 등)을 위한
로케일 설정. 예를 들어 strtod(), atof().
  LC_MONETARY : 금액 표현(천단위 구분 문자, 소수점 문자, 금액 표시 문자, 그
위치 등)을 위한 로케일 설정. 예를 들어 strfmon().
  LC_TIME : 시간과 날짜의 표현(년, 월, 일에 대한 명칭 등)을 위한 로케일 설정
예를 들어 strftime(), strptime().
  LC_ALL : 모든 카테고리에 대한 로케일 설정을 위한 환경변수이다. 위의 LC_* 및
LANG의 어떤 것보다 우선 순위가 높다. 그리고 LC_ALL을 설정하면 다른 로케일
카테고리의 값들이 LC_ALL의 값의 변경되고 LC_ALL설정을 없애면 다른 로케일
카테고리의 값들은 이전값을 유지한다.
  LANGUAGE : 로케일의 다중 설정을 위해 gettext에서 사용되는 GNU extension
환경변수로 LC_ALL보다도 우선순위가 높다. 로케일들은 구분문자 : 을 이용하여
우선순위가 높은 순대로 나열된다. 예를 들어 LANGUAGE=en_US:ko_KR
  LINGUAS : gettext를 사용하는 프로그램 설치시 지정한 언어들의 메시지만을
설치하기 위한 환경변수. 구분 문자는 스페이스이다. 예를 들어 LINGUAS="ko ja"

로케일을 지원하기 위한 방법 및 작동 원리

로케일을 제대로 지원하는 프로그램을 작성하기 위해서는 setlocale()함수를
이용하여 로케일을 설정하고 확인하여야 한다. setlocale()함수는 헤더 파일
locale.h 에 정의되어 있으며 그 프로토타입은 다음과 같다.
char *setlocale (int category, const char *locale);
이 함수의 역할은 카테고리 category에 대해 로케일 locale을 설정하고 (물론, 사용
가능한 로케일인 경우), 설정된 로케일값을 리턴하는 것이다.
locale 부분에 ""을 넣은 다음과 같은 예는
setlocale (LC_ALL, "");
적당한 환경변수를 참조하여 로케일을 설정하고 그 값을 리턴한다. 환경변수를
참조하는 우선순위는 위에서 설명한대로 LC_ALL, 그외 카테고리, LANG변수 순이고,
변수값을 알아내면 locale.alias(예를 들어, /usr/share/locale/)를 참조한 후
뒤에서부터 @ . _ 을 단위로 순서대로 잘라가며 사용가능한 로케일을 찾아낸다.
인수 locale 부분에 NULL을 넣은 다음예는
locale = setlocale (LC_ALL, NULL);
로케일 값을 변경시키지 않고 단지 카테고리에 관한 현재 로케일값을 알아내고자
하기 위해 그 리턴값을 사용하는 예이다.
다음은 함수 setlocale()을 사용하여 임시로 로케일로 변경하는 예이다. (glibc
manual에 있는 예)

     #include <stddef.h>
     #include <locale.h>
     #include <stdlib.h>
     #include <string.h>
    
     void
     with_other_locale (char *new_locale,
                        void (*subroutine) (int),
                        int argument)
     {
       char *old_locale, *saved_locale;
    
       /* 현재 로케일명을 알아낸다. */
       old_locale = setlocale (LC_ALL, NULL);
    
       /* setlocale()의 재호출 의해 변경될 것을 대비해 로케일 이름을 미리 복사해 둔다. */
       saved_locale = strdup (old_locale);
       if (saved_locale == NULL)
         fatal ("Out of memory");
    
       /* 로케일을 변경하고 subroutine을 수행한다. */
       setlocale (LC_ALL, new_locale);
       (*subroutine) (argument);
    
       /* 원래의 로케일로 복귀한다. */
       setlocale (LC_ALL, saved_locale);
       free (saved_locale);
     }

다음은 로케일을 이용하여 날짜/시간을 출력하는 프로그램의 예이다.
        #include <stdio.h>
        #include <locale.h>
        #include <time.h>

        int main(void)
        {
                time_t now;
                struct tm *l_time;
                char string[256];

                /* 환경변수로부터 시간 관련 로케일을 결정한다. */
                setlocale(LC_TIME, "");

                /* calendar 시간을 now라는 변수에 저장.
                 * 그 값은 1970년 1월 1일 이후부터 지금까지의 초단위 시간 */
                now = time((time_t *)NULL);

                /* calendar 시간의 다른 유용한 형태를 l_time변수에 저장 */
                l_time = localtime(&now);

                /* 우리가 원하는 포맷의 시간 포맷을 string이라는 변수에 저장 */
                strftime(string, sizeof string, "%c", l_time);

                /* 출력 */
                 printf("%s\n", string);

                return 0;
        }
다음은 위의 파일을 time_test.c라고 저장하고 컴파일하여 c쉘에서 시험하는
예이다.
% gcc -o time_test time_test.c
% setenv LANG ko_KR.eucKR
% ./time_test
2000년 01월 12일 수요일 오후 10시 26분 56초
% setenv LANG C
% ./time_test
Wed Jan 12 22:27:22 2000