워드프레스 번역의 번역, gettext, gettext_with_context

워드프레스가 소프트웨어의 지역화(L10N, Localization) 또는 국제화(I18N, Internationalization) 시스템(체계)을 도입(지원)하고 있으므로 번역 공헌자가 번역한 파일을 사용하여 원하는 언어의 워드프레스 버전을 사용할 수 있습니다. (이하 지역화, 국제화를 ‘지역화’로 표기. 개념적으로 지역화 표기가 적절하다 생각) 이러한 지역화 시스템을 위해 워드프레스는 gettext PHP 라이브러리를 사용합니다. 워드프레스만 사용하는 것이 아니라 다양한 종류의 소프트웨어에서도 사용하고 있는 오픈 소스입니다.

지역화 시스템에 의해 우리나라 사용자들은 대부분 한글 버전의 워드프레스를 설치하여 블로그나 웹사이트를 운영하고 있지만, 번역 결과가 모두를 만족하게 할 수는 없습니다. 많은 사람이 이해하기 어려운 번역 구문 등은 개선하면 되지만, 다른 나라 문화 기준의 소프트웨어이므로 번역의 기본적 한계가 존재하는 것은 이해해야 합니다. 때로는 구성하는 웹사이트의 특징에 따라 조금씩 다른 번역이 필요할 수도 있으며, 커뮤니티처럼 불특정 다수의 회원이 존재한다면 조금 더 이해하기 쉬운 문장을 표현해야 할 때도 있습니다. 자신이 웹사이트 제작업을 한다면 일부 번역에 대한 고객의 요청을 수용해야 할 경우도 생깁니다.

이때 워드프레스 코어의 번역 파일이나 테마, 플러그인의 번역 파일을 직접 수정하여 원하는 결과를 얻는 과정은 조금은 어렵고 번거롭습니다. 업데이트 후 발생하는 초기화 문제도 염려해야 합니다. 3개의 번역 구문을 변경하고 싶은데 번역 파일을 직접 편집해야 한다면 효율도 얻을 수 없습니다. 이처럼 내가 또는 특정 사용자가 변경을 원하는 번역 구문은 일부 특정 요소에 한정된 것이 일반적인 것으로 생각합니다. 이때 번역 파일을 다루지 않고 워드프레스 필터 훅을 사용하여 간단하게 원하는 구문을 출력할 수 있습니다. 주의할 사항도 있습니다.

운영하는 사이트에 기능적 문제는 발생하지 않으므로 워드프레스 한글 버전을 사용하고 있다면 따라 해보세요.

gettext

gettext 필터 훅은 워드프레스, 테마, 플러그인이 __(), _e() 함수를 사용하여 지역화를 지원했을 때 사용할 수 있습니다. 함수에 대한 정의는 코덱스의 도움을 얻으면 되는데 이 글에서는 번역 텍스트를 반환 또는 출력하는 함수 정도로 이해하면 됩니다. 먼저 말하면 특정 함수를 사용했을 때 특정 훅을 사용한다는 것은 기본 정의에 불과하며, 어떤 번역 텍스트가 어떤 함수를 사용했는지 찾는 것보다 훅을 적용했을 때 원하는 결과를 얻을 수 있는지 바로 확인하는 것이 좋습니다.

코덱스에서는 싱글 포스트 페이지 댓글 폼의 필드 이름(label)을 변경하여 출력하는 예시를 안내하고 있는데, 글에서는 한글 버전의 번역 구문을 변경하는 예를 PHP 함수(str_replace)와 함께 사용하여 알아봅니다. 누구나 사이트 가입이 가능하도록 설정했다면 다음 그림처럼 로그인 페이지가 나올 것입니다.

로그인 페이지
재번역 전 로그인 페이지

로그인 페이지에 나오는 ‘비밀번호’를 ‘암호’로 대체하기 위해 먼저 코덱스 예시를 기준으로 다음 코드를 사용할 수 있습니다. 사용하는 테마의 functions.php 파일에 추가합니다. 코드에서 theme_text_domain 즉, 플러그인 또는 테마의 text domain은 일치하지 않아도 (로드되지 않아도) 적용되며, 글에서는 고려하지 않기로 합니다.

add_filter( 'gettext', 'theme_change_comment_field_names', 20, 3 );
function theme_change_comment_field_names( $translated_text, $text, $domain ) {

    switch ( $translated_text ) {
        case '비밀번호' :
        $translated_text = __( '암호', 'theme_text_domain' );
        break;
    }

    return $translated_text;
}

