Add language detection (guessing)
This commit is contained in:
@@ -42,6 +42,12 @@ abstract class Main extends PhpObject
|
|||||||
protected $asMasks;
|
protected $asMasks;
|
||||||
protected $asContext;
|
protected $asContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Language Translator
|
||||||
|
* @var Translator
|
||||||
|
*/
|
||||||
|
protected $oLang;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main constructor
|
* Main constructor
|
||||||
* @param ClassManagement $oClassManagement
|
* @param ClassManagement $oClassManagement
|
||||||
@@ -123,7 +129,7 @@ abstract class Main extends PhpObject
|
|||||||
if($sPage!=$sMainPage) $asGlobalVars['consts']['pages'][$sPage] = $sPageContent;
|
if($sPage!=$sMainPage) $asGlobalVars['consts']['pages'][$sPage] = $sPageContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
$oMainMask = new Mask($sMainPage);
|
$oMainMask = new Mask($sMainPage, $this->oLang);
|
||||||
$oMainMask->setTag('GLOBAL_VARS', json_encode($asGlobalVars));
|
$oMainMask->setTag('GLOBAL_VARS', json_encode($asGlobalVars));
|
||||||
$oMainMask->setTags($asMainPageTags);
|
$oMainMask->setTags($asMainPageTags);
|
||||||
|
|
||||||
|
|||||||
38
inc/mask.php
38
inc/mask.php
@@ -14,13 +14,15 @@ class Mask extends PhpObject
|
|||||||
private $asPartsSource;
|
private $asPartsSource;
|
||||||
private $aoInstances;
|
private $aoInstances;
|
||||||
|
|
||||||
|
private $oLang;
|
||||||
|
|
||||||
const MASK_FOLDER = 'masks/';
|
const MASK_FOLDER = 'masks/';
|
||||||
const MASK_EXT = '.html';
|
const MASK_EXT = '.html';
|
||||||
const START_TAG = 'START';
|
const START_TAG = 'START';
|
||||||
const END_TAG = 'END';
|
const END_TAG = 'END';
|
||||||
const TAG_MARK = '[#]';
|
const TAG_MARK = '[#]';
|
||||||
|
|
||||||
public function __construct($sFileName='')
|
public function __construct($sFileName='', Translator $oLang=null)
|
||||||
{
|
{
|
||||||
//init
|
//init
|
||||||
parent::__construct(__FILE__, Settings::DEBUG);
|
parent::__construct(__FILE__, Settings::DEBUG);
|
||||||
@@ -31,12 +33,10 @@ class Mask extends PhpObject
|
|||||||
$this->asPartsSource = array();
|
$this->asPartsSource = array();
|
||||||
$this->aoInstances = array();
|
$this->aoInstances = array();
|
||||||
$this->sFilePath = '';
|
$this->sFilePath = '';
|
||||||
|
$this->oLang = $oLang;
|
||||||
|
|
||||||
//load file
|
//load file
|
||||||
if($sFileName!='')
|
if($sFileName!='') $this->initFile($sFileName);
|
||||||
{
|
|
||||||
$this->initFile($sFileName);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getMaskFile($sFileName)
|
public static function getMaskFile($sFileName)
|
||||||
@@ -69,6 +69,7 @@ class Mask extends PhpObject
|
|||||||
$this->sMaskName = $sMaskName;
|
$this->sMaskName = $sMaskName;
|
||||||
$this->sMask = $sSource;
|
$this->sMask = $sSource;
|
||||||
$this->setParts();
|
$this->setParts();
|
||||||
|
$this->setLangTags();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function setParts()
|
private function setParts()
|
||||||
@@ -134,7 +135,7 @@ class Mask extends PhpObject
|
|||||||
$sPartSource = $oMask->asPartsSource[$sPartName];
|
$sPartSource = $oMask->asPartsSource[$sPartName];
|
||||||
|
|
||||||
//Creating new instance
|
//Creating new instance
|
||||||
$oInstance = new Mask();
|
$oInstance = new Mask('', $this->oLang);
|
||||||
$oInstance->initFileFromString($sPartSource, $sPartName);
|
$oInstance->initFileFromString($sPartSource, $sPartName);
|
||||||
$oMask->aoInstances[$sPartName][] = $oInstance;
|
$oMask->aoInstances[$sPartName][] = $oInstance;
|
||||||
}
|
}
|
||||||
@@ -180,16 +181,21 @@ class Mask extends PhpObject
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getTags()
|
public function getTags($bLang=false)
|
||||||
{
|
{
|
||||||
$sSafeTagMark = preg_quote(self::TAG_MARK);
|
$sSafeTagMark = preg_quote(self::TAG_MARK);
|
||||||
$sPattern = '/'.$sSafeTagMark.'(?P<tag>\w+)'.$sSafeTagMark.'/u';
|
$sPattern = '/'.$sSafeTagMark.'(?P<tag>(lang\:|)\w+)'.$sSafeTagMark.'/u';
|
||||||
preg_match_all($sPattern, $this->sMask, $asMatches);
|
preg_match_all($sPattern, $this->sMask, $asMatches);
|
||||||
return array_unique(array_filter($asMatches['tag']));
|
return array_unique(array_filter($asMatches['tag']));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setTag($sTagName, $sTagValue)
|
public function setTag($sTagName, $sTagValue)
|
||||||
{
|
{
|
||||||
|
//Check if tagged should be translated
|
||||||
|
if(self::isLangTag($sTagValue)) {
|
||||||
|
if(is_null($this->oLang)) $this->addError('Missing Lang Class. Please provide in constructor');
|
||||||
|
else $sTagValue = $this->oLang->getTranslation(mb_substr($sTagValue, 5));
|
||||||
|
}
|
||||||
$this->asTags[$sTagName] = $sTagValue;
|
$this->asTags[$sTagName] = $sTagValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,6 +204,22 @@ class Mask extends PhpObject
|
|||||||
foreach($asTags as $sTagName=>$sTagValue) $this->setTag($sTagName, $sTagValue);
|
foreach($asTags as $sTagName=>$sTagValue) $this->setTag($sTagName, $sTagValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default value for tags tagged as translation: Starting with lang/
|
||||||
|
*/
|
||||||
|
private function setLangTags() {
|
||||||
|
if($this->oLang != null) {
|
||||||
|
$asTags = $this->getTags();
|
||||||
|
foreach($asTags as $sTagName) {
|
||||||
|
if(self::isLangTag($sTagName)) $this->setTag($sTagName, $this->oLang->getTranslation(mb_substr($sTagName, 5)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function isLangTag($sTag) {
|
||||||
|
return (mb_substr($sTag, 0, 5) == 'lang:');
|
||||||
|
}
|
||||||
|
|
||||||
public function getMask()
|
public function getMask()
|
||||||
{
|
{
|
||||||
$sCompletedMask = $this->sMask;
|
$sCompletedMask = $this->sMask;
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ class ToolBox
|
|||||||
return json_encode($asData);
|
return json_encode($asData);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function curl($sUrl, $bHeader=false, $asPostData=array(), $sCookie='', $sCreds='') {
|
public static function curl($sUrl, $bHeader=false, $asPostData=array(), $sReturnType='text', $sCookie='', $sCreds='') {
|
||||||
$oCurl = curl_init();
|
$oCurl = curl_init();
|
||||||
curl_setopt($oCurl, CURLOPT_URL, $sUrl);
|
curl_setopt($oCurl, CURLOPT_URL, $sUrl);
|
||||||
curl_setopt($oCurl, CURLOPT_VERBOSE, false);
|
curl_setopt($oCurl, CURLOPT_VERBOSE, false);
|
||||||
@@ -148,8 +148,18 @@ class ToolBox
|
|||||||
if($sCreds!='') curl_setopt($oCurl, CURLOPT_USERPWD, $sCreds);
|
if($sCreds!='') curl_setopt($oCurl, CURLOPT_USERPWD, $sCreds);
|
||||||
|
|
||||||
$sContent = curl_exec($oCurl);
|
$sContent = curl_exec($oCurl);
|
||||||
|
|
||||||
|
$bSuccess = ($sContent!==false);
|
||||||
|
$sDesc = '';
|
||||||
|
if(!$bSuccess) $sDesc = curl_errno($oCurl).': '.curl_strerror(curl_errno($oCurl));
|
||||||
curl_close($oCurl);
|
curl_close($oCurl);
|
||||||
return $sContent;
|
|
||||||
|
switch($sReturnType) {
|
||||||
|
case 'json': $oContent = json_decode($sContent, true); break;
|
||||||
|
default: $oContent = $sContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return array('result'=>$bSuccess, 'desc'=>$sDesc, 'content'=>$oContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getMimeType($sPath, $bSubTypeOnly=false)
|
public static function getMimeType($sPath, $bSubTypeOnly=false)
|
||||||
@@ -454,6 +464,41 @@ class ToolBox
|
|||||||
return $sText;
|
return $sText;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getUserLanguage($available_languages, $sDefaultLang='') {
|
||||||
|
$http_accept_language = isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])?$_SERVER['HTTP_ACCEPT_LANGUAGE']:'';
|
||||||
|
|
||||||
|
//Format: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
|
||||||
|
preg_match_all("/([[:alpha:]]{1,8})(-([[:alpha:]|-]{1,8}))?(\s*;\s*q\s*=\s*(1\.0{0,3}|0\.\d{0,3}))?\s*(,|$)/i", $http_accept_language, $hits, PREG_SET_ORDER);
|
||||||
|
|
||||||
|
$bestlang = $sDefaultLang;
|
||||||
|
$bestqval = 0;
|
||||||
|
|
||||||
|
foreach($hits as $arr) {
|
||||||
|
$langprefix = strtolower($arr[1]);
|
||||||
|
if(!empty($arr[3])) {
|
||||||
|
$langrange = strtolower($arr[3]);
|
||||||
|
$language = $langprefix.'-'.$langrange;
|
||||||
|
}
|
||||||
|
else $language = $langprefix;
|
||||||
|
|
||||||
|
//Q Value
|
||||||
|
$qvalue = 1.0;
|
||||||
|
if(!empty($arr[5])) $qvalue = floatval($arr[5]);
|
||||||
|
|
||||||
|
//find q-maximal language
|
||||||
|
if(in_array($language, $available_languages) && $qvalue > $bestqval) {
|
||||||
|
$bestlang = $language;
|
||||||
|
$bestqval = $qvalue;
|
||||||
|
}
|
||||||
|
//if no direct hit, try the prefix only but decrease q-value by 10% (as http_negotiate_language does)
|
||||||
|
elseif(in_array($langprefix, $available_languages) && ($qvalue * 0.9) > $bestqval) {
|
||||||
|
$bestlang = $langprefix;
|
||||||
|
$bestqval = $qvalue * 0.9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $bestlang;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return relative time description
|
* Return relative time description
|
||||||
* FIXME shitty implementation of i18n
|
* FIXME shitty implementation of i18n
|
||||||
|
|||||||
@@ -8,52 +8,59 @@
|
|||||||
class Translator extends PhpObject
|
class Translator extends PhpObject
|
||||||
{
|
{
|
||||||
private $sLang;
|
private $sLang;
|
||||||
|
private $sDefaultLang;
|
||||||
private $asLanguages;
|
private $asLanguages;
|
||||||
private $asTranslations; // [lang][key_word] = translation
|
private $asTranslations; // [lang][key_word] = translation
|
||||||
|
|
||||||
const LANG_FOLDER = 'languages/';
|
const LANG_FOLDER = 'languages/';
|
||||||
const LANG_EXT = '.lang';
|
const LANG_EXT = '.lang';
|
||||||
const LANG_SEP = '=';
|
const LANG_SEP = '=';
|
||||||
const DEFAULT_LANG = 'FR';
|
|
||||||
|
|
||||||
public function __construct($sLang='')
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param string $sLang leave empty for auto selection based on $_SERVER['HTTP_ACCEPT_LANGUAGE']
|
||||||
|
* @param string $sDefaultLang Fallback language if no fitting language is available
|
||||||
|
*/
|
||||||
|
public function __construct($sLang='', $sDefaultLang='en')
|
||||||
{
|
{
|
||||||
parent::__construct(__FILE__, Settings::DEBUG);
|
parent::__construct(__FILE__, Settings::DEBUG);
|
||||||
$this->asLanguages = array();
|
$this->asLanguages = array();
|
||||||
$this->asTranslations = array();
|
$this->asTranslations = array();
|
||||||
$this->loadLanguages();
|
$this->loadLanguages();
|
||||||
$this->setLanguage($sLang);
|
$this->setLanguage($sLang, $sDefaultLang);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setLanguage($sLang)
|
public function setLanguage($sLang, $sDefaultLang) {
|
||||||
{
|
$this->sDefaultLang = $sDefaultLang;
|
||||||
$this->sLang = in_array($sLang, $this->asLanguages)?$sLang:self::DEFAULT_LANG;
|
$this->sLang = ($sLang=='')?ToolBox::getUserLanguage($this->asLanguages, $this->sDefaultLang):$sLang;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getTranslation($sTransKey, $sLang='')
|
public function getTranslation($sTransKey='', $sLang='')
|
||||||
{
|
{
|
||||||
$sTransText = false;
|
$oTransText = false;
|
||||||
|
|
||||||
//Select language
|
//Select language & Scope
|
||||||
if($sLang=='')
|
if($sLang=='') $sLang = $this->sLang;
|
||||||
{
|
$bAllTrans = ($sTransKey=='');
|
||||||
$sLang = $this->sLang;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Look up in the selected language dictionary
|
//Look up in the selected language dictionary
|
||||||
if(in_array($sLang, $this->asLanguages) && array_key_exists($sTransKey, $this->asTranslations[$sLang]))
|
if(in_array($sLang, $this->asLanguages) && ($bAllTrans || array_key_exists($sTransKey, $this->asTranslations[$sLang])))
|
||||||
{
|
{
|
||||||
$sTransText = $this->asTranslations[$sLang][$sTransKey];
|
$oTransText = $bAllTrans?$this->asTranslations[$sLang]:$this->asTranslations[$sLang][$sTransKey];
|
||||||
}
|
}
|
||||||
//Look up in the default language dictionary
|
//Look up in the default language dictionary
|
||||||
elseif(array_key_exists($sTransKey, $this->asTranslations[self::DEFAULT_LANG]))
|
elseif(array_key_exists($sTransKey, $this->asTranslations[$this->sDefaultLang]))
|
||||||
{
|
{
|
||||||
$this->addWarning('Missing translation in "'.$sLang.'" for the key "'.$sTransKey.'", falling back to "'.self::DEFAULT_LANG.'"');
|
$this->addWarning('Missing translation in "'.$sLang.'" for the key "'.$sTransKey.'", falling back to "'.$this->sDefaultLang.'"');
|
||||||
$sTransText = $this->asTranslations[self::DEFAULT_LANG][$sTransKey];
|
$oTransText = $this->asTranslations[$this->sDefaultLang][$sTransKey];
|
||||||
}
|
}
|
||||||
else $this->addWarning('Missing translation in "'.$sLang.'" for the key "'.$sTransKey.'"');
|
else $this->addWarning($bAllTrans?'Missing language "'.$sLang.'"':'Missing translation in "'.$sLang.'" for the key "'.$sTransKey.'"');
|
||||||
|
|
||||||
return $sTransText;
|
return $oTransText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTranslations($sLang='') {
|
||||||
|
return $this->getTranslation('', $sLang);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getHashToPage($asMenuPages)
|
public function getHashToPage($asMenuPages)
|
||||||
@@ -83,7 +90,7 @@ class Translator extends PhpObject
|
|||||||
{
|
{
|
||||||
//List all available languages
|
//List all available languages
|
||||||
$asLangPaths = glob(self::getLangPath('*'));
|
$asLangPaths = glob(self::getLangPath('*'));
|
||||||
$this->asLanguages = array_map('basename', $asLangPaths, array_fill(1, count($asLangPaths), self::LANG_EXT));
|
$this->asLanguages = array_map('basename', $asLangPaths, array_fill(1, count($asLangPaths), self::LANG_EXT));
|
||||||
|
|
||||||
//Load languages
|
//Load languages
|
||||||
array_walk($this->asLanguages, array($this, 'loadLanguageFile'));
|
array_walk($this->asLanguages, array($this, 'loadLanguageFile'));
|
||||||
@@ -97,11 +104,11 @@ class Translator extends PhpObject
|
|||||||
$asData = explode("\n", $sData);
|
$asData = explode("\n", $sData);
|
||||||
foreach($asData as $sTranslation)
|
foreach($asData as $sTranslation)
|
||||||
{
|
{
|
||||||
$iSepPos = stripos($sTranslation, self::LANG_SEP);
|
$iSepPos = mb_stripos($sTranslation, self::LANG_SEP);
|
||||||
if($iSepPos!==false)
|
if($iSepPos !== false)
|
||||||
{
|
{
|
||||||
$sTransKey = trim(substr($sTranslation, 0, $iSepPos));
|
$sTransKey = trim(mb_substr($sTranslation, 0, $iSepPos));
|
||||||
$sTransText = /*htmlspecialchars(*/trim(substr($sTranslation, $iSepPos+1))/*, ENT_QUOTES)*/; //TODO when all entities have been removed
|
$sTransText = trim(mb_substr($sTranslation, $iSepPos+1));
|
||||||
$this->asTranslations[$sLang][$sTransKey] = $sTransText;
|
$this->asTranslations[$sLang][$sTransKey] = $sTransText;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -113,5 +120,3 @@ class Translator extends PhpObject
|
|||||||
return self::LANG_FOLDER.$sLang.self::LANG_EXT;
|
return self::LANG_FOLDER.$sLang.self::LANG_EXT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
|
||||||
Reference in New Issue
Block a user