계정: 로그인
AA 📝
Native Language Support

(!) 이 문서는 한국어판도 있습니다.

Obsolete Document

This document describes my works done from late 2005 to early 2006 in order to implement NLS into DokuWiki which had been adopted in Reeseo.Net. (For now, DokuWiki itself provides NLS in a better way.)

The Native Language Support (NLS) feature introduced in this document is not a built-in functionality nor an official plugin of DokuWiki, but an unofficial patch applied to the 2005-09-22e version. A future version of DokuWiki may officially ship another NLS feature which is irrelevant to one described in this document. Please consult Multilingual sites with DokuWiki and Browser Language Detection for more information about NLS in DokuWiki.

Suitable Situation

The technique introduced in this document is suitable for situations such that:

Features (as visitors)

The technique in this page works as follows:

Prerequisites (as administrators and users)

In order to use the technique introduced in this document, the following conditions are required:2

The 'translation flag' is a kind of language code that is appended at the end of page ID, which denotes in what language it is translated. When pageID is the valid ID format (including namespaces) permitted by DokuWiki, the new page ID with a translation flag should be

in regular expression. For example, foo:bar.ko is a Korean translation whereas foo:bar.en is an English translation for the same content.

Because the page ID foo:bar contains no translation flag, readers will be redirected to an appropriate translation pages according to their browser setting if they query this kind of page. Though they will be redirected basically to the most appropriate one among existing translations as far as possible, they will be redirected to a non-existing page if no existing translation matches to their browser's setting. In other words, readers will not be able to read foo:bar any more, therefore the site administrator should append translation flags to all the pages in his/her site before using this technique.

