펄 로케일 처리
펄 로케일 처리 (perllocale)
Perl의 로케일(locale) 처리, 즉 국제화와 지역화에 관한 문서예요. 어떻게 하면 한 프로그램이 세계 여러 곳의 사용자 선호에 맞춰 동작하도록 만들 수 있는지 다룬답니다.
출처: perllocale - Perl locale handling (internationalization and localization)
설명 (DESCRIPTION)
태초에 ASCII가 있었어요. "미국 정보 교환 표준 코드"죠. 영어 알파벳과 달러 표시 통화를 쓰는 미국인에게는 꽤 잘 맞아요 (센트 기호 ¢가 ASCII에 없으니 그걸 필요로 하지 않는 한 말이죠). 하지만 다른 통화(파운드 스털링 £ 같은, 그 기호도 ASCII에 없으니까요)를 쓰는 다른 영어 사용자에게조차 잘 맞지 않아요. 그리고 세계의 수천 언어 중 많은 것에는 절망적으로 부족해요.
이 결함들을 해결하기 위해 로케일의 개념이 발명됐어요 (공식적으로는 ISO C, XPG4, POSIX 1.c "로케일 시스템"). 이것은 사용자들이 자신의 선호에 더 맞게 컴퓨터와 상호작용하게 해줘요. 로케일 메커니즘을 사용하는 응용 프로그램이 작성되어 왔고 지금도 작성 중이에요. 그런 응용 프로그램이 이런 종류의 문제에서 사용자의 선호를 고려하도록 만드는 과정을 **국제화(internationalization)**라고 해요 (종종 i18n으로 줄여요); 그런 응용 프로그램에게 특정 선호 집합을 알려주는 것을 지역화(localization)(l10n)라고 해요.
Perl은 로케일 시스템에서 사용 가능한 특정 유형의 로케일을 지원하도록 확장되었어요. 이는 응용 프로그램마다 하나의 프라그마(pragma), 하나의 함수 호출, 여러 환경 변수를 사용해 제어돼요.
Perl은 ISO 8859 같은 ASCII의 상위집합인 단일 바이트 로케일과, 다음 문단에서 설명할 UTF-8 로케일인 다중 바이트 유형 로케일 하나를 지원해요. Perl은 동아시아 언어용 같은 다른 다중 바이트 로케일은 지원하지 않아요.
불행히도 로케일의 설계(그리고 종종 구현)에는 꽤 많은 결함이 있어요. 유니코드는(입문은 perlunitut 참고) 부분적으로 이러한 설계 결함을 해결하기 위해 발명됐고, 요즘에는 유니코드에 기반한 일련의 "UTF-8 로케일"이 있어요. 이것들은 문자셋이 UTF-8로 인코딩된 유니코드인 로케일이에요. v5.20부터 Perl은 정렬과 lt, ge 같은 문자열 비교를 제외하고 UTF-8 로케일을 완전히 지원해요. v5.26부터는 플랫폼의 구현에 따라 Perl이 이것들도 합리적으로 처리할 수 있어요. 하지만 더 오래된 릴리즈이거나 더 나은 제어를 원한다면 Unicode::Collate를 사용하세요.
실제로 약간 다른 두 유형의 UTF-8 로케일이 있어요: 투르크어족 언어용 하나와 그 외 전부용 하나. Perl v5.30부터 Perl은 그 동작으로 UTF-8 투르크어 로케일을 감지하고 두 유형을 매끄럽게 처리해요. 이전에는 비투르크어만 지원됐어요. 로케일 이름은 무시돼요. 시스템에 tr_TR.UTF-8 로케일이 있고 그것이 투르크어 로케일처럼 동작하지 않는다면, perl은 비투르크어 로케일처럼 취급할 거예요.
Perl은 옛 비UTF-8 로케일도 계속 지원해요. 현재 EBCDIC 플랫폼에는 UTF-8 로케일이 없어요.
perl 인터프리터는 C 언어 프로그램이에요. C 언어 명세에는 최소한 스텁(stub) 로케일 지원이 요구돼요. 따라서 어떤 perl 인스턴스든 자동으로 이것을 가져요. 나중에 POSIX 표준이 C가 요구하는 것보다 더 많은 기능을 추가했어요. Perl은 사용 가능한 플랫폼에서 이것들을 지원해요. 그리고 유니코드는 기본 로케일 시스템이 가진 것보다 더 많은 종류의 정보 데이터베이스를 지원해요. 이 데이터베이스를 CLDR, "Common Locale Data Repository"라고 해요, http://cldr.unicode.org/. 이 XML로 인코딩된 데이터에 접근을 제공하는 다양한 CPAN 모듈이 있어요. Locale::CLDR, CLDR::Number, DateTime::Format::CLDR 같은 것들이요.
로케일이란 무엇인가 (WHAT IS A LOCALE)
로케일은 세계의 다양한 공동체들이 자신의 세계를 어떻게 분류하는지의 다양한 측면을 묘사하는 데이터 집합이에요. 이 범주들은 다음 유형으로 나뉘어요 (일부는 여기에 간단한 메모가 있어요):
범주 LC_NUMERIC: 숫자 형식 (Numeric formatting)
인간이 읽기 쉽도록 숫자를 어떻게 형식화해야 하는지를 나타내요. 예를 들어 소수점으로 쓰는 문자 같은 것요.
범주 LC_MONETARY: 화폐 금액 형식 (Formatting of monetary amounts)
범주 LC_TIME: 날짜/시간 형식 (Date/Time formatting)
범주 LC_COLLATE: 조합/대조 (Collation)
비교와 정렬을 위한 문자의 순서를 나타내요. 라틴 알파벳에서 예를 들어 "b"는 일반적으로 "a"를 따른다.
범주 LC_CTYPE: 문자 유형 (Character Types)
예를 들어 문자가 대문자인지 여부를 나타내요.
범주 LC_MESSAGES: 오류 및 기타 메시지 (Error and other messages)
이것은 기본 C 언어 요구 범주를 넘어선 POSIX 확장이에요. Windows와 다른 비POSIX 플랫폼에서 perl은 그것을 시뮬레이션하기 위해 우회책을 사용해요.
범주 LC_TIME: (날짜/시간 형식, 위 참고)
범주 LC_ALL
실제 범주는 아니고, 모든 실제 범주를 지칭하는 편의상의 약칭이에요.
다른 범주 (Other categories)
일부 플랫폼은 측정 단위나 용지 크기 같은 것을 다루는 다른 범주가 있어요. Perl이 직접 사용하는 것은 없지만, Perl이 상호작용하는 외부 작업이 이것들을 쓸 수 있어요. 아래의 ""use locale"의 범위 밖"을 참고하세요.
Perl이 사용하는 범주에 대한 자세한 내용은 아래 "로케일 범주"에 있어요.
함께, 이 범주들은 단일 프로그램을 많은 다른 위치에서 실행되도록 사용자 정의하는 데 크게 기여해요. 그리고 유니코드 CLDR을 추가하면 더 나아가요. 하지만 결함이 있으니 계속 읽으세요.
로케일 사용 준비 (PREPARING TO USE LOCALES)
Perl 자체(POSIX 모듈 밖에서)는 명시적으로 요청하지 않는 한 로케일을 사용하지 않아요 (하지만 Perl이 그것을 사용하는 코드와 상호작용할 수 있음을 다시 주목하세요). 그런 요청이 있어도, 제대로 동작하기 위해 다음 모두가 참이어야 해요:
-
Perl이 로케일 시스템이 지원된다고 믿어야 함. 그렇다면
perl -V:d_setlocale이d_setlocale의 값이define이라고 말할 거예요. -
사용하는 로케일에 대한 정의가 설치되어야 함. perl이 실행되는 모든 플랫폼은 적어도 하나의 로케일, 이름 "C"를 지원해야 하는데, 이것은 본질적으로 ASCII이고 전형적인 미국 선호예요.
대부분의 플랫폼은 추가 로케일을 허용하지만, 그것들은 구체적으로 설치돼야 해요. 여러분이나 시스템 관리자가 원하는 어떤 로케일이라도 설치됐는지 확인해야 해요. 사용 가능한 로케일, 보관 위치, 설치 방식은 시스템마다 모두 달라요. 일부 시스템은 소수의 하드와이어된 로케일만 제공하고 더 추가할 수 없어요. 다른 시스템은 시스템 공급업체가 제공하는 "캔" 로케일을 추가하게 해요. 또 다른 시스템은 여러분이나 시스템 관리자가 임의 로케일을 정의하고 추가하게 해요. (운영체제와 함께 배달되지 않는 캔 로케일을 공급업체에 요청해야 할 수도 있어요.) 자세한 내용은 시스템 문서를 읽어보세요.
Perl 응용 프로그램이 특정 로케일에 따라 데이터를 처리·표시하길 원한다면, 응용 프로그램 코드는 적절한 곳에 use locale 프라그마(아래 ""use locale" 프라그마" 참고)를 포함해야 하고 다음 중 적어도 하나가 참이어야 해요:
-
로케일 결정 환경 변수(아래 "환경" 참고)가 시작 시 올바르게 설정되어야 함 — 여러분 또는 시스템 계정을 설정한 사람에 의해; 또는
-
응용 프로그램이 "setlocale 함수"에 설명된 방법으로 자체 로케일을 설정해야 함.
로케일 사용하기 (USING LOCALES)
"use locale" 프라그마
기본적으로 Perl 자체(POSIX 모듈 밖에서)는 현재 로케일을 무시해요. use locale 프라그마는 Perl에게 일부 연산에 현재 로케일을 사용하라고 말해요. v5.16부터 이 프라그마에는 선택적 매개변수가 있는데, 아래 설명하며 어떤 연산이 영향을 받는지 제한해요.
Perl 5.28부터 이 프라그마는 스레드 안전 로케일 능력이 있는 시스템의 다중 스레드 응용 프로그램에서 사용될 수 있어요. 몇 가지 주의 사항이 있는데, 아래 "다중 스레드"를 참고하세요. 이 능력이 없는 시스템이나 이전 Perl에서는 여러 스레드가 활성인 스크립트에서 이 프라그마를 사용하지 마세요. 이런 경우 로케일은 단일 스레드에 국한되지 않아요. 다른 스레드가 언제든 로케일을 바꿀 수 있고, 그러면 최소한 주어진 스레드가 기대하지 않는 로케일에서 동작하는 것을 일으킬 수 있어요. 일부 플랫폼에서는 segfault도 일어날 수 있어요. 로케일 변경이 명시적일 필요는 없어요. 일부 연산이 perl 자체가 로케일을 바꾸게 해요. 단지 "use locale"을 한 것만으로 취약해져요.
현재 로케일은 아래 설명하는 setlocale()로 실행 시간에 설정돼요. 그 함수가 프로그램 실행 과정에서 아직 호출되지 않았다면, 현재 로케일은 프로그램 시작 시 유효한 "환경"에 의해 결정된 것이에요. 유효한 환경이 없다면 현재 로케일은 시스템 기본값이 설정된 무엇이든이에요. POSIX 시스템에서 그것은 아마도 (반드시는 아니지만) "C" 로케일이에요. Windows에서 기본값은 컴퓨터의 Control Panel->Regional and Language Options(또는 현재 등가물)를 통해 설정돼요.
로케일의 영향을 받는 연산은:
"use locale"의 범위 밖 (Not within the scope of "use locale")
오직 특정 연산(모두 Perl 밖에서 비롯된 것)만 영향을 받아야 해요:
- 현재 로케일은 Perl 밖으로 나갈 때 system()이나 qx// 같은 연산으로 나가는 경우, 그 연산이 로케일 민감하다면 사용돼요.
- 또한 Perl은 POSIX 모듈을 통해 다양한 C 라이브러리 함수에 접근을 주어요. 그 함수들 중 일부는 항상 현재 로케일의 영향을 받아요. 예를 들어
POSIX::strftime()은LC_TIME을,POSIX::strtod()는LC_NUMERIC을,POSIX::strcoll()과POSIX::strxfrm()은LC_COLLATE를 사용해요. 그런 모든 함수는 그 로케일이 Perl 공간에 노출되지 않더라도 현재 기본 로케일에 따라 동작할 거예요.
이것은 I18N::Langinfo에도 적용돼요.
LC_NUMERIC을 제외한 모든 범주의 XS 모듈은 기본 로케일을 얻으므로, 그들이 호출하는 어떤 C 라이브러리 함수든 그 기본 로케일을 사용할 거예요. 더 자세한 논의는 perlclib의 "Dealing with locales"를 참고하세요.
모든 C 프로그램(perl 인터프리터를 포함, C로 작성됐으니까)은 항상 기본 로케일을 가짐을 참고하세요. 그 로케일은 setlocale() 호출로 바꾸지 않는 한 "C" 로케일이에요. Perl이 시작하면 기본 로케일을 "환경"이 나타내는 것으로 바꿔요. POSIX 모듈을 사용하거나 XS 코드를 작성할 때, 프로그램이 명시적으로 바꾸지 않았어도 기본 로케일이 "C"가 아닌 다른 것일 수 있음을 염두에 두는 것이 중요해요.
use locale의 지속 효과 (Lingering effects of use locale)
use locale의 범위 안에서 설정된 특정 Perl 연산은 범위 밖에서도 그 효과를 유지해요. 여기에는:
- write()의 출력 형식은 더 이른 형식 선언(perlfunc의 "format")에 의해 결정돼요. 그래서 출력이 로케일의 영향을 받는지 여부는
write()가 아니라format()이use locale의 범위 안에 있는지에 의해 결정돼요. - 정규식 패턴은 qr//로 컴파일되고 실제 매칭은 나중으로 연기될 수 있어요. 다시 말해 매칭 동작을 결정하는 것은 매칭이 그런 범위 안에서 이루어지든 아니든이 아니라 컴파일이
use locale의 범위 안에서 이루어졌는지 여부예요.
"use locale"; 아래 (Under "use locale";)
- 위 모든 연산
- 형식 선언(perlfunc의 "format")과 따라서 이후의 모든
write()는LC_NUMERIC을 사용해요. - 문자열화와 출력은
LC_NUMERIC을 사용해요. 여기에는print(),printf(),say(),sprintf()의 결과가 포함돼요. - 비교 연산자(
lt,le,cmp,ge,gt)는LC_COLLATE를 사용해요.sort()도 명시적 비교 함수 없이 쓰면 영향을 받는데, 기본으로cmp를 쓰기 때문이에요.
참고: eq와 ne는 로케일의 영향을 받지 않아요: 그들은 항상 스칼라 피연산자를 문자별로 비교해요. 게다가 cmp가 피연산자가 현재 로케일이 지정한 조합 순서에 따라 같다고 찾으면 문자별 비교를 계속하고, 피연산자가 문자 대 문자로 동일할 때만 0(같음)을 반환해요. eq와 cmp가 다르다고 간주할 수 있는 두 문자열이 로케일의 조합 관점에서 같은지 정말 알고 싶다면 "범주 LC_COLLATE: 조합/대조"의 논의를 참고하세요.
- 정규식과 대소문자 수정 함수(
uc(),lc(),ucfirst(),lcfirst())는LC_CTYPE을 사용해요. - 변수
$!(동의어$ERRNO와$OS_ERROR) 그리고$^E(동의어$EXTENDED_OS_ERROR)는 문자열로 쓰일 때LC_MESSAGES를 사용해요. 이 범주가 없는 플랫폼에서는LC_CTYPE이 대신 사용돼요.
기본 동작은 no locale 프라그마로, 또는 use locale을 둘러싼 블록의 끝에 도달하면 복원돼요. use locale 호출은 중첩될 수 있고, 내부 범위 안에서 유효한 것이 내부 범위 끝에서 외부 범위의 규칙으로 되돌아갈 것임을 참고하세요.
로케일 정보를 사용하는 어떤 연산의 문자열 결과든 (perl이 taint 검사를 지원한다면) taint돼요. 로케일이 신뢰할 수 없을 수 있기 때문이에요. "보안" 참고.
Perl v5.16에서 매우 제한된 방식으로, 그리고 v5.22에서 더 일반적으로, 이 프라그마의 특정 인스턴스가 활성화하는 범주를 매개변수를 추가해 제한할 수 있어요. (이 능력은 perl의 로케일 처리 결함을 해결하기 위한 코드를 쓸 수 있게 하려는 것이었는데, 그 결함들은 이후 수정됐으므로 새 코드가 그것을 쓸 필요는 없을 거예요.)
예를 들어,
use locale qw(:ctype :numeric);
범위 안에서 오직 LC_CTYPE과 LC_NUMERIC의 영향을 받는 연산(위에 나열된)만 로케일 인지하게 해요.
가능한 범주는: :collate, :ctype, :messages, :monetary, :numeric, :time, 그리고 의사 범주 :characters(아래 설명)예요.
따라서 다음을 말할 수 있고,
use locale ':messages';
오직 "$!"와 "$^E"만 로케일 인지할 거예요. 다른 모든 것은 영향을 받지 않아요.
Perl이 현재 LC_MONETARY 범주로 아무것도 하지 않으므로 :monetary를 지정하는 것은 사실상 아무것도 안 해요. 일부 시스템은 LC_PAPER 같은 다른 범주가 있지만, perl 코어는 그것들로 아무것도 하지 않고, 이 프라그마의 인자로 그것들을 지정할 방법도 없어요.
예를 들어 다음 중 하나로 모든 범주를 쓰되 하나만 뺄 수도 쉽게 말할 수 있어요:
use locale ':!ctype';
use locale ':not_ctype';
둘 다 LC_CTYPE을 뺀 모든 범주의 로케일 인지를 활성화한다는 뜻이에요. use locale에서 부정 형태라면 범주 인자 하나만 지정할 수 있어요.
v5.22 이전에는 인자가 있는 프라그마의 한 형태만 사용 가능했어요:
use locale ':not_characters';
(not_라고 말해야 해요; bang ! 형태는 쓸 수 없어요.) 이 의사 범주는 :collate와 :ctype 둘 다 지정하는 것의 약칭이에요. 따라서 부정 형태에서는 다음을 말하는 것과 거의 같아요:
use locale qw(:messages :monetary :numeric :time);
"거의"라는 표현을 쓰는 이유는 :not_characters가 또한 그 범위 안에서 use feature 'unicode_strings'를 켜기 때문이에요. 이 형태는 v5.20 이후에서 덜 유용하며, "유니코드와 UTF-8"에서 완전히 설명하지만, 간단히 말하면 Perl에게 로케일 정의의 문자 부분, 즉 LC_CTYPE과 LC_COLLATE 범주를 사용하지 말라고 알려줘요. 대신 네이티브 문자셋(유니코드로 확장된)을 사용할 거예요. 이 매개변수를 쓸 때 외부 문자셋을 네이티브/유니코드로 번역하는 것(그것이 점점 인기 있는 UTF-8 로케일 중 하나라면 이미 그렇게 돼 있을 거예요)은 여러분 책임이에요. "유니코드와 UTF-8"에 설명된 대로 이를 위한 편리한 방법이 있어요.
setlocale 함수
경고! Perl 5.28 이전이거나 스레드 안전 로케일 연산을 지원하지 않는 시스템에서 스레드 안에 이 함수를 사용하지 마세요. 로케일이 동시에 다른 모든 스레드에서 바뀌고, 스레드가 운영체제에 의해 일시 중지되고 다른 스레드가 시작되면, 그 스레드는 기대하는 로케일을 갖지 못할 거예요. 일부 플랫폼에서는 두 스레드가 거의 동시에 이 함수를 호출하면 segfault로 이어지는 경쟁이 있을 수 있어요. 이 경고는 스레드 없는 빌드나 ${^SAFE_LOCALES}가 존재하고 0이 아닌 perl, 즉 Perl 5.28 이상의 스레드 없는 또는 로케일 스레드 안전하게 컴파일된 것에는 적용되지 않아요. z/OS 시스템에서 이 함수는 어떤 스레드가 시작되면 no-op이 돼요. 따라서 그 시스템에서는 스레드를 만들기 전에 로케일을 설정할 수 있고, 그 로케일이 전체 프로그램에 유효한 것이 될 거예요.
그렇지 않으면 런타임에 POSIX::setlocale() 함수로 원하는 만큼 자주 로케일을 바꿀 수 있어요:
# Import locale-handling tool set from POSIX module.
# This example uses: setlocale -- the function call
# LC_CTYPE -- explained below
# (Showing the testing for success/failure of operations is
# omitted in these examples to avoid distracting from the main
# point)
use POSIX qw(locale_h);
use locale;
my $old_locale;
# query and save the old locale
$old_locale = setlocale(LC_CTYPE);
setlocale(LC_CTYPE, "fr_CA.ISO8859-1");
# LC_CTYPE now in locale "French, Canada, codeset ISO 8859-1"
setlocale(LC_CTYPE, "");
# LC_CTYPE now reset to the default defined by the
# LC_ALL/LC_CTYPE/LANG environment variables, or to the system
# default. See below for documentation.
# restore the old locale
setlocale(LC_CTYPE, $old_locale);
setlocale()의 첫 번째 인자는 범주(category), 두 번째는 **로케일(locale)**을 줘요. 범주는 데이터 처리의 어떤 측면에 로케일별 규칙을 적용하려는지 말해줘요. 범주 이름은 "로케일 범주"와 "환경"에서 논의해요. 로케일은 언어, 국가 또는 영토, 코드셋의 특정 조합에 대응하는 사용자 정의 정보의 모음 이름이에요. 로케일 명명에 대한 힌트를 계속 읽어보세요: 모든 시스템이 예제처럼 로케일을 명명하지는 않아요.
두 번째 인자가 제공되지 않고 범주가 LC_ALL이 아닌 다른 것이라면, 함수가 그 범주에 대한 현재 로케일을 명명하는 문자열을 반환해요. 이 값을 후속 setlocale() 호출의 두 번째 인자로 쓸 수 있어요, 하지만 일부 플랫폼에서 그 문자열은 불투명해서, 대부분의 사람들이 무엇을 뜻하는 로케일인지 해독할 수 있는 것이 아니에요.
두 번째 인자가 제공되지 않고 범주가 LC_ALL이라면 결과는 구현에 달려 있어요. 로케일 이름의 연결 문자열(구분자도 구현에 달려)이거나 단일 로케일 이름일 수 있어요. 자세한 내용은 setlocale(3) man page를 참고하세요.
두 번째 인자가 주어지고 유효한 로케일에 대응한다면, 그 범주에 대한 로케일이 그 값으로 설정되고 함수가 지금-현재 로케일 값을 반환해요. 그런 다음 이것을 또 다른 setlocale() 호출에 쓸 수 있어요. (일부 구현에서 반환 값은 두 번째 인자로 준 값과 가끔 다를 수 있어요 — 여러분이 준 값의 별명이라고 생각하세요.)
예제가 보여주듯 두 번째 인자가 빈 문자열이면, 그 범주의 로케일은 대응 환경 변수가 지정한 기본값으로 돌아가요. 일반적으로 이는 Perl이 시작할 때 시행 중이던 기본값으로 돌아가는 결과를 내요: 시작 후 응용 프로그램이 환경에 한 변경은 시스템의 C 라이브러리에 따라 알아차려질 수도 있고 아닐 수도 있어요.
모든 범주를 포함하지 않는 use locale 형태가 지정되면 Perl이 제외된 범주를 무시함을 참고하세요.
setlocale()이 어떤 이유(예: 시스템이 모르는 로케일로 설정하려는 시도)로 실패하면 그 범주에 대한 로케일은 바뀌지 않고, 함수가 undef를 반환해요.
Perl 5.28부터, POSIX 2008 스레드 안전 로케일 연산을 구현한 시스템에서 컴파일된 다중 스레드 perl에서 이 함수는 실제로 시스템 setlocale을 호출하지 않아요. 대신 그 스레드 안전 연산들이 setlocale 함수를 에뮬레이션하지만 스레드 안전한 방식으로 사용돼요.
Configure 호출에 다음을 추가해
-Accflags='-DUSE_THREAD_SAFE_LOCALE'
스레드 안전 로케일 연산이 (사용 가능하다면) 항상 사용되도록 강제할 수 있어요.
범주에 대한 더 자세한 정보는 setlocale(3)을 참고하세요.
다중 스레드 연산 (Multi-threaded operation)
Perl 5.28부터 다중 스레드 로케일 연산이 POSIX 2008 또는 Windows 특정 스레드 안전 로케일 연산 중 하나를 구현한 시스템에서 지원돼요. 다양한 Unix 변종 같은 많은 현대 시스템이 이것을 가져요. Darwin을 포함한 대부분의 *BSD 파생 변종 같은 다른 시스템은 그것이 있다고 주장하지만 2024년 5월 기준으로 버그가 있어서 Perl은 그것들의 사용을 피해요.
시스템에서 로케일 사용이 안전한지 읽기 전용 변수 ${^SAFE_LOCALES}를 봄으로써 알 수 있어요. perl이 스레드되지 않았거나, 스레드 안전 로케일 연산을 사용 중이라면 값은 1이에요.
스레드 안전 연산은 Visual Studio 2005부터 Windows, cygwin, UCRT(Universal C Run Time 라이브러리)를 사용하도록 컴파일된 MingW, 그리고 POSIX 2008과 호환되는 시스템에서 지원돼요. Perl이 버그 있는 구현을 가진 것으로 아는 플랫폼의 스레드 빌드에서 ${^SAFE_LOCALES}는 0일 거예요.
다중 스레드 응용 프로그램을 작성하는 것은 네이티브 스레드 안전 로케일 지원이 없는 플랫폼에 이식 가능하지 않을 것임을 알아 두세요. 그것이 있는 시스템에서는 스레드 perl에 대해 아무것도 하지 않아도 자동으로 이 동작을 얻어요. 어떤 이유로 이 능력을 쓰고 싶지 않다면(아마 POSIX 2008 지원이 시스템에서 버그가 있다고 밝혀져서), Configure에 -Accflags='-DNO_THREAD_SAFE_LOCALE' 인자를 넘겨 Perl이 옛 비스레드 안전 구현을 사용하도록 수동 컴파일할 수 있어요. Windows를 제외하고 이것은 일부 상황에서 POSIX 2008 함수 중 일부를 계속 사용할 거예요. 그것들이 버그가 있다면 다음을 Configure에 대신 또는 추가로 넘길 수 있어요: -Accflags='-DNO_POSIX_2008_LOCALE'. 이것은 또한 코드가 스레드 안전 로케일을 사용하지 못하게 해요. 스레드 안전 연산을 끈 시스템에서 ${^SAFE_LOCALES}는 0일 거예요.
보통 스레드 없는 빌드에서 Perl은 로케일을 바꾸기 위해 전통적인 setlocale()을 사용하고, 대안인 POSIX 2008 스레드 안전 로케일 변경 함수를 사용하지 않아요. 그것들이 있는 시스템에서 Configure에 -Accflags='-DUSE_THREAD_SAFE_LOCALE'를 추가해 사용을 강제할 수 있어요.
초기 프로그램은 "환경"에 설명된 대로 환경에서 지정된 로케일로 시작돼요. 새로 생성된 모든 스레드는 LC_ALL이 "C"로 설정되어 시작해요. 각 스레드는 POSIX::setlocale()을 사용해 언제든 다른 스레드에 영향을 주지 않고 자신의 로케일을 조회하거나 바꿀 수 있어요. 모든 로케일 종속 연산은 자동으로 자신의 스레드 로케일을 사용해요.
이것은 전적으로 Perl로 작성된 어떤 응용 프로그램에도 완전히 투명해야 해요 ("다중 스레드" 섹션에서 주어진 드물게 마주치는 몇 가지 주의점을 뺀). XS 모듈 작성자를 위한 정보는 perlclib의 "Dealing with locales"에 있어요.
로케일 찾기 (Finding locales)
시스템에서 사용 가능한 로케일을 위해 setlocale(3)도 참고해 사용 가능한 로케일 목록으로 이어지는지 볼 수 있어요 (SEE ALSO 섹션 검색). 그것이 실패하면 다음 명령 줄을 시도해 보세요:
locale -a
nlsinfo
ls /usr/lib/nls/loc
ls /usr/lib/locale
ls /usr/lib/nls
ls /usr/share/locale
그것들이 이것들과 비슷한 것을 나열하는지 보세요:
en_US.ISO8859-1 de_DE.ISO8859-1 ru_RU.ISO8859-5
en_US.iso88591 de_DE.iso88591 ru_RU.iso88595
en_US de_DE ru_RU
en de ru
english german russian
english.iso88591 german.iso88591 russian.iso88595
english.roman8 russian.koi8r
안타깝게도 setlocale()의 호출 인터페이스는 표준화됐지만, 로케일 이름과 설정이 있는 디렉터리는 표준화되지 않았어요. 이름의 기본 형태는 *language[_territory[.codeset]][@modifier]*이에요. language와 country는 보통 표준 ISO 3166과 ISO 639에서 온 것이고, 각각 세계의 국가와 언어의 두 글자 약어예요. codeset 부분은 종종 일부 ISO 8859 문자셋, 라틴 코드셋을 언급해요. 예를 들어 ISO 8859-1은 대부분의 서유럽 언어를 적절히 인코딩하는 데 쓸 수 있는 소위 "서유럽 코드셋"이에요. 다시 말하지만, 그 표준 하나의 이름조차 쓰는 여러 방법이 있어요. 유감스럽게도. modifier는 로케일의 나머지에 매우 개별화된 것으로, 로케일이 보통 포함하지 않는 다른 통화 기호 같은 어떤 변형을 명명해요.
두 개의 특별한 로케일은 특히 언급할 만해요: "C"와 "POSIX". 현재 이들은 사실상 같은 로케일이에요: 차이는 주로 첫 번째가 C 표준으로, 두 번째가 POSIX 표준으로 정의됐다는 거예요. 그들은 환경에 로케일 정보가 없는 상태에서 모든 프로그램이 시작하는 기본 로케일을 정의해요. (말하자면 기본의 기본 로케일.) 그 언어는 (미국) 영어이고 문자 코드셋은 ASCII이거나 드물게 그 상위집합("DEC Multinational Character Set (DEC-MCS)" 같은)이에요. 경고. 일부 판매업체가 배달하는 C 로케일은 실제로 C 표준이 요구하는 것과 정확히 일치하지 않을 수 있어요. 조심하세요.
참고: 모든 시스템에 "POSIX" 로케일이 있는 것은 아니므로(모든 시스템이 POSIX 준수는 아니니까요), 이 기본 로케일을 명시적으로 지정해야 할 때 "C"를 사용하세요.
로케일 문제 (LOCALE PROBLEMS)
Perl 시작 시 다음 경고 메시지를 만날 수 있어요:
perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
LC_ALL = "En_US",
LANG = (unset)
are supported and installed on your system.
perl: warning: Falling back to the standard locale ("C").
이것은 로케일 설정이 LC_ALL을 "En_US"로 설정했고 LANG이 존재하지만 값이 없었다는 뜻이에요. Perl은 믿으려 했지만 못했어요. 대신 Perl은 포기하고 "C" 로케일, 무슨 일이 있어도 동작해야 하는 기본 로케일로 폴백했어요. (Windows에서는 먼저 시스템 기본 로케일로 폴백을 시도해요.) 이는 보통 로케일 설정이 잘못됐거나, 시스템이 들어본 적 없는 로케일을 언급하거나, 시스템의 로케일 설치에 문제(예: 일부 시스템 파일이 깨지거나 없음)가 있다는 뜻이에요. 이 문제에 대한 빠르고 임시적인 수정뿐 아니라 더 철저하고 지속적인 수정이 있어요.
깨진 로케일 테스트 (Testing for broken locales)
Perl을 소스에서 빌드한다면 Perl 테스트 스위트 파일 lib/locale.t를 사용해 시스템의 로케일을 테스트할 수 있어요. 환경 변수 PERL_DEBUG_FULL_TEST를 1로 설정하면 상세 결과를 출력하게 해요. 예를 들어 Linux에서:
PERL_DEBUG_FULL_TEST=1 ./perl -T -Ilib lib/locale.t > locale.log 2>&1
다른 많은 테스트 외에도 시스템에서 찾은 모든 로케일을 테스트해 POSIX 표준에 부합하는지 봐요. 오류가 있다면 출력 끝 근처에 어떤 로케일이 모든 테스트를 통과했고 어떤 것이 실패했으며 왜 그런지에 대한 요약을 포함할 거예요.
로케일 문제 임시 수정 (Temporarily fixing locale problems)
가장 빠른 두 수정은 Perl을 로케일 불일치에 대해 침묵하게 하거나 "C" 기본 로케일 아래에서 Perl을 실행하는 거예요.
환경 변수 PERL_BADLANG을 "0" 또는 ""으로 설정해 Perl의 로케일 문제에 대한 불평을 침묵시킬 수 있어요. 이 방법은 정말 문제를 카펫 밑으로 쓸어버리는 것뿐이에요: Perl이 뭔가 잘못된 것을 봐도 입을 다물라고 말하는 거예요. 나중에 로케일 종속적인 무언가가 잘못 행동해도 놀라지 마세요.
환경 변수 LC_ALL을 "C"로 설정해 "C" 로케일 아래에서 Perl을 실행할 수 있어요. 이 방법은 아마 PERL_BADLANG 접근보다 조금 더 문명화됐지만, LC_ALL(또는 다른 로케일 변수)을 설정하면 Perl뿐 아니라 다른 프로그램에도 영향을 줄 수 있어요. 특히 Perl 내부에서 실행되는 외부 프로그램이 이러한 변경을 볼 거예요. 새 설정을 영구적으로 만든다면(계속 읽어보세요) 실행하는 모든 프로그램이 변경을 볼 거예요. 관련 환경 변수의 전체 목록은 "환경", Perl에서의 효과는 "로케일 사용하기"를 참고하세요. 다른 프로그램에서의 효과는 쉽게 추론할 수 있어요. 예를 들어 LC_COLLATE 변수는 sort 프로그램(또는 시스템에서 "레코드"를 알파벳순으로 배열하는 프로그램이 무엇이든)에 영향을 줄 수 있어요.
이 변수들을 임시로 바꿔 테스트할 수 있고, 새 설정이 도움이 되는 것 같으면 그 설정을 셸 시작 파일에 넣어요. 정확한 세부 사항은 로컬 문서를 참고하세요. Bourne류 셸(sh, ksh, bash, zsh)의 경우:
LC_ALL=en_US.ISO8859-1
export LC_ALL
이것은 위에서 논의한 명령으로 로케일 "en_US.ISO8859-1"을 봤다고 가정해요. 우리는 결함 있는 "En_US" 대신 그것을 시도해 보기로 했어요.
Csh류 셸(csh, tcsh)에서는:
setenv LC_ALL en_US.ISO8859-1
또는 "env" 응용 프로그램이 있다면 (어떤 셸에서든) 다음을 할 수 있어요:
env LC_ALL=en_US.ISO8859-1 perl ...
무슨 셸이 있는지 모르겠다면 로컬 헬프데스크나 등가물을 참고하세요.
로케일 문제 영구 수정 (Permanently fixing locale problems)
느리지만 우월한 수정은 여러분이 자신의 환경 변수의 잘못된 설정을 스스로 고칠 수 있을 때예요. 전체 시스템의 로케일의 잘못된(누락된) 설정은 보통 시스템 관리자의 도움이 필요해요.
첫째, 이 문서 앞부분의 "로케일 찾기"를 참고하세요. 시스템에서 어떤 로케일이 실제로 지원되고—더 중요하게—설치됐는지 찾는 방법을 알려줘요. 우리 예제 오류 메시지에서 로케일에 영향을 주는 환경 변수는 중요도가 감소하는 순서로 나열돼요 (설정되지 않은 변수는 상관없어요). 따라서 LC_ALL이 "En_US"로 설정된 것이 나쁜 선택이었음에 틀림없어요. 오류 메시지가 보여주듯이요. 먼저 나열된 로케일 설정을 고치는 것을 먼저 시도하세요.
둘째, 나열된 명령을 사용해 (접두사 매칭은 셈하지 않고 대소문자는 보통 중요해요) 그대로(따옴표 없이) "En_US" 같은 것을 보면 괜찮을 거예요. 시스템에 설치되고 사용 가능해야 하는 로케일 이름을 사용하고 있으니까요. 이 경우 "시스템 로케일 설정을 영구적으로 고치기"를 참고하세요.
시스템 로케일 설정을 영구적으로 고치기 (Permanently fixing your system's locale configuration)
이것은 다음과 같은 것을 볼 때예요:
perl: warning: Please check that your locale settings:
LC_ALL = "En_US",
LANG = (unset)
are supported and installed on your system.
하지만 위에서 언급한 명령이 "En_US"를 나열한 것을 볼 수 없을 때요. "en_US.ISO8859-1" 같은 것을 볼 수 있지만 그것은 같지 않아요. 이 경우, 나열할 수 있고 시도한 것과 어떻게든 일치하는 로케일 아래에서 실행해 보세요. 로케일 이름 매칭 규칙은 이 영역에서 표준화가 약하기 때문에 약간 모호해요. 일반 규칙에 대해서는 다시 "로케일 찾기"를 참고하세요.
시스템 로케일 구성 고치기 (Fixing system locale configuration)
시스템 관리자(가급적 자신의 관리자)에게 연락하고 받은 정확한 오류 메시지를 보고하고, 지금 읽고 있는 이 문서를 읽어달라고 요청하세요. 그들은 시스템의 로케일 구성에 뭔가 잘못된 것이 있는지 확인할 수 있어야 해요. "로케일 찾기" 섹션은 정확한 명령과 위치에 대해 불행히도 다소 모호한데, 이런 것들이 그렇게 표준화되지 않았기 때문이에요.
localeconv 함수
POSIX::localeconv() 함수는 현재 기본 LC_NUMERIC과 LC_MONETARY 로케일이 지정한 로케일 종속 숫자 형식 정보의 세부 사항을 얻을 수 있게 해요 (use locale의 범위 안에서 호출됐든 아니든 상관없이). (특정 범주에 대한 현재 로케일의 이름만 원한다면 단일 매개변수로 POSIX::setlocale()을 사용하세요—"setlocale 함수" 참고.)
use POSIX qw(locale_h);
# Get a reference to a hash of locale-dependent info
$locale_values = localeconv();
# Output sorted list of the values
for (sort keys %$locale_values) {
printf "%-20s = %s\n", $_, $locale_values->{$_}
}
localeconv()는 인자를 받지 않고 해시 에 대한 참조를 반환해요. 이 해시의 키는 decimal_point와 thousands_sep 같은 형식화를 위한 변수 이름이에요. 값은 대응하는, 어, 값들이에요. 구현이 제공할 것으로 기대되는 범주를 나열한 더 긴 예는 POSIX의 "localeconv"를 참고하세요. 어떤 것은 더 많고 어떤 것은 더 적게 제공해요. 명시적 use locale이 필요하지 않아요. localeconv()가 항상 현재 로케일을 관찰하니까요.
명령줄 매개변수를 현재 로케일에서 올바르게 형식화된 정수로 다시 쓰는 단순한 예제 프로그램:
use POSIX qw(locale_h);
# Get some of locale's numeric formatting parameters
my ($thousands_sep, $grouping) =
@{localeconv()}{'thousands_sep', 'grouping'};
# Apply defaults if values are missing
$thousands_sep = ',' unless $thousands_sep;
# grouping and mon_grouping are packed lists
# of small integers (characters) telling the
# grouping (thousand_seps and mon_thousand_seps
# being the group dividers) of numbers and
# monetary quantities. The integers' meanings:
# 255 means no more grouping, 0 means repeat
# the previous grouping, 1-254 means use that
# as the current grouping. Grouping goes from
# right to left (low to high digits). In the
# below we cheat slightly by never using anything
# else than the first grouping (whatever that is).
if ($grouping) {
@grouping = unpack("C*", $grouping);
} else {
@grouping = (3);
}
# Format command line params for current locale
for (@ARGV) {
$_ = int; # Chop non-integer part
1 while
s/(\d)(\d{$grouping[0]}($|$thousands_sep))/$1$thousands_sep$2/;
print "$_";
}
print "\n";
플랫폼에 LC_NUMERIC 및/또는 LC_MONETARY가 없거나 활성화되지 않았다면 해시의 대응 요소가 없을 것임을 참고하세요.
I18N::Langinfo
로케일 종속 정보를 조회하는 또 다른 인터페이스는 I18N::Langinfo::langinfo() 함수예요.
다음 예제는 langinfo() 함수 자체와 langinfo()에 인자로 쓰일 세 상수: 요일의 약식 첫날 상수(번호는 일요일=1로 시작)와 현재 로케일의 yes/no 질문에 대한 긍정·부정 답을 위한 두 상수를 import할 거예요.
use I18N::Langinfo qw(langinfo ABDAY_1 YESSTR NOSTR);
my ($abday_1, $yesstr, $nostr)
= map { langinfo } qw(ABDAY_1 YESSTR NOSTR);
print "$abday_1? [$yesstr/$nostr] ";
다시 말해, "C"(또는 영어) 로케일에서 위는 아마 다음처럼 출력할 거예요:
Sun? [yes/no]
자세한 내용은 I18N::Langinfo를 참고하세요.
로케일 범주 (LOCALE CATEGORIES)
다음 하위 섹션들은 기본 로케일 범주를 설명해요. 그 너머로, 일부 조합 범주가 한 번에 둘 이상의 기본 범주 조작을 허용해요. 이에 대한 논의는 "환경"을 참고하세요.
범주 LC_COLLATE: 조합/대조: 텍스트 비교와 정렬 (Collation: Text Comparisons and Sorting)
조합을 포함하는 use locale 형태의 범위에서 Perl은 LC_COLLATE 환경 변수를 봐서 문자의 조합(순서)에 대한 응용 프로그램의 개념을 결정해요. 예를 들어 "b"는 라틴 알파벳에서 "a"를 따를 가능성이 높지만, "á"와 "å"는 어디에 속할까요? 그리고 "color"는 영어에서 "chocolate"를 따르지만, 전통 스페인어에서는 어떨까요?
다음 조합은 모두 의미가 있고 "use locale"하면 그 중 어떤 것이든 만날 수 있어요.
A B C D E a b c d e
A a B b C c D d E e
a A b B c C d D e E
a b c d e A B C D E
현재 로케일에서 어떤 "word" 문자가 그 로케일 순서로 있는지 말해주는 코드 조각:
use locale;
print +(sort grep /\w/, map { chr } 0..255), "\n";
로케일을 무시해야 한다고 명시하면 보이는 문자와 그 순서와 비교해 보세요:
no locale;
print +(sort grep /\w/, map { chr } 0..255), "\n";
이 기계 네이티브 조합(같은 블록에서 use locale이 더 일찍 나타나지 않으면 얻는 것)은 원시 이진 데이터를 정렬하는 데 쓰여야 하고, 첫 예제의 로케일 종속 조합은 자연 텍스트에 유용해요.
"로케일 사용하기"에서 언급했듯이 cmp는 use locale이 유효할 때 현재 조합 로케일에 따라 비교하지만, 로케일이 같다고 말하는 문자열에 대해서는 문자별 비교로 폴백해요. 이 폴백을 원하지 않는다면 POSIX::strcoll()을 쓸 수 있어요:
use POSIX qw(strcoll);
$equal_in_locale =
!strcoll("space and case ignored", "SpaceAndCaseIgnored");
조합 로케일이 공백 문자를 완전히 무시하고 대소문자를 접는 사전류 순서를 지정한다면 $equal_in_locale은 참일 거예요.
Perl은 플랫폼의 C 라이브러리 조합 함수 strcoll()과 strxfrm()을 사용해요. 즉 그들이 주는 것을 얻는다는 뜻이에요. 일부 플랫폼에서 이 함수들은 UTF-8 로케일에서 잘 동작해, 그 로케일에서 중요한 코드 포인트에 대해 합리적인 기본 조합을 줘요. (그리고 잘 동작하지 않는다면, 문제는 단지 로케일 정의가 부족해서일 수 있어, 더 나은 정의 파일을 사용해 고칠 수 있어요. 유니코드의 정의(아래 "자유롭게 사용 가능한 로케일 정의" 참고)는 합리적인 UTF-8 로케일 조합 정의를 제공해요.) Perl v5.26부터 Perl의 이 함수 사용이 더 매끄러워졌어요. 이는 필요에 충분할 수 있어요. 더 많은 제어를 위해, 그리고 어떤 코드 포인트(로케일에서 중요한 것만이 아니라)를 포함하는 문자열이 올바로 조합되도록 보장하기 위해 Unicode::Collate 모듈이 제안돼요.
비UTF-8 로케일(따라서 단일 바이트)에서 0xFF 위의 코드 포인트는 기술적으로 유효하지 않아요. 하지만 존재한다면, 다시 v5.26부터, 그들은 가장 높은 유효 코드 포인트와 같은 위치에 조합될 거예요. 이것은 일반적으로 좋은 결과를 주지만, 유효 코드 포인트가 로케일이 정의한 다른 문자와 특정 시퀀스를 형성할 때 특별 취급을 받으면 조합 순서가 왜곡될 수 있어요. 두 문자열이 동일하게 조합될 때 코드 포인트 순서가 타이브레이커로 사용돼요.
Perl이 로케일 조합 순서에 문제가 있다고 감지하면 그 로케일에 대해 비로케일 조합 규칙 사용으로 되돌아가요.
["로케일에서의 동등성"을 다른 여러 문자열에 대해 확인하려는 단일 문자열이 있다면 POSIX::strxfrm()을 eq와 함께 사용해 약간의 효율을 얻을 수 있다고 생각할 수 있어요:
use POSIX qw(strxfrm);
$xfrm_string = strxfrm("Mixed-case string");
print "locale collation ignores spaces\n"
if $xfrm_string eq strxfrm("Mixed-casestring");
print "locale collation ignores hyphens\n"
if $xfrm_string eq strxfrm("Mixedcase string");
print "locale collation ignores case\n"
if $xfrm_string eq strxfrm("mixed-case string");
strxfrm()은 문자열을 가져와 조합 중 다른 변환 문자열에 대한 문자별 비교에 쓰기 위한 변환 문자열로 매핑해요. "후드 아래에서" 로케일의 영향을 받는 Perl 비교 연산자는 양쪽 피연산자에 대해 strxfrm()을 호출한 다음 변환 문자열을 문자별로 비교해요. strxfrm()을 명시적으로 호출하고 로케일 영향을 받지 않는 비교를 사용함으로써, 이 예제는 몇 가지 변환을 절약하려 해요. 하지만 실제로는 아무것도 절약하지 않아요: Perl 매직(perlguts의 "Magic Variables" 참고)은 문자열의 변환 버전을 비교에서 처음 필요할 때 만들고, 다시 필요할 경우를 위해 이 버전을 보관해요. cmp로 쉬운 방법으로 다시 쓴 예제는 거의 같은 속도로 실행돼요. 또한 문자열에 내장된 null 문자를 처리해요. strxfrm()을 직접 호출하면 찾는 첫 null을 종결자로 취급해요. 그것이 생성한 변환 문자열이 시스템 간에—또는 운영체제의 한 수정판에서 다음으로—이식 가능하길 기대하지 마세요. 요컨대 strxfrm()을 직접 호출하지 마세요: Perl에게 맡기세요.
참고: 일부 예제에 use locale이 안 보이는 이유는 필요 없기 때문이에요: strcoll()과 strxfrm()은 항상 현재 LC_COLLATE 로케일을 따르는 표준 시스템 제공 libc 함수를 사용하는 POSIX:: 함수예요.
범주 LC_CTYPE: 문자 유형 (Character Types)
LC_CTYPE을 포함하는 use locale 형태의 범위에서 Perl은 LC_CTYPE 로케일 설정을 따르죠. 이것은 어떤 문자가 알파벳, 숫자, 구두점 등인지에 대한 응용 프로그램의 개념을 제어해요. 이것은 영숫자 문자—즉 알파벳, 숫자, 플랫폼 네이티브 밑줄—를 나타내는 Perl의 \w 정규식 메타표기를 영향을 줘요. (정규식에 대한 자세한 내용은 perlre 참고) LC_CTYPE 덕분에 로케일 설정에 따라 "æ", "ð", "ß", "ø" 같은 문자가 \w 문자로 이해될 수 있어요. \s, \D 그리고 [[:graph:]] 같은 POSIX 문자 클래스에도 영향을 줘요. (이 모든 것에 대한 자세한 정보는 perlrecharclass 참고.)
LC_CTYPE 로케일은 또한 문자를 소문자와 대문자 사이에서 전환하는 데 쓰는 맵을 제공해요. 이는 대소문자 매핑 함수—fc(), lc(), lcfirst(), uc(), ucfirst(); 이중 인용 문자열과 s/// 치환에서 \F, \l, \L, \u, \U를 사용한 대소문자 매핑 보간; 그리고 i 수정자를 사용한 대소문자 무시 정규식 패턴 매칭에 영향을 줘요.
v5.20부터 Perl은 LC_CTYPE에 대해 UTF-8 로케일을 지원하지만, 그 외에는 Perl이 ISO 8859 시리즈 같은 단일 바이트 로케일만 지원해요. 이것은 예를 들어 아시아 언어용 넓은 문자 로케일이 잘 지원되지 않는다는 뜻이에요. 이 로케일을 사용하면 core dump가 일어날 수 있어요. 플랫폼에 Perl이 그런 로케일을 감지할 능력이 있다면, Perl v5.22부터 Perl이 경고할 거예요 (기본 활성화), locale 경고 범주를 사용해서, 그런 로케일로 전환될 때마다요. UTF-8 로케일 지원은 실제로 POSIX 로케일의 상위집합이에요. LC_CTYPE 로케일이 전혀 없는 것처럼 정말 완전한 유니코드 동작이기 때문이죠 (tain팅 제외; "보안" 참고). POSIX 로케일은, UTF-8 로케일조차도, 분자 대소문자 변경이 둘 이상의 문자로 확장될 수 있다는 개념 같은 유니코드의 특정 개념이 없어요. UTF-8 로케일의 Perl은 그 확장을 줄 거예요. v5.20 이전에 Perl은 일부 플랫폼에서 UTF-8 로케일을 약간의 제한이 있는 ISO 8859-1처럼, 다른 플랫폼에서는 "C" 로케일처럼 더 취급했어요. v5.16과 v5.18 릴리즈에서 use locale 'not_characters'를 이에 대한 우회책으로 쓸 수 있었어요 ("유니코드와 UTF-8" 참고).
현재 로케일의 영향을 받지 않는 꽤 많은 것이 있음을 참고하세요. 어떤 리터럴 문자든 주어진 플랫폼의 네이티브 문자예요. 따라서 'A'는 ASCII 플랫폼에서 코드 포인트 65, EBCDIC에서 193의 문자를 의미해요. 그것이 현재 로케일의 'A'일 수도 아닐 수도 있어요 — 그 로케일에 'A'가 있다면 말이죠. 마찬가지로 특정 문자에 대한 모든 이스케이프 시퀀스, 예를 들어 \n은 항상 플랫폼의 네이티브 것을 의미해요. 이것은 예를 들어 정규식의 \N(줄바꿈을 뺀 모든 문자)이 플랫폼 문자셋에서 동작한다는 뜻이에요.
v5.22부터 Perl은 기본적으로 어떤 ASCII 인쇄 가능 문자(플러스 \t와 \n)를 기대와 다른 클래스로 재정의하는 로케일로 전환할 때 경고해요. 이것은 현대 로케일에서는 EBCDIC 플랫폼에서만 일어날 가능성이 높아요. 예를 들어 CCSID 1047 머신의 CCSID 0037 로케일이 "["를 옮기지만, 본질적으로 구식인 ISO 646과 다른 7비트 로케일에서는 ASCII 플랫폼에서 일어날 수 있어요. 프로그램이 Perl의 어떤 기능을 사용하는지에 따라 여전히 동작할 수 있어요. 예를 들어 위의 예에서 "|"가 \w가 되는데 그것이 중요한 정규식이 없다면 프로그램은 여전히 올바르게 동작할 수 있어요. 그 경고는 영향을 받을 수 있다고 결정할 수 있는 모든 문자를 나열해요.
참고: 깨졌거나 악의적인 LC_CTYPE 로케일 정의는 분명히 부적격한 문자가 응용 프로그램에 의해 영숫자로 간주되게 할 수 있어요. (평범한) ASCII 문자와 숫자의 엄격한 매칭을 위해—예를 들어 명령 문자열에서—로케일 인지 응용 프로그램은 /a 정규식 수정자와 함께 \w를 사용해야 해요. "보안" 참고.
범주 LC_NUMERIC: 숫자 형식 (Numeric Formatting)
적절한 POSIX::setlocale() 호출 후, numerics를 포함하는 use locale 형태의 범위 안에서 Perl은 숫자를 인간이 읽기 쉽도록 어떻게 형식화해야 하는지의 응용 프로그램 생각을 제어하는 LC_NUMERIC 로케일 정보를 따르죠. 대부분의 구현에서 유일한 효과는 소수점에 쓰는 문자를 바꾸는 것—아마 "."에서 ","로—이에요. 함수들은 천 단위 구분 같은 세련된 것을 알지 못해요. (이런 것에 신경 쓴다면 "localeconv 함수" 참고.)
use POSIX qw(strtod setlocale LC_NUMERIC);
use locale;
setlocale LC_NUMERIC, "";
$n = 5/2; # Assign numeric 2.5 to $n
$x = " $n"; # Locale-dependent conversion to string
print "half five is $n\n"; # Locale-dependent output
printf "half five is %g\n", $n; # Locale-dependent output
print "DECIMAL POINT IS COMMA\n"
if $n == (strtod("2,5"))[0]; # Locale-dependent conversion
I18N::Langinfo와 RADIXCHAR도 참고하세요.
범주 LC_MONETARY: 화폐 금액 형식 (Formatting of monetary amounts)
C 표준은 LC_MONETARY 범주를 정의하지만, 그 내용의 영향을 받는 함수는 정의하지 않아요. (표준 위원회 경험이 있는 분들은 작업 그룹이 이 문제를 미루기로 결정했음을 알아볼 거예요. POSIX 2001이 통화 금액을 형식화하는 strfmon() 함수를 추가했지만, 통화 값을 나타내는 문자열을 파싱하는 공식 함수는 없어요.)
Perl은 본질적으로 이 범주를 알아차리지 못해요. POSIX 시스템에서 XS 코드에서 strfmon()을 호출해 형식화된 문자열을 만들 수 있고, 그리고/또는 "localeconv 함수"로 LC_MONETARY 로케일별 값을 조회하고 반환된 정보를 응용 프로그램 자신의 통화 금액 형식에 사용할 수 있어요. 하지만 정보가 방대하고 복잡할지라도 여전히 요구 사항을 정확히 충족하지 못한다는 것을 알게 될 수 있어요: 통화 형식은 깨기 어려운 견과예요.
I18N::Langinfo의 CRNCYSTR도 참고하세요.
범주 LC_TIME: 시간 표현 (Representation of time)
형식화된 인간이 읽을 수 있는 날짜/시간 문자열을 만드는 POSIX::strftime()이 생성하는 출력은 현재 LC_TIME 로케일의 영향을 받아요. 따라서 프랑스 로케일에서 1월에 대한 %B 형식 요소(전체 월 이름)가 생성하는 출력은 "janvier"일 거예요. 현재 로케일에서 긴 월 이름 목록을 얻는 방법:
use POSIX qw(strftime);
for (0..11) {
$long_month_name[$_] =
strftime("%B", 0, 0, 0, 1, $_, 96);
}
참고: 이 예제에 use locale이 필요하지 않아요: strftime()은 항상 현재 LC_TIME 로케일을 따르는 표준 시스템 제공 libc 함수를 사용하는 POSIX:: 함수예요.
I18N::Langinfo와 ABDAY_1..ABDAY_7, DAY_1..DAY_7, ABMON_1..ABMON_12, 그리고 ABMON_1..ABMON_12도 참고하세요.
POSIX 2001부터 정의된 libc strptime() 함수도 있어요 (Windows에는 없어요) — 형식화된 시간 문자열을 파싱해요. 현재 이 함수에 대한 순수 perl 접근은 없으므로 사용하려면 XS 코드를 작성해야 해요.
범주 LC_MESSAGES: 시스템 메시지 (System messages)
이 범주는 "$!" 또는 "$^E"라고 말해 얻는 것 같은 시스템 오류 번호를 설명하는 문자열을 만드는 데 perl이 사용해요. 일부 시스템과 로케일에서 그 문자열은 LC_MESSAGES가 주는 로케일의 언어일 거예요. 하지만 시스템의 모든 사용 가능한 로케일에 대해 그런 번역을 설치하는 데 신경 쓴 시스템은 많지 않아요. 주어진 로케일에 대해 번역이 없으면 문자열은 영어일 거예요. 오류 코드를 이식 가능하게 사용하는 정보는 Errno을 참고하세요.
지금까지 언급한 다른 범주들은 Perl이 실행될 수 있는 어떤 플랫폼에서든 존재해야 요구돼요. 하지만 이 범주는 POSIX 확장이고, Perl은 예를 들어 Windows처럼 그것이 없는 플랫폼에서 실행돼요. 그런 플랫폼에서 시스템 오류의 기본 언어는 LC_CTYPE이 주는 것이거나 영어일 거예요.
이 범주는 I18N::Langinfo와 함께 사용해 yes/no를 로케일의 언어로 출력하고, 그 언어로 "yes"나 "no"를 포함하는 문자열을 파싱하는 데 쓸 수 있어요.
기타 범주 (Other categories)
일부 플랫폼은 추가 범주가 있어요. Perl 자신은 그것들을 사용하지 않아요. I18N::Langinfo로 조회할 수 있고, 없는 플랫폼에서는 스텁 값을 줘요. 하지만 Perl이 상호작용하는 것들이 이것들을 사용할 수 있음을 다시 참고하세요. 표준 Perl 배포 밖의 확장, 운영체제와 그 유틸리티를 포함해서요.
보안 (SECURITY)
Perl 보안 문제의 주요 논의는 perlsec에서 찾을 수 있지만, 로케일 종속 보안 문제에 주의를 환기하지 않으면 Perl의 로케일 처리 논의는 불완전할 거예요. 로케일—특히 비특권 사용자가 자기 로케일을 만들 수 있는 시스템에서—은 신뢰할 수 없어요. 악의적인(또는 그냥 깨진) 로케일은 로케일 인지 응용 프로그램이 예상치 못한 결과를 주게 만들 수 있어요. 몇 가지 가능성:
\w를 사용한 안전한 파일 이름이나 메일 주소에 대한 정규식 검사는">"와"|"같은 문자가 영숫자라고 주장하는LC_CTYPE로케일에 의해 속일 수 있어요.$dest = "C:\U$name.$ext"같은, 대소문자 매핑이 있는 문자열 보간은 가짜LC_CTYPE대소문자 매핑 테이블이 유효하면 위험한 결과를 낼 수 있어요.- 교활한
LC_COLLATE로케일은 "D" 학점 학생의 이름이 "A" 학생들 앞에 나타나게 할 수 있어요. LC_MONETARY의 정보를 사용하는 수고를 하는 응용 프로그램은 그 로케일이 전복됐다면 대변을 대변으로, 반대로 형식화할 수 있어요. 또는 홍콩 달러 대신 미국 달러로 지불할 수도 있어요.strftime()이 형식화한 날짜의 날짜와 요일 이름은LC_TIME로케일을 전복할 수 있는 악의적인 사용자가 유리하게 조작할 수 있어요. ("봐요—일요일에 건물에 없었다고 쓰여 있어요.")
그런 위험은 로케일 시스템에만 있는 것이 아니에요: 응용 프로그램 환경의 악의적으로 수정될 수 있는 어떤 측면도 비슷한 도전을 제시해요. 마찬가지로 Perl에만 있는 것도 아니에요: 환경을 고려하는 프로그램을 쓸 수 있게 하는 어떤 프로그래밍 언어든 이런 문제에 노출시켜요.
Perl은 예제에 나온 모든 가능성으로부터 여러분을 보호할 수 없어요 — 여러분 자신의 경계심을 대체할 것은 없어요 — 하지만 use locale이 유효할 때 Perl은 tainting 메커니즘(perlsec 참고)을 사용해 로케일 종속이 되고 결과적으로 신뢰할 수 없을 수 있는 문자열 결과를 표시해요.
taint 지원 없이 Perl을 컴파일할 수 있음을 참고하세요. 그 경우 모든 taint 기능이 조용히 아무것도 안 해요.
로케일의 영향을 받을 수 있는 연산자와 함수의 tainting 동작 요약:
- 비교 연산자 (
lt,le,ge,gt,cmp):
스칼라 참/거짓(또는 작음/같음/큼) 결과는 결코 taint되지 않아요.
- 대소문자 매핑 보간 (
\l,\L,\u,\U,\F와 함께):
LC_CTYPE을 포함하는 use locale 형태가 유효하면 보간된 재료를 포함하는 결과 문자열은 taint돼요.
- 매칭 연산자 (
m//):
스칼라 참/거짓 결과는 결코 taint되지 않아요.
LC_CTYPE을 포함하는 use locale 형태가 유효하고 하위 패턴 정규식이 로케일 종속 구성을 포함하면, 모든 하위 패턴은 리스트 컨텍스트 결과로 배달되든 $1 등으로든 taint돼요. 이러한 구성은 \w(영숫자 문자 매칭), \W(비영숫자 문자), \b와 \B(단어 경계와 비경계, \w와 \W가 무엇을 매치하는지에 달림), \s(공백 문자), \S(비공백 문자), \d와 \D(숫자와 비숫자), 그리고 [:alpha:] 같은 POSIX 문자 클래스(perlrecharclass의 "POSIX Character Classes")를 포함해요.
패턴이 대소문자 무시로(/i를 통해) 매치되어야 한다면 tainting이 또한 가능성이 높아요. 예외는 이런 식으로 매치될 모든 코드 포인트가 255 위에 있고 유니코드 규칙 아래 256 아래로 접지(fold)가 없을 때예요. Perl은 그런 코드 포인트에 대해 유니코드 규칙만 사용하므로 tainting이 되지 않아요. 그 규칙은 현재 로케일과 무관하게 같으니까요.
매치된 패턴 변수 $&, $`(매치 전), $'(매치 후), $+(마지막 매치)도 taint돼요.
- 치환 연산자 (
s///):
매치 연산자와 같은 동작을 해요. 또한, LC_CTYPE을 포함하는 use locale 형태가 유효할 때, 앞 항목에 언급된 것 중 어떤 것이든 포함하는 정규식 매치, 또는 \l, \L, \u, \U, \F 같은 대소문자 매핑에 기반한 치환의 결과로 수정된다면 =~의 왼쪽 피연산자가 taint돼요.
- 출력 형식화 함수 (
printf()와write()):
결과는 결코 taint되지 않아요. 그렇지 않으면 use locale이 유효할 때 print(1/7) 같은 print의 출력조차 taint되어야 하니까요.
- 대소문자 매핑 함수 (
lc(),lcfirst(),uc(),ucfirst()):
LC_CTYPE을 포함하는 use locale 형태가 유효하면 결과가 taint돼요.
- POSIX 로케일 종속 함수 (
localeconv(),strcoll(),strftime(),strxfrm()):
결과는 결코 taint되지 않아요.
세 예제가 로케일 종속 tainting을 보여줘요. 첫 프로그램은 로케일을 무시하고 실행되지 않을 거예요: 명령줄에서 직접 가져온 값은 taint 검사가 활성화될 때 출력 파일 이름으로 쓰일 수 없어요.
#/usr/local/bin/perl -T
# Run with taint checking
# Command line sanity check omitted...
$tainted_output_file = shift;
open(F, ">$tainted_output_file")
or warn "Open of $tainted_output_file failed: $!\n";
프로그램은 taint된 값을 정규식을 통해 "세탁"함으로써 실행되게 만들 수 있어요: 두 번째 예제—여전히 로케일 정보를 무시—가 실행되며, 가능하면 명령줄에 이른 파일을 만들어요.
#/usr/local/bin/perl -T
$tainted_output_file = shift;
$tainted_output_file =~ m%[\w/]+%;
$untainted_output_file = $&;
open(F, ">$untainted_output_file")
or warn "Open of $untainted_output_file failed: $!\n";
비슷하지만 로케일 인지 프로그램과 비교해 보세요:
#/usr/local/bin/perl -T
$tainted_output_file = shift;
use locale;
$tainted_output_file =~ m%[\w/]+%;
$localized_output_file = $&;
open(F, ">$localized_output_file")
or warn "Open of $localized_output_file failed: $!\n";
이 세 번째 프로그램은 $&가 taint돼서 실행에 실패해요: use locale이 유효한 동안 \w를 포함하는 매치의 결과이기 때문이에요.
환경 (ENVIRONMENT)
PERL_SKIP_LOCALE_INIT
v5.20부터 사용 가능한 이 환경 변수는 설정되면(어떤 값이든) Perl에게 나머지 환경 변수를 초기화에 사용하지 말라고 말해요. 대신 Perl은 현재 로케일 설정이 무엇이든 그것을 사용해요. 이것은 임베디드 환경에서 특히 유용해요. perlembed의 "Using embedded Perl with POSIX locales" 참고.
PERL_BADLANG
시작 시 로케일 설정 실패에 대한 Perl의 경고를 억제할 수 있는 문자열이에요. 운영체제의 로케일 지원이 어떤 방식으로든 부족(깨짐)하거나—환경을 설정할 때 로케일 이름을 잘못 입력했을 때—실패가 일어날 수 있어요. 이 환경 변수가 없거나 "0"이나 "" 외의 값을 가지면 Perl은 로케일 설정 실패에 대해 불평할 거예요.
참고: PERL_BADLANG은 경고 메시지를 숨길 방법만 줄 뿐이에요. 그 메시지는 시스템 로케일 지원의 어떤 문제에 대해 말하고, 문제가 무엇인지 조사해야 해요.
다음 환경 변수는 Perl 특정이 아니에요: 그들은 데이터에 대한 응용 프로그램의 견해를 제어하는 표준화된 (ISO C, XPG4, POSIX 1.c) setlocale() 방법의 일부예요. Windows는 비POSIX이지만 Perl은 다음이 어쨌든 설명된 대로 동작하게 마련해요. 환경 변수가 준 로케일이 유효하지 않으면 Perl은 우선순위에서 다음 낮은 것을 시도해요. 그것들 모두 유효하지 않으면 Windows에서 시스템 기본 로케일이 시도돼요. 모든 것이 실패하면 "C" 로케일이 사용돼요. 그것조차 동작하지 않으면 뭔가 심하게 깨진 것이지만, Perl은 로케일 설정이 무엇이든 그것으로 전진하려 애써요.
LC_ALL
LC_ALL은 "전부 덮어쓰는" 로케일 환경 변수예요. 설정되면 나머지 모든 로케일 환경 변수를 덮어써요.
LANGUAGE
참고: LANGUAGE는 GNU 확장으로, GNU libc를 사용하는 경우에만 영향을 줘요. 예를 들어 Linux를 사용한다면 그 경우예요. "상업용" Unix를 사용한다면 대부분 GNU libc를 사용하지 않고 LANGUAGE를 무시할 수 있어요.
하지만 LANGUAGE를 사용하는 경우: 명령이 출력하는 정보, 경고, 오류 메시지의 언어에 영향을 줘요 (다시 말해 LC_MESSAGES 같아요) 하지만 LC_ALL보다 높은 우선순위를 가져요. 게다가 단일 값이 아니라 언어(로케일이 아니라)의 "경로"(":"로 구분된 목록)예요. 자세한 내용은 GNU gettext 라이브러리 문서를 참고하세요.
LC_CTYPE
LC_ALL이 없으면 LC_CTYPE이 문자 유형 로케일을 골라요. LC_ALL과 LC_CTYPE 둘 다 없으면 LANG이 문자 유형 로케일을 골라요.
LC_COLLATE
LC_ALL이 없으면 LC_COLLATE가 조합(정렬) 로케일을 골라요. LC_ALL과 LC_COLLATE 둘 다 없으면 LANG이 조합 로케일을 골라요.
LC_MONETARY
LC_ALL이 없으면 LC_MONETARY가 화폐 형식 로케일을 골라요. LC_ALL과 LC_MONETARY 둘 다 없으면 LANG이 화폐 형식 로케일을 골라요.
LC_NUMERIC
LC_ALL이 없으면 LC_NUMERIC이 숫자 형식 로케일을 골라요. LC_ALL과 LC_NUMERIC 둘 다 없으면 LANG이 숫자 형식을 골라요.
LC_TIME
LC_ALL이 없으면 LC_TIME이 날짜와 시간 형식 로케일을 골라요. LC_ALL과 LC_TIME 둘 다 없으면 LANG이 날짜와 시간 형식 로케일을 골라요.
LANG
LANG은 "catch-all" 로케일 환경 변수예요. 설정되면 전체 LC_ALL과 범주별 LC_*foo* 다음의 마지막 수단으로 사용돼요.
예제 (Examples)
LC_NUMERIC이 숫자 출력을 제어해요:
use locale;
use POSIX qw(locale_h); # Imports setlocale() and the LC_ constants.
setlocale(LC_NUMERIC, "fr_FR") or die "Pardon";
printf "%g\n", 1.23; # If the "fr_FR" succeeded, probably shows 1,23.
그리고 POSIX::strtod()가 문자열을 숫자로 파싱하는 방식도 제어해요:
use locale;
use POSIX qw(locale_h strtod);
setlocale(LC_NUMERIC, "de_DE") or die "Entschuldigung";
my $x = strtod("2,34") + 5;
print $x, "\n"; # Probably shows 7,34.
참고 (NOTES)
문자열 eval과 LC_NUMERIC
문자열 eval은 표현식을 표준 Perl로 파싱해요. 따라서 소수점이 점(dot)이길 기대해요. LC_NUMERIC이 이것을 쉼표로 설정했다면 파싱이 아마도 조용히 혼란스러워질 거예요.
use locale;
use POSIX qw(locale_h);
setlocale(LC_NUMERIC, "fr_FR") or die "Pardon";
my $x = 1.2;
print eval "$x + 1.5";
print "\n";
13,5를 출력해요. 그 로케일에서 쉼표가 소수점 문자이기 때문이에요. eval은 따라서 다음으로 확장돼요:
eval "1,2 + 1.5"
그리고 결과는 아마 기대한 것이 아니에요. 경고는 생성되지 않아요. use locale의 범위 안에서 문자열 eval을 한다면 대신 eval 줄을 다음과 같이 바꿔야 해요:
print eval "no locale; $x + 1.5";
이것은 2.7을 출력해요.
필요 없다면 LC_NUMERIC을 제외할 수도 있어요:
use locale ':!numeric';
하위 호환성 (Backward compatibility)
5.004 이전의 Perl 버전은 대부분 로케일 정보를 무시했고, 일반적으로 프로그램 환경이 달리 암시해도 "C" 로케일과 비슷한 것이 항상 유효한 것처럼 행동했어요 ("setlocale 함수" 참고). 기본적으로 Perl은 하위 호환성을 위해 여전히 이렇게 행동해요. Perl 응용 프로그램이 로케일 정보에 주의를 기울이게 하려면 use locale 프라그마(""use locale" 프라그마" 참고)를 반드시 사용하거나, 오직 패턴 매칭에만 그러고 싶은 드문 경우에는 /l 정규식 수정자(perlre의 "Character set modifiers")를 사용해 그렇게 하라고 지시해야 해요.
5.002에서 5.003까지의 Perl 버전은 사용 가능하면 LC_CTYPE 정보를 사용했어요. 즉 \w가 로케일 환경 변수에 따라 무엇이 문자인지 이해했어요. 문제는 사용자가 그 기능을 제어할 수 없다는 거였어요: C 라이브러리가 로케일을 지원하면 Perl이 그것들을 사용했죠.
I18N::Collate 구식 (I18N:Collate obsolete)
5.004 이전의 Perl 버전에서 로케일별 조합은 I18N::Collate 라이브러리 모듈을 사용해 가능했어요. 이 모듈은 이제 다소 구식이고 새 응용 프로그램에서는 피해야 해요. LC_COLLATE 기능은 이제 Perl 코어 언어에 통합됐어요: use locale로 로케일별 스칼라 데이터를 완전히 정상적으로 사용할 수 있으므로, 더 이상 I18N::Collate의 스칼라 참조를 저글링할 필요가 없어요.
정렬 속도와 메모리 사용 영향 (Sort speed and memory use impacts)
로케일로 비교하고 정렬하는 것은 보통 기본 정렬보다 느려요. 2배에서 4배의 속도 저하가 관찰됐어요. 더 많은 메모리도 소비해요: Perl 스칼라 변수가 로케일 조합 규칙을 따르는 어떤 문자열 비교나 정렬 연산에 한 번이라도 참여하면, 이전보다 3-15배 더 많은 메모리를 차지할 거예요. (정확한 배율은 문자열 내용, 운영체제, 로케일에 달려 있어요.) 이 단점들은 Perl보다 운영체제의 로케일 시스템 구현에 더 좌우돼요.
자유롭게 사용 가능한 로케일 정의 (Freely available locale definitions)
유니코드 CLDR 프로젝트는 많은 로케일의 POSIX 부분을 추출하며, 다음에서 사용 가능해요:
https://unicode.org/Public/cldr/2.0.1/
(CLDR의 최신 버전은 POSIX 데이터를 직접 계산해야 해요. https://unicode.org/Public/cldr/latest/ 참고.)
다음에 큰 로케일 정의 모음이 있어요:
http://std.dkuug.dk/i18n/WG15-collection/locales/
그것은 지원되지 않고 어떤 목적에도 적합하다고 주장되지 않음을 알아 두세요. 시스템이 임의 로케일 설치를 허용한다면 그 정의를 있는 그대로, 또는 자기 로케일 개발의 기초로 유용하게 쓸 수 있어요.
I18n과 l10n
"Internationalization"은 종종 i18n으로 줄여요. 첫 글자와 마지막 글자가 18개의 다른 글자로 분리되기 때문이에요. (마찬가지로 "localization"은 종종 l10n으로 줄여요.)
불완전한 표준 (An imperfect standard)
C와 POSIX 표준에 정의된 국제화는 불완전하고 투박하다고 비판받을 수 있어요. 그들은 또한 표준 그룹처럼 세계를 국가들로 나누는 경향이 있어요. 하지만 우리 모두 세계가 은행가, 자전거 타는 사람, 게이머 등으로도 똑같이 잘 나눌 수 있다는 걸 알죠.
유니코드와 UTF-8 (Unicode and UTF-8)
유니코드 지원은 Perl 버전 v5.6부터 새롭고, v5.8 이후 버전에서 더 완전히 구현됐어요. perluniintro 참고.
Perl v5.20부터 UTF-8 로케일이 Perl에서 지원돼요. 단 LC_COLLATE는 부분적으로만 지원되고, 조합 지원은 Perl v5.26에서 당신의 필요에 충분할 수준으로 개선됐어요 ("범주 LC_COLLATE: 조합/대조: 텍스트 비교와 정렬" 참고).
Perl v5.16이나 v5.18이 있고 업그레이드할 수 없다면 다음을 쓸 수 있어요:
use locale ':not_characters';
이 프라그마 형태가 사용될 때 Perl은 LC_NUMERIC처럼 로케일의 비문자 부분만 사용해요. Perl은 연산할 모든 문자를 유니코드(실제로는 플랫폼 네이티브 문자셋(ASCII나 EBCDIC) 플러스 유니코드)로 번역했다고 가정해요. 파일의 데이터의 경우 다음도 지정해 편리하게 할 수 있어요:
use open ':locale';
이 프라그마는 파일에서의 모든 입력을 환경에 지정된 현재 로케일에서 유니코드로, 모든 파일 출력을 로케일로 다시 번역되게 마련해요. ("환경" 및 open 참고). 파일 핸들별로는 PerlIO::locale 모듈이나 Encode::Locale 모듈을 대신 쓸 수 있고, 둘 다 CPAN에서 사용 가능해요. 후자 모듈은 ARGV와 환경 변수 처리를 쉽게 하는 메서드도 있고, 개별 문자열에 쓸 수 있어요. 모든 로케일이 UTF-8이 될 것을 안다면, 요즘 그런 경우가 많지만, -C 명령줄 스위치를 쓸 수 있어요.
이 프라그마 형태는 유니코드와 함께 로케일을 본질적으로 매끄럽게 처리하게 해요. 조합 순서는 유니코드 코드 포인트 순서가 될 거예요. Unicode::Collate로 유니코드 규칙 조합을 얻을 수 있어요.
방금 설명한 모든 모듈과 스위치는 v5.20에서 평범한 use locale과 함께 사용할 수 있고, 입력 로케일이 UTF-8이 아니면 아래 설명하는 것처럼 이상적이지 않은 동작을 얻을 거예요. v5.16 이전 Perl이나 v5.16·v5.18에서 :not_characters 매개변수 없이 locale 프라그마를 쓸 때 얻는 것요. v5.20 이상에서 독점적으로 UTF-8 로케일을 사용한다면 이 섹션의 나머지는 여러분에게 적용되지 않아요.
두 경우가 있어요, 다중 바이트와 단일 바이트 로케일. 먼저 다중 바이트:
Perl이 지원할 가능성이 있는 유일한 다중 바이트(또는 넓은 문자) 로케일은 UTF-8이에요. 이는 구현의 어려움, 세계 각 지역에 대해 고품질 UTF-8 로케일이 이제 발표된다는 사실(이미 설정된 것은 https://unicode.org/Public/cldr/2.0.1/, 최신이지만 POSIX 정보를 직접 추출해야 하는 것은 https://unicode.org/Public/cldr/latest/), 그리고 그마저도 Encode 모듈로 로케일에서/로 번역할 수 있기 때문이에요. 그래서 Big5나 Shift JIS 같은 로케일을 쓴다면 그 중 하나를 해야 해요. UTF-8 로케일의 경우, 완전한 UTF-8 로케일 지원이 없는 (v5.20 이전) Perl에서, 그것들과 Perl이 여러 바이트를 차지하는 문자를 같은 방식으로 저장하기 때문에 (C 라이브러리 구현에 따라) 합리적으로 잘 동작할 수 있어요. 하지만 일부, 아니 대부분의 C 라이브러리 구현은 LC_CTYPE 아래에서 Latin-1 범위의 상반부(128-255)의 문자를 제대로 처리하지 못할 수 있어요. Perl은 로케일 아래에서 문자가 특정 유형인지 보기 위해 isalnum() 같은 함수를 사용해요. C 라이브러리가 그 함수들로 UTF-8 로케일에서 동작하지 않고, Perl이 사용하지 않는 iswalnum() 같은 최신 넓은 라이브러리 함수 아래에서만 동작할 수 있어요. 이 다중 바이트 로케일은 단일 바이트 로케일처럼 취급되고 아래 설명할 제한이 있을 거예요. Perl v5.22부터 Perl이 완전히 지원하지 않는 다중 바이트 로케일을 감지하면 경고 메시지가 올라와요.
단일 바이트 로케일의 경우 Perl은 일반적으로 단일 바이트에 들어갈 수 있는 코드 포인트에는 로케일 규칙을, 그럴 수 없는 것에는 유니코드 규칙을 사용하는 전략을 취해요 (하지만 균일하게 적용되지는 않아요. 이 섹션 끝의 참고 참고). 이것은 UTF-8이 아닌 로케일에서 많은 문제를 방지해요. 로케일이 ISO8859-7, 그리스어라고 해 보죠. 0xD7의 문자는 대문자 Chi예요. 하지만 ISO8859-1 로케일, Latin1에서는 곱셈 기호예요. POSIX 정규식 문자 클래스 [[:alpha:]]는 그리스 로케일에서 0xD7을 마술처럼 매치하지만 라틴 로케일에서는 아니에요.
하지만 이것이 무너지는 곳이 있어요. 특정 Perl 구성은 유니코드 전용이에요. \p{Alpha} 같은 것요. 그것들은 0xD7이 항상 유니코드 의미(또는 EBCDIC 플랫폼에서 등가물)를 가진다고 가정해요. Latin1은 유니코드의 부분집합이고 0xD7은 Latin1과 유니코드 둘 다에서 곱셈 기호이므로, \p{Alpha}는 로케일과 무관하게 결코 그것을 매치하지 않을 거예요. \N{...}에도 비슷한 문제가 있어요. v5.20 이전에 평범한 use locale 아래에서 \p{}나 \N{}을 쓰는 것은 나쁜 생각이에요—로케일이 ISO8859-1이 될 것을 보장할 수 없다면. 대신 POSIX 문자 클래스를 사용하세요.
이 접근의 또 다른 문제는 단일 바이트/다중 바이트 경계를 가로지르는 연산이 잘 정의되지 않아 금지된다는 거예요. (이 경계는 255/256의 코드 포인트 사이에 있어요.) 예를 들어 LATIN CAPITAL LETTER Y WITH DIAERESIS (U+0178)를 소문자로 바꾸면 LATIN SMALL LETTER Y WITH DIAERESIS (U+00FF)를 반환해야 해요. 하지만 예를 들어 그리스 로케일에서 0xFF에는 문자가 없고, Perl은 0xFF의 문자가 실제로 무엇을 나타내야 하는지 알 방법이 없어요. 따라서 그 연산을 금지해요. 이 모드에서 U+0178의 소문자는 그 자신이에요.
비ISO8859-1, 비UTF-8 로케일에서 표준 파일 핸들의 자동 UTF-8화, 기본 open() 레이어, @ARGV를 활성화하면 ( -C 명령줄 스위치나 PERL_UNICODE 환경 변수를 사용해; perlrun의 -C 참고) 같은 문제가 생겨요. 것들이 UTF-8로 읽히는데, 이는 보통 유니코드 해석을 의미하지만, 로케일의 존재로 인해 그 로케일로 해석되게 해요. 예를 들어 유니코드 입력의 0xD7 코드 포인트, 곱셈 기호를 의미해야 하는 것은 그리스 로케일 아래에서 Perl이 그렇게 해석하지 않을 거예요. 모든 로케일이 항상 그리고 오직 ISO8859-1이거나, 결점 있는 C 라이브러리가 없다면 UTF-8 로케일이도록 확실히 한다면 이건 문제가 아니에요.
또 다른 문제는 이 접근이 같은 문자를 의미하는 두 코드 포인트를 만들 수 있다는 거예요. 따라서 그리스 로케일에서 U+03A7과 U+00D7 둘 다 GREEK CAPITAL LETTER CHI예요.
이 모든 문제 때문에 v5.22부터 Perl은 단일 바이트 로케일이 유효할 때 다중 바이트(따라서 유니코드) 코드 포인트가 사용되면 경고를 올려요. (단, 그렇게 하는 것이 실행을 비합리적으로 느리게 한다면 이것을 확인하지 않아요.)
벤더 로케일은 악명 높게 버그가 있고, Perl이 통제할 수 없는 코드와 상호작용하기 때문에 Perl이 자기 로케일 처리 코드를 테스트하기 어려워요. 따라서 Perl의 로케일 처리 코드도 버그가 있을 수 있어요. (하지만 유니코드 제공 로케일이 더 나아야 하고, 문제를 수정하는 피드백 메커니즘이 있어요. "자유롭게 사용 가능한 로케일 정의" 참고.)
Perl v5.16이 있다면, 위에서 언급한 문제는 locale 프라그마에 :not_characters 매개변수를 사용하면 (비문자 부분의 벤더 버그를 제외하고) 사라져요. v5.16이 없고 작동하는 로케일이 있다면, 이미 언급한 함정을 염두에 두는 한, 특정 목적에 그것을 사용하는 것이 가치 있을 수 있어요. 예를 들어 로케일의 조합이 동작한다면 Unicode::Collate 아래보다 로케일 아래에서 더 빠르게 실행돼요. 그리고 로컬 통화 기호, 월·요일 이름 같은 것에 접근할 수 있어요. (하지만 다시 강조하자면, v5.16에서 :not_characters 형태의 프라그마를 사용하면 로케일의 단점 없이 이 접근을 얻어요.)
참고: 한 바이트에 들어갈 수 있는 코드 포인트에는 로케일 규칙, 그럴 수 없는 것에는 유니코드 규칙을 사용한다는 정책은 균일하게 적용되지 않아요. v5.12 이전에는 다소 엉터리였어요. v5.12에서는 괄호 문자 클래스를 제외하고 정규식 매칭에 상당히 일관되게 적용됐고, v5.14에서는 모든 regex 매치로 확장됐으며, v5.16에서는 \L과 uc() 같은 대소문자 연산으로 확장됐어요. 조합의 경우 지금까지 모든 릴리즈에서 시스템의 strxfrm() 함수가 호출되고, 그것이 하는 것이 무엇이든 얻는 것이에요. v5.26부터 Perl이 이 함수를 사용하는 방식의 다양한 버그가 수정됐어요.
버그 (BUGS)
내장 NUL 문자를 포함하는 문자열의 조합 (Collation of strings containing embedded NUL characters)
NUL 문자는 가장 낮은 조합 제어 문자와 같이 정렬될 거예요. 로케일에 제어 문자가 전혀 없을 드문 경우 "\001"와 같이요. 문자열이 이 비NUL 제어 문자를 포함하지 않는 경우 결과는 정확할 것이고, 많은 로케일에서 이 제어 문자는 무엇이든 간에 드물게 마주칠 거예요. 하지만 NUL이 이 제어 문자보다 먼저 정렬되어야 하는데 그렇지 않은 경우가 있어요. 두 문자열이 동일하게 조합되면 NUL을 포함하는 것이 더 일찍 정렬돼요. 5.26 이전에는 버그가 더 많았어요.
LANGUAGE
위에서 말했듯이 Perl은 이 환경 변수를 무시해요.
임베디드 perl과 다중 스레드 (Embedded perls and multi-threaded)
${^SAFE_LOCALES}가 0인 플랫폼에서는 시작 후 로케일을 바꾸지 말아야 해요. 비스레드 플랫폼에서는 항상 1일 거예요.
XS 작성자는 perlclib의 "Dealing with embedded perls and threads"를 참고해야 해요.
깨진 시스템 (Broken systems)
남아 있는 몇몇 시스템에서 운영체제의 로케일 지원이 깨져 있고 Perl이 고치거나 사용할 수 없어요. 그런 결함은 use locale이 유효할 때 신비한 멈춤 및/또는 Perl core dump를 초래할 수 있고 실제로 그래요. 그런 시스템을 만나면 https://github.com/Perl/perl5/issues에 극도로 상세히 보고하고, 벤더에도 연락하세요: 운영체제에 이 문제에 대한 버그 수정이 있을 수 있어요. 때로 그런 버그 수정을 운영체제 업그레이드라고 불러요. Perl 소스가 있다면 버그 리포트에 위 "깨진 로케일 테스트"에서 설명한 테스트의 출력을 포함하세요.
더 보기 (SEE ALSO)
I18N::Langinfo, perluniintro, perlunicode, open, POSIX의 "localeconv", POSIX의 "setlocale", POSIX의 "strcoll", POSIX의 "strftime", POSIX의 "strtod", POSIX의 "strxfrm".
Perl이 C 프로그램에 임베디드될 때의 특별한 고려 사항은 perlembed의 "Using embedded Perl with POSIX locales"를 참고하세요.
역사 (HISTORY)
Jarkko Hietaniemi의 원래 perli18n.pod가 Dominic Dunlop에 의해 크게 해킹되고 perl5-porters가 도왔어요. 산문은 Tom Christiansen이 약간 다듬었고, 이제 Perl 5 porters가 관리해요.