* @package Kernel
* @version $Id$
*/
?>
setFileRoot();
$this->init();
if (!is_null($strLanguageID)) {
$this->load($strLanguageID);
} else if (!isset($_SESSION['vcdlang'])) {
$this->load($this->detectBrowserLanguage());
}
}
/**
* Initialize the class, load the index file with available languages.
*
*/
private function init() {
try {
// Check if a restricted language subset should be used ..
if ($this->loadRestricted()) {
return;
}
// Nope .. no restrictions defined, using the defaults
if (file_exists(self::$LANGUAGE_ROOT.self::PRIMARY_LANGINDEX )) {
$xmlStream = simplexml_load_file(self::$LANGUAGE_ROOT.self::PRIMARY_LANGINDEX );
foreach ($xmlStream->language as $node) {
$this->arrLanguages[] = new _VCDLanguageItem($node);
}
}
} catch (Exception $ex) {
throw $ex;
}
}
/**
* Try to load languages based on restrictions if any ..
* Returns true if restrictions were found, otherwise false.
*
* @return bool
*/
private function loadRestricted() {
try {
$fileroot = str_replace('classes', '', dirname(__FILE__));
if (file_exists($fileroot.self::USERSET_LANGINDEX)) {
$xmlStream = simplexml_load_file($fileroot.self::USERSET_LANGINDEX);
foreach ($xmlStream->language as $node) {
$this->arrLanguages[] = new _VCDLanguageItem($node);
}
$this->isRestricted = true;
return true;
} else {
return false;
}
} catch (Exception $ex) {
throw $ex;
}
}
/**
* Get all the languages loaded within the class,
* returns array of _VCDLanguageItem
*
* @return array
*/
public function getAllLanguages() {
return $this->arrLanguages;
}
/**
* Set restrictions on what languages to display
*
* @param array $arrRestrictionIDs | Array of language ID's
*/
public function setRestrictions($arrRestrictionIDs) {
try {
$newRestrictions = array();
foreach($this->getTranslationFiles() as $transObj) {
if (in_array($transObj['id'], $arrRestrictionIDs)) {
array_push($newRestrictions, $transObj);
}
}
if (sizeof($newRestrictions) < 1) {
throw new Exception('At least 1 language must be defined.');
}
$strXML = "\n";
$strXML .= "";
foreach($newRestrictions as $transObj) {
$strXML .= "\t\n";
$strXML .= "\t\t{$transObj['name']}\n";
$strXML .= "\t\t\n";
$strXML .= "\t\t\n";
$strXML .= "\t\n";
}
$strXML .= "";
$xmlObj = simplexml_load_string($strXML);
$xmlFile = str_replace('classes', '', dirname(__FILE__)).self::USERSET_LANGINDEX;
$xmlObj->asXML($xmlFile);
} catch (Exception $ex) {
throw $ex;
}
}
/**
* Check if the language system is running in restriction mode
*
* @return bool
*/
public function isRestricted() {
return $this->isRestricted;
}
/**
* Set the correct language file root
*
*/
private function setFileRoot() {
$fileroot = str_replace('classes', '', dirname(__FILE__));
self::$LANGUAGE_ROOT = $fileroot.'includes'.DIRECTORY_SEPARATOR.'languages'.DIRECTORY_SEPARATOR;
}
/**
* Load a translation based on the parameter ID.
* For example is_IS for Icelandic.
*
* @param string $strLanguageID
*/
public function load($strLanguageID) {
foreach($this->arrLanguages as $obj) {
if (strcmp($obj->getID(), $strLanguageID) == 0) {
$this->primaryLanguage = &$obj;
$this->primaryLanguage->getKeys();
// Store the current selection in Session
$_SESSION['vcdlang'] = $strLanguageID;
return;
}
}
// Language not found .. try to load the default one ..
foreach($this->arrLanguages as $obj) {
if (strcmp($obj->getID(), self::FALLBACK_ID) == 0) {
$this->primaryLanguage = &$obj;
$this->primaryLanguage->getKeys();
// Store the current selection in Session
$_SESSION['vcdlang'] = self::FALLBACK_ID;
return;
}
}
// English not available in the list .. then force en_EN include
try {
$this->primaryLanguage = new _VCDLanguageItem($this->createDefaultElement());
$this->primaryLanguage->getKeys();
$this->fallbackLanguage = &$this->primaryLanguage;
$_SESSION['vcdlang'] = self::FALLBACK_ID;
} catch (Exception $ex) {
throw new Exception("Could not load language with ID " . $strLanguageID);
}
}
/**
* Get the translation for the requested key, if key is not found in the
* Primary language object, translation is seeked from the fallback Language object.
*
* @param string $key | The Key for the language phrase
* @return string | The translated phrase
*/
public function doTranslate($key) {
$strValue = $this->primaryLanguage[$key];
if (!is_null($strValue)) {
return $strValue;
} else {
return $this->fallbackTranslate($key);
}
}
/**
* Get all translation keys and values for javascripts.
* They all begin with "js." in the xml files.
*
* @return array
*/
public function doJavascriptKeys() {
// First get missing keys if any and if main lang is not English
if ($this->primaryLanguage->getID() == self::FALLBACK_ID) {
return $this->primaryLanguage->getJsKeys();
} else {
// Trigger load of the fallback language if not loaded
if (!$this->fallbackLanguage instanceof _VCDLanguageItem) {
$this->fallbackTranslate(null);
}
$primaryJsKeys = $this->primaryLanguage->getJsKeys();
$fallbackJsKeys = $this->fallbackLanguage->getJsKeys();
return array_merge($fallbackJsKeys, $primaryJsKeys);
}
}
/**
* Check if the primary language is English
*
* @return bool
*/
public function isEnglish() {
return ($this->primaryLanguage->getID() == self::FALLBACK_ID);
}
/**
* Get the ID of the primary/selected langauge
*
* @return string
*/
public function getPrimaryLanguageID() {
return $this->primaryLanguage->getID();
}
/**
* Get information about all the translations residing in /includes/languages
* Returns array of array's with index [id,filename,name,charset,num]
*
* @return array
*/
public function getTranslationFiles() {
$arrFiles = $this->findfiles(self::$LANGUAGE_ROOT,'/\.(xml)$/');
$arrLangs = array();
foreach ($arrFiles as $file) {
$xmlFile = @simplexml_load_file($file);
if (is_object($xmlFile) && strcmp(basename($file), "languages.xml") != 0) {
$item = array(
'id' => (string)$xmlFile->id,
'filename' => basename($file),
'name' => (string)$xmlFile->name,
'charset' => (string)$xmlFile->charset,
'num' => count($xmlFile->strings->string)
);
array_push($arrLangs, $item);
}
}
return $arrLangs;
}
/**
* Find translation for the current key
*
* @param string $key
* @return string
*/
public static function translate($key) {
return VCDClassFactory::getInstance('VCDLanguage')->doTranslate($key);
}
/**
* Get keys, only for javascript items.
*
* @return array
*/
public static function getJavascriptKeys() {
return VCDClassFactory::getInstance('VCDLanguage')->doJavascriptKeys();
}
/**
* Try to detect the default browser language if no language has been selected.
*
* @return string | The best match for language ID
*/
private function detectBrowserLanguage() {
try {
if (!isset($_SERVER["HTTP_ACCEPT_LANGUAGE"])) {
return self::FALLBACK_ID;
}
$pref=array();
foreach(split(',', $_SERVER["HTTP_ACCEPT_LANGUAGE"]) as $lang) {
if (preg_match('/^([a-z]+).*?(?:;q=([0-9.]+))?/i', $lang.';q=1.0', $split) && isset($split[2])) {
$pref[sprintf("%f%d", $split[2], rand(0,9999))]=strtolower($split[1]);
}
}
krsort($pref);
$a = array();
$b = array();
foreach ($this->arrLanguages as $langObj) {
$tokens = explode('_', $langObj->getID());
array_push($a, $tokens[0]);
$b[$tokens[0]] = $langObj->getID();
}
$items = array_merge(array_intersect($pref, $a), $a);
$bestMatch = array_shift($items);
if (isset($b[$bestMatch])) {
return $b[$bestMatch];
} else {
return self::FALLBACK_ID;
}
} catch (Exception $ex) {
throw $ex;
}
}
/**
* Get the translated value from the fallback language, since it
* was not found in the Primary translation object.
*
* @param string $key | The key for the language phrase
* @return The translated language phrase
*/
private function fallbackTranslate($key) {
$bIsloaded = false;
if (!$this->fallbackLanguage instanceof _VCDLanguageItem) {
foreach($this->arrLanguages as $obj) {
if (strcmp($obj->getID(), self::FALLBACK_ID ) == 0) {
$this->fallbackLanguage = $obj;
$this->fallbackLanguage->getKeys();
$bIsloaded = true;
break;
}
}
}
if (!$bIsloaded) {
// If fallbacklanguge could not be loaded .. then force the load
$this->fallbackLanguage = new _VCDLanguageItem($this->createDefaultElement());
$this->fallbackLanguage->getKeys();
}
$strValue = $this->fallbackLanguage[$key];
if (!is_null($strValue)) {
return $strValue;
} else {
return "undefined";
}
}
private function createDefaultElement() {
$xmlElement = "English";
$xmlElement .= "EnglishKonni";
return simplexml_load_string($xmlElement);
}
/**
* Search folder for files with certain extensions defined in the $fileregex parameter.
*
* @param string $location | The file location to seek
* @param string $fileregex | The regual expression to search by
* @return array
*/
private function findfiles($location='',$fileregex='') {
if (!$location or !is_dir($location) or !$fileregex) {
return false;
}
$matchedfiles = array();
$all = opendir($location);
while ($file = readdir($all)) {
if (is_dir($location.'/'.$file) and $file <> ".." and $file <> ".") {
$subdir_matches = $this->findfiles($location.'/'.$file,$fileregex);
$matchedfiles = array_merge($matchedfiles,$subdir_matches);
unset($file);
}
elseif (!is_dir($location.'/'.$file)) {
if (preg_match($fileregex,$file)) {
array_push($matchedfiles,$location.'/'.$file);
}
}
}
closedir($all);
unset($all);
sort($matchedfiles);
return $matchedfiles;
}
}
/**
* Container for a Language Translation object
*
*/
class _VCDLanguageItem implements ArrayAccess {
CONST LANG_FILE_ROOT = "includes/languages/";
private $id;
private $charset;
private $name;
private $native_name;
private $author;
private $filename;
private $keys = array();
private $jsKeypointer = array();
/**
* Function constructor, gets a single SimpleXMLElement from the language.xml index file.
*
* @param SimpleXMLElement $element
*/
public function __construct(SimpleXMLElement $element) {
$this->id = (string)$element['id'];
$this->charset = (string)$element['charset'];
$this->name = (string)$element->name;
$this->native_name = (string)$element->native;
$this->author = (string)$element->author;
}
/**
* Load the language keys from the XML file
*
*/
private function loadKeys() {
$file = VCDLanguage::$LANGUAGE_ROOT.$this->id.".xml";
if (file_exists($file)) {
$xmlStream = simplexml_load_file($file);
foreach ($xmlStream->strings->string as $node) {
$k = new _VCDLanguageKey($node);
$this->keys[] = $k;
if (strpos($k->getID(),'js.')===0) {
$this->jsKeypointer[] = $k;
}
}
} else {
throw new Exception("Could not load language file " . $file . ".xml");
}
}
/**
* Get the language Keys for this language
*
* @return array
*/
public function getKeys() {
if (sizeof($this->keys) == 0) {
$this->loadKeys();
}
return $this->keys;
}
/**
* Get the javascript only languages keys.
*
* @return array
*/
public function getJsKeys() {
if (sizeof($this->keys) == 0) {
$this->loadKeys();
}
return $this->jsKeypointer;
}
/**
* Get the Language ID
*
* @return string
*/
public function getID() {
return $this->id;
}
/**
* Get the Language charset
*
* @return string
*/
public function getCharset() {
return $this->charset;
}
/**
* Get the Language name
*
* @return string
*/
public function getName() {
return $this->name;
}
/**
* Get the language name on the native tongue
*
* @return string
*/
public function getNativeName() {
return $this->native_name;
}
/**
* Get the original author of the translation
*
* @return string
*/
public function getAuthor() {
return $this->author;
}
/**
* Set the Obj filename
*
* @param string $strFileName
*/
public function setFileName($strFileName) {
$this->filename = $strFileName;
}
/**
* Get the filename of the Xml file
*
* @return string
*/
public function getFileName() {
return $this->filename;
}
/**
* Defined by ArrayAccess interface
* Set a value given it's key e.g. $A['title'] = 'foo';
* @param mixed key (string or integer)
* @param mixed value
* @return void
*/
public function offsetSet($key, $value) {
throw new Exception("Language Keys are read only!");
}
/**
* Defined by ArrayAccess interface
* Return a value given it's key e.g. echo $A['title'];
* @param mixed key (string or integer)
* @return mixed value
*/
public function offsetGet($key) {
foreach ($this->keys as $keyObj) {
if (strcmp($keyObj->getID(), $key) == 0) {
return $keyObj->getKey();
}
}
return null;
}
/**
* Defined by ArrayAccess interface
* Unset a value by it's key e.g. unset($A['title']);
* @param mixed key (string or integer)
* @return void
*/
public function offsetUnset($key) {
throw new Exception("Language Keys are read only!");
}
/**
* Defined by ArrayAccess interface
* Check value exists, given it's key e.g. isset($A['title'])
* @param mixed key (string or integer)
* @return boolean
*/
public function offsetExists($offset) {
foreach ($this->keys as $keyObj) {
if (strcmp($keyObj->getID(), $key) == 0) {
return true;
}
}
return false;
}
}
class _VCDLanguageKey {
private $id;
private $key;
public function __construct(SimpleXMLElement $element) {
$this->id = (string)$element['id'];
$this->key = (string)$element;
}
/**
* Get the Key ID
*
* @return string
*/
public function getID() {
return $this->id;
}
/**
* Get the Key value
*
* @return string
*/
public function getKey() {
return $this->key;
}
}
?>