A method I used and recommend for appending translation flags to pre-existings page is:

  1. Check in what language a pre-existing page foo:bar is written. (Let's assume that this page is written in zz language.)

  2. Create a new page foo:bar.zz copying the content of foo:bar verbatim.

  3. Modify links ID's of all the internal links in the copied content appropiately.
  4. Delete foo:bar.

This procedure, of course, is very tedious, but I don't know better way to change page ID's in DokuWiki.

Algorithm (as developers)

The algorithm is very simple:

  1. A reader query a page.
  2. If the queried page has no translation flag (e.g. foo:bar) and the reader is not doing indexing

    1. Extract languages from the browser setting and sort them by their priority.
    2. If one of these language (e.g. ko) matches an existing translation page, redirect the reader to that page (e.g. foo:bar.ko).

    3. If none matches, redirect him/her to the default translation page configured by the administrator (e.g. foo:bar.{$conf['lang']}) regardless of whether such page exists or not.

  3. If the queried page has a translation flag, show it to the reader.
  4. Aumatically choose an appropriate language for UI by comparing browser settings and DOKU_INC/inc/lang/* in the DokuWiki installation. (If none matches, choose $conf['lang'].)

  5. Overwrite chosen language for UI on $conf['lang'].

  6. Reset locale.
  7. Provide API in order for administrators to be able to add a menu (choosing other translations) to their template.

That's all. :-)

Code and Usage

Please remind again that you should append translation flags to all the page ID's before using this.

Creating inc/NLS.php File

Create inc/NLS.php with the following code (Administrators can modify two arrays $NLS_locarr and $NLS_langname in the first part of this code):

   1 <?php
   2 /**
   3  * National Language Support (NLS) script for DokuWiki
   4  *
   5  * @version    2005-09-22e-1.1.0
   6  * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
   7  * @author     CHA Reeseo <http://www.reeseo.net/>
   8  *
   9  * Usage:
  10  *     Include this script into 'doku.php',
  11  *     between the inclusions of 'inc/pageutils.php' and 'inc/html.php'
  12  *
  13  * CAUTION:
  14  *     This script demands a special policy that EVERY page should denote
  15  *     its own language (.xx or .xx-xx) at the end of its ID.
  16  *     For example, 'foo:bar.ko' is a page translated into Korean
  17  *     and 'foo:bar.en' is a page containing the same content in English.
  18  *
  19  *     Quering any page having ID without explicit language notation,
  20  *     you will be redirected to its appropriate 'localized' page.
  21  *     ('Unlocalized' page will be invisible.)
  22  *
  23  *     Please rename all the pre-existing pages before applying this script.
  24  *
  25  * To do:
  26  *     - Upgrade NLS_locale function
  27  */
  28 
  29 
  30 /* ---------------------------------------------------------------
  31  * Configuration options: You can modify or add something to these */
  32 
  33 $NLS_locarr['ko'] = 'ko_KR';
  34 $NLS_locarr['en'] = 'en_US';
  35 
  36 $NLS_langname['ko'] = '한국어';
  37 $NLS_langname['en'] = 'English';
  38 
  39 /* Configuration options end
  40  * --------------------------------------------------------------- */
  41 
  42 
  43 
  44 // Setting $ID
  45 $ID = getID();
  46 
  47 // Redirecting if no language is specified at the end of $ID
  48 if (! NLS_pagelang($ID)) {
  49         $target = NLS_page4browser($ID);
  50         if (! array_key_exists("idx", $_GET) && $_GET['do'] != 'recent')  // No redirection when indexing
  51                 header('Location: ' . wl($target));
  52 }
  53 
  54 // Resetting $conf['lang'] according to the language setting of user's browser
  55 $conf['lang'] = NLS_UI4browser();
  56 // Resetting $lang array
  57 @require_once(DOKU_INC.'inc/lang/'.$conf['lang'].'/lang.php');
  58 // Resetting locale
  59 setlocale(LC_ALL, NLS_locale($conf['lang']));
  60 
  61 
  62 /**
  63  * Getting locale string
  64  *
  65  * FIXME: What a poor function this is!
  66  */
  67 function NLS_locale($ln = NULL) {
  68         global $conf;
  69         global $lang;
  70         global $NLS_locarr;
  71         if (! $ln) $ln = $conf['lang'];
  72         $loc = array_key_exists($ln, $NLS_locarr) ? $NLS_locarr[$ln] : $ln;
  73         $loc .= '.';
  74         $loc .= array_key_exists('encoding', $lang) ? strtoupper($lang['encoding']) : 'UTF-8';
  75         return $loc;
  76 }
  77 
  78 
  79 /**
  80  * Printing links to other translations of the given page
  81  * (API for template files such as DOKU_TPL/main.php
  82  */
  83 function NLS_transmenu($pid = NULL, $delimiter = ",\n", $withself = FALSE) {
  84         global $NLS_langname;
  85         if (! $pid) {
  86                 global $ID;
  87                 $pid = $ID;
  88         }
  89         $currplang = NLS_pagelang($pid);
  90         $tpages = NLS_transpages($pid);
  91         if ($currplang && ! $withself)
  92                 unset($tpages[$currplang]);
  93         if ($tpages) {
  94                 $first = TRUE;
  95                 foreach ($tpages as $ln => $tid) {
  96                         $repr = array_key_exists($ln, $NLS_langname) ? $NLS_langname[$ln] : $ln;
  97                         if (! $first)
  98                                 echo $delimiter;
  99                         $first = FALSE;
 100                         if ($currplang == $ln)
 101                                 echo "<em>$repr</em>";
 102                         else
 103                                 echo "<a href=\"".wl($tid)."\">$repr</a>";
 104                 }
 105         } else {
 106                 echo "None.";
 107         }
 108 }
 109 
 110 
 111 
 112 /**
 113  * Selecting a redirection target (page) which best match the browser setting
 114  *
 115  * Default: page of $conf['lang'] (whether it exists or not)
 116  * Choice : among existing translations, highest priority for the browser
 117  */
 118 function NLS_page4browser($pid = NULL) {
 119         if (! $pid) {
 120                 global $ID;
 121                 $pid = $ID;
 122         }
 123         $blang = NLS_browserlang();
 124         $existing_pages = NLS_transpages($pid);
 125         $pid_base = NLS_ID_base($pid);
 126         $tmp_page = $pid_base . '.' . $conf['lang'];    // default page
 127         foreach ($blang as $lang_str => $priority) {
 128                 $lang_str = str_replace("_", "-", strtolower($lang_str));
 129                 if (array_key_exists($lang_str, $existing_pages))
 130                         $tmp_page = $pid_base . '.' . $lang_str;
 131                 elseif (array_key_exists(substr($lang_str, 0, 2), $existing_pages))
 132                         $tmp_page = $pid_base . '.' . substr($lang_str, 0, 2);
 133         }
 134         return $tmp_page;
 135 }
 136 
 137 
 138 /**
 139  * Selecting a language for UI
 140  *
 141  * Default: $conf['lang']
 142  * Choice : among existing 'inc/lang/*', highest priority for the browser
 143  */
 144 function NLS_UI4browser() {
 145         global $conf;
 146         $tmp_lang = $conf['lang'];      // This ($conf['lang']) is the default!
 147         $blang = NLS_browserlang();
 148         foreach ($blang as $lang_str => $priority) {
 149                 $lang_str = str_replace("_", "-", strtolower($lang_str));
 150                 if (is_dir(DOKU_INC . "inc/lang/" . $lang_str))
 151                         $tmp_lang = $lang_str;
 152                 elseif (is_dir(DOKU_INC . "inc/lang/" . substr($lang_str, 0, 2)))
 153                         $tmp_lang = substr($lang_str, 0, 2);
 154         }
 155         return $tmp_lang;
 156 }
 157 
 158 
 159 /**
 160  * Getting a sorted (by priority) array of the languages
 161  * from the language setting of the user's web browser
 162  */
 163 function NLS_browserlang() {
 164         $acclang_arr = split(" *, *", trim($_SERVER['HTTP_ACCEPT_LANGUAGE']));
 165         foreach ($acclang_arr as $acclang) {
 166                 if (ereg("^(.+) *;.+= *(.+)$", $acclang, $acclang_parts))
 167                         $acclang_sorted[$acclang_parts[1]] = (double)($acclang_parts[2]);
 168                 else
 169                         $acclang_sorted[$acclang] = 1.0;
 170         }
 171         asort($acclang_sorted, SORT_NUMERIC);
 172         reset($acclang_sorted);
 173         return $acclang_sorted;
 174 }
 175 
 176 
 177 /**
 178  * Getting an associative array (lang => ID) of
 179  * all the translated pages for the given page
 180  */
 181 function NLS_transpages($pid = NULL) {
 182         global $conf;
 183         if (! $pid) {
 184                 global $ID;
 185                 $pid = $ID;
 186         }
 187         $transpages = array();
 188         $pid_base_path = $conf['datadir'] . '/' . str_replace(":", "/", NLS_ID_base($pid));
 189         foreach(glob($pid_base_path . ".*.txt") as $fn) {
 190                 $aid = str_replace("/", ":", substr($fn, strlen($conf['datadir']) + 1, -4));
 191                 if ($alang = NLS_pagelang($aid)) {
 192                         $transpages[$alang] = $aid;
 193                 }
 194         }
 195         return $transpages;
 196 }
 197 
 198 
 199 /**
 200  * Drop language notation (.xx or .xx-xx) from the given ID
 201  */
 202 function NLS_ID_base($pid = NULL) {
 203         if (! $pid) {
 204                 global $ID;
 205                 $pid = $ID;
 206         }
 207         if (substr($pid, -3, 1) == '.')
 208                 return substr($pid, 0, -3);
 209         elseif (substr($pid, -6, 1) == '.' && substr($pid, -3, 1) == '-')
 210                 return substr($pid, 0, -6);
 211         else
 212                 return $pid;
 213 }
 214 
 215 
 216 /**
 217  * Get language (xx, xx-xx, or NULL) from the given ID
 218  */
 219 function NLS_pagelang($pid = NULL) {
 220         if (! $pid) {
 221                 global $ID;
 222                 $pid = $ID;
 223         }
 224         if (substr($pid, -3, 1) == '.')
 225                 return substr($pid, -2);
 226         elseif (substr($pid, -6, 1) == '.' && substr($pid, -3, 1) == '-')
 227                 return substr($pid, -5);
 228         else
 229                 return NULL;
 230 }
 231 ?>

Modifying doku.php File

Now, let's add an inclusion of inc/NLS.php at the beginning part of doku.php. The location of this inclusion is very important! Though NLS script should be loaded as early as possible, it should be placed after inc/pageutils.php because NLS script uses it. Please insert the line of inclusion just after including inc/pageutils.php (This code shows only the first few lines):

   1 <?php
   2 /**
   3  * DokuWiki mainscript
   4  *
   5  * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
   6  * @author     Andreas Gohr <andi@splitbrain.org>
   7  */
   8 
   9 //  xdebug_start_profiling();
  10 
  11   if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__)).'/');
  12   require_once(DOKU_INC.'inc/init.php');
  13   require_once(DOKU_INC.'inc/common.php');
  14   require_once(DOKU_INC.'inc/pageutils.php');
  15   require_once(DOKU_INC.'inc/NLS.php');       // insert this line!
  16   require_once(DOKU_INC.'inc/html.php');
  17   require_once(DOKU_INC.'inc/auth.php');
  18   require_once(DOKU_INC.'inc/actions.php');
  19 
  20   //import variables
  21 ...

Adding Menu (for Selecting Translations) to the Template

For the last, let's add a menu for selecting translations to the template in order for the readers to choose another translations of current page. Just insert the following code into the main.php or so in the template you are using:

   1 Other translations of this page: <?php NLS_transmenu(); ?>

NLS_transmenu() function is an API which generate a menu to the template and defines following parameters:

Each parameter is:

To do

Discussion

As a personal web site, this site prevents visitors from creating or modifying any document. Please use DokuWiki tip page if you have any comment.


  1. This is only for the content translations. UI language has nothing to do with URL, and web crawlers will take only one language according to their own settings or site default. (1)

  2. Some of these may change or disappear in a future version (1.2.x or above) of this technique. (2)