1;") { $type = 2; // French } // special cases elseif (strpos($type, "n%100!=11")) { if (strpos($type, "n!=0")) { $type == 21; // Latvian } if (strpos($type, "n%10<=4")) { $type = 22; // a few Slavic langs (code similar to Polish below) } if (strpos($type, "n%10>=2")) { // Lithuanian $type = 23; } $type = 0; } // specials, group 2 elseif (strpos($type, "n<=4")) { // Slovak $type = 25; } elseif (strpos($type, "n==2")) { // Irish $type = 31; } elseif (strpos($type, "n%10>=2")) { // Polish $type = 26; } elseif (strpos($type, "n%100==3")) { // Slovenian $type = 28; } // fallbacks elseif (strpos($type, ";plural=n;")) { $type = 7; // unused } // first at this point a tokenizer/parser/interpreter would have made sense else { $type = 0; // no plurals } } #-- return plural index value from pre-set formulas switch ($type) { case -1: // no plural forms return(0); case 1: // English, and lots of others... return($n != 1 ? 1 : 0); case 2: // French, Brazilian Protuguese return($n > 1 ? 1 : 0); case 7: // unused return($n); case 21: // Latvian return (($n%10==1) && ($n%100!=11)) ? (0) : ($n!=0 ? 1 : 2) ; case 22: // Slavic langs return ($n%10==1) && ($n%100!=11) ? 0 : ( ($n%10>=2) && ($n%10<=4) && ($n%100<10 || $n%100>=20) ? 1 : 2 ) ; case 23: // Lithuanian return ($n%10==1) && ($n%100!=11) ? 0 : ( ($n%10>=2) && ($n%100<10 || $n%100>=20) ? 1 : 2 ) ; case 25: // Slovak return $n==1 ? 0 : ($n>=2 && $n<=4 ? 1 : 2) ; case 26: // Polish return $n==1 ? 0 : ( $n%10>=2 && $n%10<=4 && ($n%100<10 || $n%100>=20) ? 1 : 2 ) ; case 28: // Slovenian return $n%100==1 ? 0 : ($n%100==2 || $n%100==3 || $n%100==4 ? 2 : 3) ; case 31: // Irish return ($n == 1) ? (0) : (($n == 2) ? 1 : 2) ; default: $type = -1; } // unsupported, always return non-plural index [0] return(0); } #-- wrappers around monster function above function ngettext($msg1, $msg2, $plural) { return gettext($msg1, $msg2, NULL, NULL, $plural); } function dngettext($domain, $msg1, $msg2, $plural) { return gettext($msg1, $msg2, $domain, NULL, $plural); } function dcngettext($domain, $msg1, $msg2, $plural, $category) { return gettext($msg1, $msg2, $domain, $category, $plural); } function dcgettext($domain, $msg, $category) { return gettext($msg, NULL, $domain, $category); } function dgettext($domain, $msg) { return gettext($msg, NULL, $domain); } #-- sets current translation data source # (must have been loaded beforehand) function textdomain($default="NULL") { global $_GETTEXT; $prev = isset($_GETTEXT['%domain']) ? $_GETTEXT['%domain'] : NULL; if (isset($default)) { $_GETTEXT['%domain'] = $default; } return $prev; } #-- loads data files function bindtextdomain($domain, $directory="/usr/share/locale:/usr/local/share/locale:./locale") { global $_GETTEXT; if (isset($_GETTEXT[$domain]) && (count($_GETTEXT[$domain]) > 3)) { return; // don't load twice } $_GETTEXT[$domain]['%dir'] = $directory; $_GETTEXT['%locale'] = setlocale(LC_CTYPE, 0); #-- allowed languages $langs = @$_ENV['LANGUAGE'] . ',' . @$_ENV['LC_ALL'] . ',' . @$_ENV['LC_MESSAGE'] .',' . @$_ENV['LANG'] . ',' . @$_GETTEXT['%locale'] . ',' . @$_SERVER['HTTP_ACCEPT_LANGUAGE'] . ',C,en'; #-- add shortened language codes (en_UK.UTF-8 -> + en_UK, en) foreach (explode(',', $langs) as $d) { $d = trim($d); // $dir2[] = $d; $d = strtok($d, "@.-+=%:; "); if (strlen($d)) { $dir2[] = $d; } if (strpos($d, '_')) { $dir2[] = strtok($d, '_'); } } #-- search for matching directory and load data file foreach (explode(':', $directory) as $directory) { foreach ($dir2 as $lang) { $base_fn = "$directory/$lang/LC_MESSAGES/$domain"; #echo "GETTEXT:$lang:$base_fn\n"; #-- binary format if (file_exists($fn = "$base_fn.mo") && ($f = fopen($fn, "rb"))) { gettext___load_mo($f, $domain); break 2; } #-- text file elseif (function_exists("gettext___load_po") and file_exists($fn = "$base_fn.po") && ($f = fopen($fn, "r"))) { gettext___load_po($f, $domain); break 2; } } }//foreach #-- extract headers if ($head = $_GETTEXT[$domain][""]) { foreach (explode("\n", $head) as $line) { $header = strtok(':', $line); $line = trim(strtok("\n")); $_GETTEXT[$domain]['%po-header'][strtolower($header)] = $line; } #-- plural-forms header if (function_exists("gettext___plural_guess") and ($h = @$_GETTEXT[$domain]['%po-header']["plural-forms"])) { $h = preg_replace("/[(){}\[\]^\s*\\]+/", "", $h); // rm whitespace gettext___plural_guess($h, 0); // pre-decode into algorithm type integer $_GETTEXT[$domain]['%plural-c'] = $h; } } #-- set as default textdomain if (empty($_GETTEXT['%domain'])) { textdomain($domain); } return($domain); } #-- load string data from binary .mo files (ign checksums) function gettext___load_mo($f, $domain) { global $_GETTEXT; #-- read in data file completely $data = fread($f, 1<<20); fclose($f); #-- extract header fields and check file magic if ($data) { $header = substr($data, 0, 20); $header = unpack("L1magic/L1version/L1count/L1o_msg/L1o_trn", $header); extract($header); if ((dechex($magic) == "950412de") && ($version == 0)) { #-- fetch all entries for ($n=0; $n<$count; $n++) { #-- msgid $r = unpack("L1len/L1offs", substr($data, $o_msg + $n * 8, 8)); $msgid = substr($data, $r["offs"], $r["len"]); unset($msgid_plural); if (strpos($msgid, "\000")) { list($msgid, $msgid_plural) = explode("\000", $msgid); } #-- translation(s) $r = unpack("L1len/L1offs", substr($data, $o_trn + $n * 8, 8)); $msgstr = substr($data, $r["offs"], $r["len"]); if (strpos($msgstr, "\000")) { $msgstr = explode("\000", $msgstr); } #-- add $_GETTEXT[$domain][$msgid] = $msgstr; if (isset($msgid_plural)) { $_GETTEXT[$domain][$msgid_plural] = &$_GETTEXT[$domain][$msgid]; } } } } } #-- read from textual .po source file (not fully correct, and redundant # because the original gettext/libintl doesn't support this at all) function gettext___load_po($f, $domain) { global $_GETTEXT; $c_esc = array("\\n"=>"\n", "\\r"=>"\r", "\\\\"=>"\\", "\\f"=>"\f", "\\t"=>"\t", "\\"=>""); #-- read line-wise from text file do { $line = trim(fgets($f)); #-- check what's in the current line $space = strpos($line, " "); // comment if ($line[0] == "#") { //continue; } // msgid elseif (strncmp($line, "msgid", 5)==0) { $msgid[] = trim(substr($line, $space+1), '"'); } // translation elseif (strncmp($line, "msgstr", 6)==0) { $msgstr[] = trim(substr($line, $space+1), '"'); } // continued (could be _id or _str) elseif ($line[0] == '"') { $line = trim($line, '"'); if (isset($msggstr)) { $msgstr[count($msgstr)] .= $line; } else { $msgid[count($msgid)] .= $line; } } #-- append to global $_GETTEXT hash as soon as we have a complete dataset if (isset($msgid) && isset($msgstr) && (empty($line) || ($line[0]=="#") || feof($f)) ) { $msgid[0] = strtr($msgid[0], $c_esc); foreach ($msgstr as $v) { $_GETTEXT[$domain][$msgid[0]] = strtr($v, $c_esc); } if ($msgid[1]) { $msgid[1] = strtr($msgid[1], $c_esc); $_GETTEXT[$domain][$msgid[1]] = &$_GETTEXT[$domain][$msgid[0]]; } $msgid = array(); $msgstr = array(); } } while (!feof($f)); fclose($f); } #-- ignored setting (no idea what it really should do) function bind_textdomain_codeset($domain, $codeset) { global $_GETTEXT; $_GETTEXT[$domain]["%codeset"] = $codeset; return($domain); } } #-- define gettexts preferred function name _ separately if (!function_exists("_")) { function _($str) { return gettext($str); } } ?>