Files
selima-perl/lib/php/monica/getlang.inc.php
2026-03-10 21:31:43 +08:00

436 lines
13 KiB
PHP

<?php
// File name: getlang.inc.php
// Description: PHP subroutine to get the current user preferred language
// Date: 2002-04-11
// Author: imacat <imacat@pristine.com.tw>
// Copyright: Copyright (C) 2002-2010 Pristine Communications
// Set the include path
if (!defined("INCPATH_SET")) {
require_once dirname(__FILE__) . "/incpath.inc.php";
}
// Referenced subroutines
require_once "monica/cgiemu.inc.php";
require_once "monica/lninfo.inc.php";
require_once "monica/requri.inc.php";
// getlang: Get the appropriate language from the user-agent.
// Input: type: the type of language information to be returned
// See lninfo.inc.php for the most current list of available types.
// LN_NAME: Language name
// LN_CHARSET: It's suggested character set
// LN_FILENAME: It's proper format as part of a file name
// LN_LOCALE: It's locale
// LN_DATABASE: It's proper format as part of a database field name
// LN_HTMLID: It's proper format as part of an HTML ID.
// LN_SPACE_BREAK: If this language break lines at spaces
// LN_IGNORE_CASE: If case can be ignored (can safely do strlower() and strupper())
// LN_COUNTRY_FIRST: If country should be listed first in a mail address
// LN_DESC: It's long description, in English
// LN_DESC_CURLC: It's long description in the current language (UTF-8)
// LN_DESC_SELFLC: It's long description in its own language (UTF-8)
function getlang($type = null)
{
$lang = _getlang_getlang();
// Return in the proper data type
return !is_null($type)? ln($lang, $type): $lang;
}
// getcharset: Get the appropriate character set from the user-agent.
function getcharset()
{
static $charset;
// Obtained before
if (isset($charset)) {
return $charset;
}
// Default character set of this language
$default = getlang(LN_CHARSET);
// Obtain all the available character sets
$all_charsets = _getlang_all_charsets();
// We have no choice
if (count($all_charsets) < 2) {
$charset = $all_charsets[0];
return $charset;
}
// Parse the character set by the Accept-Charset header
$charset = _getlang_getcs_accept();
if (!is_null($charset)) {
return $charset;
}
// Cannot parse - return the default
$lang = $default;
return $lang;
}
// _getlang_getlang: The real subroutine
function _getlang_getlang()
{
static $lang;
// Obtained before
if (isset($lang)) {
return $lang;
}
// Default fallback settings
if (!array_key_exists("ALL_LINGUAS", $GLOBALS)) {
$GLOBALS["ALL_LINGUAS"] = array("en");
}
global $ALL_LINGUAS;
if (!defined("DEFAULT_LANG")) {
define("DEFAULT_LANG", "en");
}
// Uni-lingual
if (count($ALL_LINGUAS) == 1) {
$lang = $ALL_LINGUAS[0];
return $lang;
}
// Check the file name for specified language
// No setting environment in this case
$lang = _getlang_getlang_filename();
if (!is_null($lang)) {
return $lang;
}
// Methods below should set the language in the environment
// Check the environment for specified language
$lang = _getlang_getlang_env();
if (!is_null($lang)) {
_getlang_getlang_setenv($lang);
return $lang;
}
// Parse the language by the Accept-Language header
$lang = _getlang_getlang_accept();
if (!is_null($lang)) {
_getlang_getlang_setenv($lang);
return $lang;
}
// Parse the language by the console locale
$lang = _getlang_getlang_locale();
if (!is_null($lang)) {
_getlang_getlang_setenv($lang);
return $lang;
}
// Cannot parse - return the default
$lang = DEFAULT_LANG;
_getlang_getlang_setenv($lang);
return $lang;
}
// _getlang_getlang_filename: Check the file name for specified language
function _getlang_getlang_filename()
{
global $ALL_LINGUAS;
// Check the file name format
if (!preg_match("/\.([^\.\/]+)\.[^\.\/]+$/", REQUEST_PATH, $m)) {
return null;
}
$langfile = $m[1];
// Check each language for its file name format
for ($l = 0, $map = array(); $l < count($ALL_LINGUAS); $l++) {
$lang = $ALL_LINGUAS[$l];
if ($langfile == ln($lang, LN_FILENAME)) {
return $lang;
}
}
// Not found
return null;
}
// _getlang_getlang_env: Check the environment for specified language
function _getlang_getlang_env()
{
global $ALL_LINGUAS;
// Check the query string
if ( array_key_exists("lang", $_GET)
&& in_array($_GET["lang"], $ALL_LINGUAS)) {
return $_GET["lang"];
// Check the POSTed form
} elseif ( array_key_exists("lang", $_POST)
&& in_array($_POST["lang"], $ALL_LINGUAS)) {
return $_POST["lang"];
// Check the cookies
} elseif ( array_key_exists("lang", $_COOKIE)
&& in_array($_COOKIE["lang"], $ALL_LINGUAS)) {
return $_COOKIE["lang"];
// Check the session
} elseif ( isset($_SESSION)
&& array_key_exists("lang", $_SESSION)
&& in_array($_SESSION["lang"], $ALL_LINGUAS)) {
return $_SESSION["lang"];
}
// Not set
return null;
}
// _getlang_getlang_accept: Parse the language by the Accept-Language header
// Refer to HTTP/1.1 section 14.4 for this algorism
function _getlang_getlang_accept()
{
// Accept-Language not set
if (!array_key_exists("HTTP_ACCEPT_LANGUAGE", $_SERVER)) {
return null;
}
global $ALL_LINGUAS;
// Split into language ranges
$rngs = preg_split("/\s*,\s*/", trim($_SERVER["HTTP_ACCEPT_LANGUAGE"]));
$rngqf = array(); // User Assigned quality factor
foreach ($rngs as $range) {
// Split into attributes
$attrs = preg_split("/\s*;\s*/", trim($range));
// First piece is the language range
$ln = array_shift($attrs);
// Lower-case it
$ln = strtolower($ln);
// Find the quality factor
foreach ($attrs as $attr) {
// A numeric quality factor found
if (strtolower(substr($attr, 0, 2)) == "q="
&& is_numeric(substr($attr, 2))) {
$rngqf[$ln] = substr($attr, 2) + 0;
}
}
// Default quality factor to 1
if (!array_key_exists($ln, $rngqf)) {
$rngqf[$ln] = 1;
}
}
// The default quality factor
if (array_key_exists("*", $rngqf)) {
$defqf = $rngqf["*"];
unset($rngqf["*"]);
} else {
$defqf = 0;
}
// Language tags (what we have)
$tagqf = array(); // Calculated quality factor
for ($l = 0; $l < count($ALL_LINGUAS); $l++) {
$ln = $ALL_LINGUAS[$l];
// Language tag, as specified in ISO
$tag = ln($ln, LN_NAME);
unset($match); // Matched range of the quality factor
// Language ranges (what the user sent to match us)
foreach (array_keys($rngqf) as $range) {
// Exactly match or match a prefix
if ( $tag == $range
|| substr($tag, 0, strlen($range)+1) == "$range-") {
// Not matched yet
if (!isset($match)) {
$tagqf[$ln] = $rngqf[$range]; // Quality Factor
$match = $range; // Record the matched range
// A longer match range
} elseif (strlen($range) > strlen($match)) {
$tagqf[$ln] = $rngqf[$range]; // Quality Factor
$match = $range; // Record the matched range
}
}
}
// Not matched - apply a default quality factor
if (!array_key_exists($ln, $tagqf)) {
$tagqf[$ln] = $defqf;
}
}
// Drop unacceptable languages
foreach (array_keys($tagqf) as $ln) {
if ($tagqf[$ln] <= 0) {
unset($tagqf[$ln]);
}
}
// Nothing acceptable
if (count($tagqf) == 0) {
return null;
}
// Sort by the quality factor
$GLOBALS["_GETLANG_TAGQF"] =& $tagqf;
$GLOBALS["_GETLANG_DEFAULT"] = DEFAULT_LANG;
$ln = array_keys($tagqf);
usort($ln, "_getlang_cmp_tagqf");
unset($GLOBALS["_GETLANG_TAGQF"]);
unset($GLOBALS["_GETLANG_DEFAULT"]);
// A preferred match
return $ln[0];
}
// _getlang_getlang_locale: Parse the language by the console locale
function _getlang_getlang_locale()
{
// Only work on console
if (IS_CGI) {
return null;
}
// Check these environment variables in order
// See http://www.gnu.org/software/libc/manual/html_node/Using-gettextized-software.html
$envs = array("LANGUAGE", "LC_ALL", "LC_MESSAGES", "LANG");
for ($i = 0, $lang = null; $i < count($envs); $i++) {
$locale = getenv($envs[$i]);
// Obtain the first valid one and discard the rest
if ($locale !== false && $locale != "") {
$lang = $locale;
break;
}
}
// No locale setting was found
if (is_null($lang)) {
return null;
}
// Remove the character set
$lang = preg_replace("/\..*$/", "", $lang);
// Lower-case it
$lang = strtolower($lang);
// Replace underscore with dash
$lang = str_replace("_", "-", $lang);
// en-us is en
if ($lang == "en-us") {
$lang = "en";
}
// Not in our available languages list
if (!in_array($lang, $GLOBALS["ALL_LINGUAS"])) {
return null;
}
// Return it
return $lang;
}
// _getlang_getlang_setenv: Set the language in the environment
function _getlang_getlang_setenv($lang)
{
// Set the session variable
if ( isset($_SESSION)
&& (!array_key_exists("lang", $_SESSION)
|| $_SESSION["lang"] != $lang)) {
$_SESSION["lang"] = $lang;
}
// Set the cookie
if ( (!array_key_exists("lang", $_COOKIE)
|| $_COOKIE["lang"] != $lang)
&& !headers_sent()) {
setcookie("lang", $lang, time() + 86400 * 365, "/");
}
return;
}
// _getlang_getcs_accept: Parse the character set by the Accept-Charset header
// Refer to HTTP/1.1 section 14.2 for this algorism
function _getlang_getcs_accept()
{
// Accept-Charset not set
if (!array_key_exists("HTTP_ACCEPT_CHARSET", $_SERVER)) {
return null;
}
// Obtain all the available character sets
$all_charsets = _getlang_all_charsets();
// Split into character set ranges
$rngs = preg_split("/\s*,\s*/", trim($_SERVER["HTTP_ACCEPT_CHARSET"]));
$rngqf = array(); // User Assigned quality factor
foreach ($rngs as $range) {
// Split into attributes
$attrs = preg_split("/\s*;\s*/", trim($range));
// First piece is the character set range
$cs = array_shift($attrs);
// Lower-case it
$cs = strtolower($cs);
// Find the quality factor
foreach ($attrs as $attr) {
// A numeric quality factor found
if (strtolower(substr($attr, 0, 2)) == "q="
&& is_numeric(substr($attr, 2))) {
$rngqf[$cs] = substr($attr, 2) + 0;
}
}
// Default quality factor to 1
if (!array_key_exists($cs, $rngqf)) {
$rngqf[$cs] = 1;
}
}
// The default quality factor
if (array_key_exists("*", $rngqf)) {
$defqf = $rngqf["*"];
unset($rngqf["*"]);
} else {
// Default ISO-8859-1 to 1
if (!array_key_exists("iso-8859-1", $rngqf)) {
$rngqf["iso-8859-1"] = 1;
}
$defqf = 0;
}
$tagqf = array(); // Calculated quality factor
// Character set tags (what we have)
for ($l = 0; $l < count($all_charsets); $l++) {
$cs = $all_charsets[$l];
$tag = strtolower($cs);
// Character set ranges (what the user sent to match us)
foreach (array_keys($rngqf) as $range) {
// Matched
if ($tag == $range) {
$tagqf[$cs] = $rngqf[$range]; // Quality Factor
}
}
// Not matched - apply a default quality factor
if (!array_key_exists($cs, $tagqf)) {
$tagqf[$cs] = $defqf;
}
}
// Drop unacceptable languages
foreach (array_keys($tagqf) as $cs) {
if ($tagqf[$cs] <= 0) {
unset($tagqf[$cs]);
}
}
// Nothing acceptable
if (count($tagqf) == 0) {
return null;
}
// Sort by the quality factor
$GLOBALS["_GETLANG_TAGQF"] =& $tagqf;
$GLOBALS["_GETLANG_DEFAULT"] = getlang(LN_CHARSET);
$cs = array_keys($tagqf);
usort($cs, "_getlang_cmp_tagqf");
unset($GLOBALS["_GETLANG_TAGQF"]);
unset($GLOBALS["_GETLANG_DEFAULT"]);
// A preferred match
return $cs[0];
}
// _getlang_all_charsets: Obtain all the available character sets
// Available character sets are the default character set of this
// language, and UTF-8
function _getlang_all_charsets()
{
static $charsets;
if (isset($charsets)) {
return $charsets;
}
$charsets = array();
$charsets[] = getlang(LN_CHARSET);
$charsets[] = "UTF-8";
$charsets = array_values(array_unique($charsets));
return $charsets;
}
// _getlang_cmp_tagqf: Compare the quality factor of the tags
function _getlang_cmp_tagqf($a, $b)
{
global $_GETLANG_TAGQF, $_GETLANG_DEFAULT;
if ($_GETLANG_TAGQF[$a] != $_GETLANG_TAGQF[$b]) {
return $_GETLANG_TAGQF[$b] < $_GETLANG_TAGQF[$a]? -1: 1;
}
if ($a == $_GETLANG_DEFAULT) {
return -1;
}
if ($b == $_GETLANG_DEFAULT) {
return 1;
}
return 0;
}
?>