위의 코드 추가 결과는 다음 그림처럼 ‘비밀번호’ 텍스트와 정확히 같은(match) 경우에만 변경합니다.

'비밀번호' 텍스트와 정확히 같은(match) 경우에만 변경
‘비밀번호’ 텍스트와 정확히 같은(match) 경우에만 변경

이번에는 PHP str_replace 함수와 조합하여 적용해 봅니다. 앞의 코드를 모두 지우고 다음 코드를 추가합니다.

add_filter( 'gettext', 'my_retranslation' );
function my_retranslation( $translated ) {
    $translated = str_replace( '비밀번호', '암호', $translated );
    return $translated;
}

로그인 페이지를 확인하면 다음 그림을 볼 수 있습니다. 두 군데가 변경되었습니다.

재번역 후 로그인 페이지
재번역 후 로그인 페이지

위의 코드는 실제로 사이트 전역에 걸쳐 ‘비밀번호’ 텍스트를 모두 ‘암호’로 변경합니다. 위 코드를 추가한 상태에서 로그인 후 관리페이지 사용자 > 나의 프로필 페이지를 보면 ‘새 비밀번호’가 ‘새 암호’로, ‘비밀번호 생성’ 버튼 텍스트가 ‘암호 생성’으로 변경된 것을 볼 수 있습니다. 또 암호를 변경하는 워드프레스 기본 페이지의 ‘새 비밀번호 얻기’ 버튼의 텍스트도 ‘새 암호 얻기’로 변경됩니다. 그 외에도 많습니다.

‘비밀번호’ 텍스트가 한 단어든 문장에 속한 단어든 모두 변경되어도 이해하는 데 문제가 없고, 조사도 틀리지 않으므로 사이트 전역에 걸쳐 변경해도 문제가 없는 경우입니다. 이때 변경된 특정 부분이 __() 또는 _e() 함수 중 어떤 것을 사용했는지, 뒤에 안내할 _x() 함수를 사용했는지 모르며 알 필요도 없습니다. 직접 적용하여 결과를 확인하고 결정하는 것이 현실적입니다. 원하지 않는 부분이 변경되는 것을 염려한다면 조건 태그를 사용하여 적용 범위를 제한할 수 있지만, 글에서 안내하지 않습니다.

재번역의 경우 PHP 함수와 조합하여 적용한 직전의 경우처럼 보다 간편하게 처리하는 것이 글의 방향이므로 이후 내용은 str_replace 함수를 함께 사용하는 기준에서 이어갑니다.

이번에는 ‘비밀번호’를 ‘암호’로 변경하기 전이라는 가정에서 ‘비밀번호’를 ‘암호’로 변경하고, ‘비밀번호를 잃어버렸나요?’도 ‘암호를 잊었습니다.‘로 변경하기 위해 이전 코드를 모두 지우고 다음 코드로 변경합니다.

add_filter( 'gettext', 'my_retranslation' );
function my_retranslation( $translated ) {
    $translated = str_replace( '비밀번호', '암호', $translated );
    $translated = str_replace( '비밀번호를 잃어버렸나요?', '암호를 잊었습니다.', $translated );
    return $translated;
}

결과는 ‘비밀번호’ 단어 하나는 ‘암호’로 변경되지만, ‘비밀번호를 잃어버렸나요?’는 ‘암호를 잃어버렸나요?’로 변경될 것입니다. 따라서 다음 코드로 변경하면 원하는 결과를 얻을 수 있다는 것을 감각적인 분이라면 이미 알아챘을 것입니다.

add_filter( 'gettext', 'my_retranslation' );
function my_retranslation( $translated ) {
    $translated = str_replace( '비밀번호', '암호', $translated );
    $translated = str_replace( '암호를 잃어버렸나요?', '암호를 잊었습니다.', $translated );
    return $translated;
}

회원가입이 가능한 사이트에서는 가입자를 위해 조금 더 상세한 안내가 필요합니다. 특히 가입 후나 암호를 변경할 때 수신 메일의 구문들이 직관적이지 않아 워드프레스 시스템 사용의 경험이 없는 가입자는 간단한 과정이지만 쉽게 처리하지 못하는 경우가 제법 있습니다. gettext 훅은 수신 메일의 번역 구문도 변경된 것으로 출력합니다.

