이 문서에서 소개하는 다국어지원 (NLS) 기능은 도쿠위키의 내장 기능이나 정식 플러그인이 아니라 2005-09-22e 버전을 제가 나름대로 수정하여 구현한 비공식 기능입니다. 어쩌면 도쿠위키의 이후 버전에서는 지금 이 문서에서 소개하는 기능과는 별개로 자체적인 다국어지원 기능이 공식적으로 내장되어 들어갈지도 모릅니다. 도쿠위키의 다국어지원 기능에 관한 더 많은 정보는 Multilingual sites with DokuWiki와 Browser Language Detection을 참조하시기 바랍니다.
이 문서에서 소개하는 기법은 다음과 같은 상황에서 사용할 수 있습니다.
en.wikipedia.org나 ko.wikipedia.org를 사용할 수 없는 경우입니다.foo는 A, B, C 세 가지 언어로 번역되는 반면 다른 문서 bar는 B, C, D, E 네 가지 언어로 번역되고, 또다른 문서 baz는 오직 C 언어로만 제공될 수도 있습니다.이 문서에서 소개하는 기법은 다음과 같이 동작합니다.
이 문서에서 소개하는 기법을 사용하기 위해서는 다음과 같은 사항들을 먼저 만족시켜야만 합니다.2)
참고로, 번역지표란 문서 ID의 마지막 부분에 추가되는 언어 코드이며 그 문서가 어떤 언어로 번역된 번역본인지를 나타냅니다. 도쿠위키가 기본적으로 규정하고있는 적법한 문서 (이름공간을 포함한) ID 형식을 pageID라고 하면, 번역지표가 포함된 문서 ID는 정규식으로
pageID\.[a-z][a-z](-[a-z][a-z])?
입니다. 예를 들어 foo:bar.ko는 한국어 번역판이고 foo:bar.en은 같은 내용의 문서에 대한 영어 번역판입니다.
foo:bar라는 ID에는 번역지표가 들어있지 않으며, NLS가 적용된 이후 방문자가 이런 문서를 요청하면 브라우저 언어 설정에 따라 적절한 번역판으로 자동 이동됩니다. 가능한 한 존재하는 번역판들 중에서 가장 적절한 것을 선택하여 이동하지만, 만일 브라우저 언어 설정에 부합하는 번역판이 존재하지 않더라도 무조건 (없는 번역판으로라도) 이동됩니다. 즉, 방문자는 이제 foo:bar라는 문서를 더이상 읽지 못하게된다는 뜻이며, 따라서 사이트 관리자는 NLS를 적용하기 전에 미리 모든 문서의 ID에 적절한 번역지표를 달아두어야 합니다.
기존 문서들의 ID에 번역지표를 붙이기 위해서 제가 사용했고 추천하는 방법은:
foo:bar의 내용이 어떤 언어로 쓰여있는지 확인합니다. → zz라는 언어로 쓰여있다고 가정합니다.foo:bar.zz를 만들고 foo:bar의 내용을 그대로 복사합니다.foo:bar를 삭제합니다.매우 단순 무식한 방법이지만, 저는 도쿠위키에서 기존 문서의 ID를 깨끗하게 변경하는 더 좋은 방법을 알지 못합니다.
알고리즘은 무척 단순합니다.
foo:bar) 인덱싱 중이 아니라면ko)에 대한 번역판이 존재하면 그 번역판(foo:bar.ko)으로 자동 이동시킵니다.foo:bar.{$conf[’lang’]})으로 (그러 번역판이 있든 없든 무조건) 자동 이동시킵니다.DOKU_INC/inc/lang/*를 비교하여 적절한 UI 언어를 자동 선정합니다. (매치되는 것이 없으면 역시 $conf[’lang’]을 따릅니다.)$conf[’lang’]을 위에서 선정한 UI 언어로 덧씌웁니다.$lang 배열을 다시 읽어들입니다.
이상입니다. 간단하죠?
다시 한 번 당부드립니다만, 일단 기존의 모든 문서들이 그 ID에 적절한 번역지표를 갖도록 조치해두셔야합니다.
준비가 되셨으면, 아래 코드를 담고 있는 inc/NLS.php 파일을 생성합니다 (초반부의 $NLS_locarr 배열과 $NLS_langname 배열은 사이트 관리자가 적절히 설정하시면 됩니다):
<?php /** * National Language Support (NLS) script for DokuWiki * * @version 2005-09-22e-1.1.0 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) * @author CHA Reeseo <http://www.reeseo.net/> * * Usage: * Include this script into 'doku.php', * between the inclusions of 'inc/pageutils.php' and 'inc/html.php' * * CAUTION: * This script demands a special policy that EVERY page should denote * its own language (.xx or .xx-xx) at the end of its ID. * For example, 'foo:bar.ko' is a page translated into Korean * and 'foo:bar.en' is a page containing the same content in English. * * Quering any page having ID without explicit language notation, * you will be redirected to its appropriate 'localized' page. * ('Unlocalized' page will be invisible.) * * Please rename all the pre-existing pages before applying this script. * * To do: * - Upgrade NLS_locale function */ /* --------------------------------------------------------------- * Configuration options: You can modify or add something to these */ $NLS_locarr['ko'] = 'ko_KR'; $NLS_locarr['en'] = 'en_US'; $NLS_langname['ko'] = '한국어'; $NLS_langname['en'] = 'English'; /* Configuration options end * --------------------------------------------------------------- */ // Setting $ID $ID = getID(); // Redirecting if no language is specified at the end of $ID if (! NLS_pagelang($ID)) { $target = NLS_page4browser($ID); if (! array_key_exists("idx", $_GET) && $_GET['do'] != 'recent') // No redirection when indexing header('Location: ' . wl($target)); } // Resetting $conf['lang'] according to the language setting of user's browser $conf['lang'] = NLS_UI4browser(); // Resetting $lang array @require_once(DOKU_INC.'inc/lang/'.$conf['lang'].'/lang.php'); // Resetting locale setlocale(LC_ALL, NLS_locale($conf['lang'])); /** * Getting locale string * * FIXME: What a poor function this is! */ function NLS_locale($ln = NULL) { global $conf; global $lang; global $NLS_locarr; if (! $ln) $ln = $conf['lang']; $loc = array_key_exists($ln, $NLS_locarr) ? $NLS_locarr[$ln] : $ln; $loc .= '.'; $loc .= array_key_exists('encoding', $lang) ? strtoupper($lang['encoding']) : 'UTF-8'; return $loc; } /** * Printing links to other translations of the given page * (API for template files such as DOKU_TPL/main.php */ function NLS_transmenu($pid = NULL, $delimiter = ",\n", $withself = FALSE) { global $NLS_langname; if (! $pid) { global $ID; $pid = $ID; } $currplang = NLS_pagelang($pid); $tpages = NLS_transpages($pid); if ($currplang && ! $withself) unset($tpages[$currplang]); if ($tpages) { $first = TRUE; foreach ($tpages as $ln => $tid) { $repr = array_key_exists($ln, $NLS_langname) ? $NLS_langname[$ln] : $ln; if (! $first) echo $delimiter; $first = FALSE; if ($currplang == $ln) echo "<em>$repr</em>"; else echo "<a href=\"".wl($tid)."\">$repr</a>"; } } else { echo "None."; } } /** * Selecting a redirection target (page) which best match the browser setting * * Default: page of $conf['lang'] (whether it exists or not) * Choice : among existing translations, highest priority for the browser */ function NLS_page4browser($pid = NULL) { if (! $pid) { global $ID; $pid = $ID; } $blang = NLS_browserlang(); $existing_pages = NLS_transpages($pid); $pid_base = NLS_ID_base($pid); $tmp_page = $pid_base . '.' . $conf['lang']; // default page foreach ($blang as $lang_str => $priority) { $lang_str = str_replace("_", "-", strtolower($lang_str)); if (array_key_exists($lang_str, $existing_pages)) $tmp_page = $pid_base . '.' . $lang_str; elseif (array_key_exists(substr($lang_str, 0, 2), $existing_pages)) $tmp_page = $pid_base . '.' . substr($lang_str, 0, 2); } return $tmp_page; } /** * Selecting a language for UI * * Default: $conf['lang'] * Choice : among existing 'inc/lang/*', highest priority for the browser */ function NLS_UI4browser() { global $conf; $tmp_lang = $conf['lang']; // This ($conf['lang']) is the default! $blang = NLS_browserlang(); foreach ($blang as $lang_str => $priority) { $lang_str = str_replace("_", "-", strtolower($lang_str)); if (is_dir(DOKU_INC . "inc/lang/" . $lang_str)) $tmp_lang = $lang_str; elseif (is_dir(DOKU_INC . "inc/lang/" . substr($lang_str, 0, 2))) $tmp_lang = substr($lang_str, 0, 2); } return $tmp_lang; } /** * Getting a sorted (by priority) array of the languages * from the language setting of the user's web browser */ function NLS_browserlang() { $acclang_arr = split(" *, *", trim($_SERVER['HTTP_ACCEPT_LANGUAGE'])); foreach ($acclang_arr as $acclang) { if (ereg("^(.+) *;.+= *(.+)$", $acclang, $acclang_parts)) $acclang_sorted[$acclang_parts[1]] = (double)($acclang_parts[2]); else $acclang_sorted[$acclang] = 1.0; } asort($acclang_sorted, SORT_NUMERIC); reset($acclang_sorted); return $acclang_sorted; } /** * Getting an associative array (lang => ID) of * all the translated pages for the given page */ function NLS_transpages($pid = NULL) { global $conf; if (! $pid) { global $ID; $pid = $ID; } $transpages = array(); $pid_base_path = $conf['datadir'] . '/' . str_replace(":", "/", NLS_ID_base($pid)); foreach(glob($pid_base_path . ".*.txt") as $fn) { $aid = str_replace("/", ":", substr($fn, strlen($conf['datadir']) + 1, -4)); if ($alang = NLS_pagelang($aid)) { $transpages[$alang] = $aid; } } return $transpages; } /** * Drop language notation (.xx or .xx-xx) from the given ID */ function NLS_ID_base($pid = NULL) { if (! $pid) { global $ID; $pid = $ID; } if (substr($pid, -3, 1) == '.') return substr($pid, 0, -3); elseif (substr($pid, -6, 1) == '.' && substr($pid, -3, 1) == '-') return substr($pid, 0, -6); else return $pid; } /** * Get language (xx, xx-xx, or NULL) from the given ID */ function NLS_pagelang($pid = NULL) { if (! $pid) { global $ID; $pid = $ID; } if (substr($pid, -3, 1) == '.') return substr($pid, -2); elseif (substr($pid, -6, 1) == '.' && substr($pid, -3, 1) == '-') return substr($pid, -5); else return NULL; } ?>
이제 doku.php 파일의 초반부에 inc/NLS.php 파일을 불러오는 부분을 추가하는데, 이 위치가 무척 중요합니다: NLS 스크립트는 가능한 한 일찍 로딩되는 것이 좋지만, inc/pageutils.php 파일에 들어있는 기능을 꼭 써야하므로 이보다는 뒤에 들어와야합니다. 따라서 아래 코드처럼 inc/pageutils.php 바로 아래에 추가하십시오 (이 코드는 중반부 이하가 생략되어있습니다):
<?php /** * DokuWiki mainscript * * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) * @author Andreas Gohr <andi@splitbrain.org> */ // xdebug_start_profiling(); if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__)).'/'); require_once(DOKU_INC.'inc/init.php'); require_once(DOKU_INC.'inc/common.php'); require_once(DOKU_INC.'inc/pageutils.php'); require_once(DOKU_INC.'inc/NLS.php'); // 바로 이 줄을 추가합니다! require_once(DOKU_INC.'inc/html.php'); require_once(DOKU_INC.'inc/auth.php'); require_once(DOKU_INC.'inc/actions.php'); //import variables ...
마지막으로, 사용자가 어떤 문서를 읽던 도중에 그 문서의 다른 언어 번역판을 선택할 수 있도록 ‘번역판 선택 메뉴’를 추가하겠습니다. 현재 사용중인 템플릿의 main.php 파일 정도에 간단히 다음과 같은 부분을 추가하기만 하면 끝입니다:
Other translations of this page: <?php NLS_transmenu(); ?>
참고로, NLS_transmenu() 함수는 템플릿에 번역판 선택 메뉴를 추가시켜주는 API로서 다음과 같은 매개변수들이 정의되어있습니다:
NLS_transmenu($pid, $delimiter, $withself)
각각을 간단히 부연하면:
$pid$ID (즉, 현재 문서)입니다.$delimiter",\n"입니다.$withselfTrue면 나열되는 번역판 메뉴에 $pid 문서 자체의 언어도 함께 나타나고, False면 $pid 문서 자체의 언어는 메뉴에 포함되지 않습니다. 기본값은 False입니다.
NLS_locale() 함수의 처리 방식은 한마디로 “It sucks!” 입니다. 이 사이트는 개인 웹사이트이기 때문에 방문객들이 문서를 만들거나 고치는 것을 허용하지 않습니다. 조언하실 내용이 있으시면 도쿠위키 사이트의 팁 페이지를 이용해주시기 바랍니다.