워드프레스 사이트에 가입 후 가입자는 등록한 메일로 사용자명과 암호 변경 링크가 있는 메일을 받는데 메일 내용에 ‘비밀번호 설정하시려면, 다음 링크를 방문하십시오:’라는 구문이 나옵니다. 앞의 코드를 추가한 상태 기준으로 보면 ‘비밀번호’가 ‘암호’로 변경되었으므로 ‘암호 설정하시려면, 다음 링크를 방문하십시오:’ 구문이 나올 것입니다.

이 구문을 ‘암호 등록을 위해 아래 링크를 꼭 방문하십시오. 만약 화면에 링크가 나오지 않는다면 메일 “원문보기” 기능으로 소스를 확인해야 합니다.‘, 라는 구문으로 변경하는데, 아래 코드를 사용할 수 있습니다.

(보유한 여유 메일 계정이 있다면 그 메일을 사용하면 되고, Gmail을 사용하고 myid@gmail.com 메일 주소가 있다면 myid+01@gmail.com 유형으로 다른 메일 계정이 없어도 간단하게 확인할 수 있습니다.)

add_filter( 'gettext', 'my_retranslation' );
function my_retranslation( $translated ) {
    $translated = str_replace( '비밀번호', '암호', $translated );
    $translated = str_replace( '암호를 잃어버렸나요?', '암호를 잊었습니다.', $translated );
    $translated = str_replace( '암호 설정하시려면, 다음 링크를 방문하십시오:', '암호 등록을 위해 아래 링크를 꼭 방문하십시오. 만약 화면에 링크가 나오지 않는다면 메일 "원문보기" 기능으로 소스를 확인해야 합니다.', $translated );
    return $translated;
}

나머지 워드프레스 기본 페이지의 번역 구문, 테마나 플러그인의 특정 구문도 추가하여 원하는 것으로 변경하면 됩니다.

gettext_with_context

gettext_with_context 필터 훅은 _x() 함수를 사용하여 지역화를 지원했을 때 사용할 수 있다고 정의하고 있습니다. 이 함수는 문맥에 따른 번역 함수 정도로 이해하면 됩니다. 예를 들어, 워드프레스에서 ‘글’이라는 것은 기본 포스트 타입 ‘post’의 한글 번역 명칭이지만, 누구나 알고 이해하는 ‘글’ 그 자체의 뜻으로도 워드프레스에서 사용되므로(될 수 있으므로) 지역화를 고려할 때 _x() 함수를 사용하는 것이 적절하다는 것 정도로 생각하면 충분합니다.

먼저 관리페이지에 접근하여 메뉴에서 ‘글’을 클릭합니다. gettext_with_context 훅을 사용한 다음 코드를 functions.php 파일에 새로 추가하고 ‘글’ 메뉴를 클릭하면 ‘글’ 메뉴 텍스트와 목록의 ‘글’이 ‘포스트’로 변경된 것을 확인할 수 있습니다. ‘모든 글’은 변화가 없습니다.

add_filter( 'gettext_with_context', 'theme_change_comment_field_names', 20, 3 );
function theme_change_comment_field_names( $translated_text, $text, $domain ) {
    switch ( $translated_text ) {
        case '글' :
        $translated_text = __( '포스트', 'theme_text_domain' );
        break;
        case '모든 글' :
        $translated_text = __( '포스트', 'theme_text_domain' );
        break;
    }
    return $translated_text;
}

다음 그림은 위의 코드 추가 결과입니다.

'글' 메뉴 텍스트와 목록의 '글'이 '포스트'로 변경
‘글’ 메뉴 텍스트와 목록의 ‘글’이 ‘포스트’로 변경

이번에는 gettext 훅만 변경한 아래 코드로 변경하고 관리페이지를 다시 확인합니다.

add_filter( 'gettext', 'theme_change_comment_field_names', 20, 3 );
function theme_change_comment_field_names( $translated_text, $text, $domain ) {
    switch ( $translated_text ) {
        case '글' :
        $translated_text = __( '포스트', 'theme_text_domain' );
        break;
        case '모든 글' :
        $translated_text = __( '포스트', 'theme_text_domain' );
        break;
    }
    return $translated_text;
}

다음 그림은 훅만 변경한 위의 코드 결과입니다. ‘모든 글’ 텍스트만 ‘포스트’로 변경된 것을 볼 수 있습니다.

관리페이지 메뉴 변경에 gettext 훅을 사용한 경우
관리페이지 메뉴 변경에 gettext 훅을 사용한 경우

2가지 결과를 통해 지역화에 사용한 함수가 __()_e()인지 _x()인지 가늠할 수 있습니다. 어떤 함수를 사용했는지 확인하는 것이 아니라 훅을 변경하여 원하는 결과를 얻는 데 초점을 맞추면 됩니다.

이번에는 str_replace PHP 함수와 조합하여 결과를 확인합니다. 다음 그림은 워드프레스 한글 버전의 관리페이지에 나오는 ‘글’이 포함된 일부 요소입니다.

워드프레스 한글 버전 관리페이지
워드프레스 한글 버전 관리페이지

‘글’을 ‘포스트’로 변경하기 위해 우선 다음 코드를 사용해 봅니다. 직전의 코드는 모두 지웁니다.

add_filter( 'gettext', 'my_retranslation' );
function my_retranslation( $translated ) {
    $translated = str_replace( '글', '포스트', $translated );
    return $translated;
}

결과는 다음 그림처럼 나타나는데 이 방법이 아닌 것으로 판단하고 다른 방법을 찾는 것이 좋습니다. 이 코드는 지웁니다.

재번역 후 관리페이지
재번역 후 관리페이지

아래 gettext_with_context 훅을 사용한 코드를 새로 추가한 후 확인하면, 단어나 구문에 ‘글’이 들어간 것은 모두 ‘포스트’로 변경됩니다.

add_filter( 'gettext_with_context', 'my_retranslation2' );
function my_retranslation2( $translated ) {
    $translated = str_replace( '글', '포스트', $translated );
    return $translated;
}

gettext_with_context 훅을 사용한 후 어느 정도 원하는 결과를 얻을 수 있지만, ‘댓글’이 ‘댓포스트’로 변경되는 문제가 있으므로 이 역시 한계가 있습니다. 사이트 전체에 걸쳐 문제가 되는 곳을 찾으면 더 많이 있습니다.

살펴 본 예는 관리페이지 메뉴가 포함된 것인데, 실제로 관리페이지 기본 메뉴, 기본 포스트 타입 및 편집 페이지 여러 요소의 이름(label)은 글에서 소개하는 gettext, gettext_with_context 훅보다 admin_menu, init 훅 등을 사용하여 처리하는 것이 일반적입니다. 물론 적절한 PHP 함수의 조합으로 특정 요소만 변경하는 것은 좋습니다.

요약

글에서 안내한 번역 결과 변경은 기본 포스트 타입 관련, 기본 제공 메뉴를 제외하고, 단어보다는 문장의 변경에 효과적일 수 있습니다. 문장은 똑같은 경우가 드물며, 문장이 같은 경우는 문제가 될 수 없으므로 염려하지 않아도 됩니다. 물론 검토는 필요합니다. 단점은 번역 파일이 업데이트되면 정의한 내용이 적용되지 않는다는 것인데, 사이트 운영에 문제는 없습니다. 적절한 수의 재번역으로 간편하게 사용할 때 PHP 함수의 조합으로 사용한다면 효율적입니다.

댓글 4

  1. 번역의 번역 <= 작명 센스가 너무 좋으세요. ㅎㅎ

    정말 눈이 번쩍 떠지는 희소식인거 같습니다. 이렇게 좋은 정보를 자세한 설명, 예제와 곁들여서 포스팅 해주시니 너무 감사합니다.

    저는 완전 베테랑님 팬입니다. ^^*

    1. 반갑습니다. 포스트 제목은 고민하지 않고 피싱형(^^) 지양과 평면적 작성을 선호하는데 센스있다니 저보다 랭커님이 센스 가득한 거 같네요.

      포스트와 비슷한 정보는 인터넷에 널려 있습니다. 희소식이라기엔 좀 낡았습니다.

      구성하시는 사이트는 잘 진행되는지 궁금하네요.

    2. 한참 전에 다 만들었는데 공개하는 것은 포기했어요.

      일단 너무 부족하기도 하고요, 제가 운영할 자신이 없어서요. ㅜㅠ

      그냥 간단한 포폴 사이트나 1개 만들어야 겠어요. 영업을 위해서요. ^^;;;