diff --git a/README.md b/README.md index 5b4fcaa..4b4e7f1 100755 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ Adds language redirection to Symphony. -- Version: 1.0.3 -- Date: 2011-07-02 +- Version: 1.0.4 +- Date: 2011-10-27 - Requirements: Symphony 2.1.x - Author: Jonas Coch, jonas@klaftertief.de, Vlad Ghita, vlad_micutul@yahoo.com - GitHub Repository: @@ -44,4 +44,7 @@ For developers: Have a look at `lib/class.languageredirect.php` for using langua - Separated language identification from page redirect. **Version 1.0.3** -- Changed static method `cleanLanguageCodes` to non-static in `LanguageRedirect` class. \ No newline at end of file +- Changed static method `cleanLanguageCodes` to non-static in `LanguageRedirect` class. + +**Version 1.0.4** +- Fixed a redirect loop when removing a language from preferences, that language is still matched by cookie reference. \ No newline at end of file diff --git a/events/event.language_redirect.php b/events/event.language_redirect.php index 547e081..193c6a9 100755 --- a/events/event.language_redirect.php +++ b/events/event.language_redirect.php @@ -1,134 +1,67 @@ -Error

You cannot directly access this file

'); - - require_once(TOOLKIT . '/class.event.php'); - require_once(EXTENSIONS . '/language_redirect/lib/class.languageredirect.php'); - - Class eventlanguage_redirect extends Event{ - - const ROOTELEMENT = 'language-redirect'; - - public static function about(){ - return array( - 'name' => __('Language Redirect'), - 'author' => array( - array( 'name' => 'Jonas Coch', - 'website' => 'http://klaftertief.de', - 'email' => 'jonas@klaftertief.de' - ), - - array( 'name' => 'Vlad Ghita', - 'email' => 'vlad_micutul@yahoo.com' - ), - ), - 'version' => '1.1', - 'release-date' => '2011-06-15', - 'trigger-condition' => ''); - } - - public function load(){ - return $this->__trigger(); - } - - public static function documentation(){ - return __('This event redirects users to a language version of the page depending on browser settings or cookies.'); - } - - protected function __trigger(){ - $supported_language_codes = LanguageRedirect::instance()->getSupportedLanguageCodes(); - - // only do something when there is a set of supported languages defined - if ( !empty($supported_language_codes) ) { - - $current_language_code = LanguageRedirect::instance()->getLanguageCode(); - - // no redirect, set current language and region in cookie - if (isset($current_language_code) and in_array($current_language_code, $supported_language_codes)) { - $Cookie = new Cookie(__SYM_COOKIE_PREFIX_ . 'language-redirect', TWO_WEEKS, __SYM_COOKIE_PATH__); - $Cookie->set('language', LanguageRedirect::instance()->getLanguage()); - $Cookie->set('region', LanguageRedirect::instance()->getRegion()); - } - - // redirect to language-code depending on cookie or browser settings - else { - $current_path = !isset($current_language_code) ? $this->_env['param']['current-path'] : substr($this->_env['param']['current-path'],strlen($current_language_code)+1); - $browser_languages = $this->getBrowserLanguages(); - foreach ($browser_languages as $language) { - if (in_array($language, $supported_language_codes)) { - $in_browser_languages = true; - $browser_language = $language; - break; - }; - } - $Cookie = new Cookie(__SYM_COOKIE_PREFIX_ . 'language-redirect', TWO_WEEKS, __SYM_COOKIE_PATH__); - $cookie_language_code = $Cookie->get('language'); - if (strlen($cookie_language_code) > 0) { - $language_code = $Cookie->get('region') ? $cookie_language_code.'-'.$Cookie->get('region') : $cookie_language_code; - } - elseif ($in_browser_languages) { - $language_code = $browser_language; - } - else { - $language_code = $supported_language_codes[0]; - } - // redirect and exit - header('Location: '.$this->_env['param']['root'].'/'.$language_code.'/'.$current_path); - die(); - } - - $all_languages = LanguageRedirect::instance()->getAllLanguages(); - - $result = new XMLElement('language-redirect'); - - $current_language_xml = new XMLElement('current-language', $all_languages[$current_language_code] ? $all_languages[$current_language_code] : $current_language_code); - $current_language_xml->setAttribute('handle', $current_language_code); - $result->appendChild($current_language_xml); - - $supported_languages_xml = new XMLElement('supported-languages'); - foreach($supported_language_codes as $language) { - $language_code = new XMLElement('item', $all_languages[$language] ? $all_languages[$language] : $language); - $language_code->setAttribute('handle', $language); - $supported_languages_xml->appendChild($language_code); - } - $result->appendChild($supported_languages_xml); - - return $result; - } - - return false; - } - - /** - * Get browser languages - * - * Return languages accepted by browser as an array sorted by priority - * @return array language codes, e. g. 'en' - */ - public static function getBrowserLanguages() { - static $languages; - if(is_array($languages)) return $languages; - - $languages = array(); - - if(strlen(trim($_SERVER['HTTP_ACCEPT_LANGUAGE'])) < 1) return $languages; - if(!preg_match_all('/(\w+(?:-\w+)?,?)+(?:;q=(?:\d+\.\d+))?/', preg_replace('/\s+/', '', $_SERVER['HTTP_ACCEPT_LANGUAGE']), $matches)) return $languages; - - $priority = 1.0; - $languages = array(); - foreach($matches[0] as $def){ - list($list, $q) = explode(';q=', $def); - if(!empty($q)) $priority=floatval($q); - $list = explode(',', $list); - foreach($list as $lang){ - $languages[$lang] = $priority; - $priority -= 0.000000001; - } - } - arsort($languages); - $languages = array_keys($languages); - // return list sorted by descending priority, e.g., array('en-gb','en'); - return $languages; - } - - } +Error

You cannot directly access this file

'); + + require_once(TOOLKIT . '/class.event.php'); + require_once(EXTENSIONS . '/language_redirect/lib/class.languageredirect.php'); + + Class eventlanguage_redirect extends Event{ + + const ROOTELEMENT = 'language-redirect'; + + public static function about(){ + return array( + 'name' => __('Language Redirect'), + 'author' => array( + array( 'name' => 'Jonas Coch', + 'website' => 'http://klaftertief.de', + 'email' => 'jonas@klaftertief.de' + ), + + array( 'name' => 'Vlad Ghita', + 'email' => 'vlad_micutul@yahoo.com' + ), + ), + 'version' => '1.1', + 'release-date' => '2011-06-15', + 'trigger-condition' => ''); + } + + public function load(){ + return $this->__trigger(); + } + + public static function documentation(){ + return __('This event redirects users to a language version of the page depending on browser settings or cookies.'); + } + + protected function __trigger(){ + $supported_language_codes = LanguageRedirect::instance()->getSupportedLanguageCodes(); + + // only do something when there is a set of supported languages defined + if ( !empty($supported_language_codes) ) { + $current_language_code = LanguageRedirect::instance()->determineLanguageCode(); + + $all_languages = LanguageRedirect::instance()->getAllLanguages(); + + $result = new XMLElement('language-redirect'); + + $current_language_xml = new XMLElement('current-language', $all_languages[$current_language_code] ? $all_languages[$current_language_code] : $current_language_code); + $current_language_xml->setAttribute('handle', $current_language_code); + $result->appendChild($current_language_xml); + + $supported_languages_xml = new XMLElement('supported-languages'); + foreach($supported_language_codes as $language) { + $language_code = new XMLElement('item', $all_languages[$language] ? $all_languages[$language] : $language); + $language_code->setAttribute('handle', $language); + $supported_languages_xml->appendChild($language_code); + } + $result->appendChild($supported_languages_xml); + + return $result; + } + + return false; + } + + } diff --git a/extension.driver.php b/extension.driver.php index 330d425..9c190de 100755 --- a/extension.driver.php +++ b/extension.driver.php @@ -1,12 +1,20 @@ Error

You cannot directly access this file

'); + + + require_once(EXTENSIONS . '/language_redirect/lib/class.languageredirect.php'); + Class extension_language_redirect extends Extension{ + private $resolved = false; + public function about(){ return array( 'name' => 'Language Redirect', - 'version' => '1.0.3', - 'release-date' => '2011-07-02', + 'version' => '1.0.4', + 'release-date' => '2011-10-27', 'author' => array( array( 'name' => 'Jonas Coch', @@ -23,6 +31,12 @@ public function about(){ public function getSubscribedDelegates(){ return array( + array( + 'page' => '/frontend/', + 'delegate' => 'FrontendPrePageResolve', + 'callback' => 'detectLanguage' + ), + array( 'page' => '/system/preferences/', 'delegate' => 'AddCustomPreferenceFieldsets', @@ -37,6 +51,41 @@ public function getSubscribedDelegates(){ ); } + public function detectLanguage($context){ + if( !$this->resolved ){ + $this->resolved = true; + + // if language is already set do nothing + $language = LanguageRedirect::instance()->getLanguage(); + $region = LanguageRedirect::instance()->getRegion(); + + if( empty($language) ){ + // get language + $current_language_code = LanguageRedirect::instance()->determineLanguageCode(); + $current_language_code_arr = explode('-', $current_language_code); + + $language = empty($current_language_code_arr[0]) ? '' : $current_language_code_arr[0]; + $region = empty($current_language_code_arr[1]) ? '' : $current_language_code_arr[1]; + + // store references + LanguageRedirect::instance()->setLanguage($language); + LanguageRedirect::instance()->setRegion($region); + + // mantain compatibility with previous implementations + $_GET['language'] = $language; + $_GET['region'] = $region; + + $_REQUEST['language'] = $language; + $_REQUEST['region'] = $region; + } + + // save to Cookie + $Cookie = new Cookie(__SYM_COOKIE_PREFIX_ . 'language-redirect', TWO_WEEKS, __SYM_COOKIE_PATH__); + $Cookie->set('language', $language); + $Cookie->set('region', $region); + } + } + public function appendPreferences($context){ $group = new XMLElement('fieldset'); $group->setAttribute('class', 'settings'); @@ -64,8 +113,8 @@ public function install(){ public function enable(){ $languageCodes = Symphony::Configuration()->get('language_codes', 'language_redirect'); - - return self::__updateRewriteRules('edit', $languageCodes); + + return self::__updateRewriteRules('create') && self::__updateRewriteRules('edit', $languageCodes); } public function update($previousVersion){ diff --git a/lib/class.languageredirect.php b/lib/class.languageredirect.php index 79ae852..c4e5a5f 100644 --- a/lib/class.languageredirect.php +++ b/lib/class.languageredirect.php @@ -8,7 +8,6 @@ private $_language; private $_region; - private $_language_code; private $_supported_language_codes; // I don't know those languages, so if You know for sure that browser uses different code, @@ -74,12 +73,12 @@ 'fo' => 'føroyskt', // Faeroese 'fa' => 'فارسی', // Farsi 'fi' => 'suomi', // Finnish - 'fr-be' => 'français (Belgium)', // French (Belgium) - 'fr-ca' => 'français canadien', // French (Canada) - 'fr-lu' => 'français (Luxembourg)', // French - 'fr-mc' => 'français (Monaco)', // French - 'fr-ch' => 'français (Switzerland)', // French - 'fr' => 'français', // French + 'fr-be' => 'Français (Belgium)', // French (Belgium) + 'fr-ca' => 'Français canadien', // French (Canada) + 'fr-lu' => 'Français (Luxembourg)', // French + 'fr-mc' => 'Français (Monaco)', // French + 'fr-ch' => 'Français (Switzerland)', // French + 'fr' => 'Français', // French 'ff' => 'Fulfulde, Pulaar, Pular', // Fula, Fulah, Fulani 'gl' => 'Galego', // Galician 'gd' => 'Gàidhlig', // Gaelic (Scottish) @@ -195,25 +194,37 @@ 'zu' => 'isiZulu', // Zulu ); + private function __construct(){ $this->_language = General::sanitize($_REQUEST['language']); $this->_region = General::sanitize($_REQUEST['region']); - $this->_language_code = $this->_region ? $this->_language.'-'.$this->_region : $this->_language; $supported_language_codes = explode(',', General::sanitize(Symphony::Configuration()->get('language_codes', 'language_redirect'))); $this->_supported_language_codes = $this->cleanLanguageCodes($supported_language_codes); } + + public static function instance() { - if (!self::$_instance) {self::$_instance = new self(); } + if (!self::$_instance) { + self::$_instance = new self(); + } return self::$_instance; } + public function setLanguage($language){ + $this->_language = $language; + } + + public function setRegion($region){ + $this->_region = $region; + } + /** * Get current language. - * + * * @return string */ public function getLanguage(){ @@ -222,7 +233,7 @@ public function getLanguage(){ /** * Get current region. - * + * * @return string */ public function getRegion(){ @@ -231,16 +242,16 @@ public function getRegion(){ /** * Get current language code. - * + * * @return string */ public function getLanguageCode(){ - return $this->_language_code; + return $this->_region ? $this->_language.'-'.$this->_region : $this->_language; } /** * Get supported language codes. - * + * * @return array */ public function getSupportedLanguageCodes(){ @@ -254,6 +265,61 @@ public function getAllLanguages(){ return $this->_languages; } + /** + * Will determine current language. See method body for order. + */ + public function determineLanguageCode(){ + $supported_language_codes = LanguageRedirect::instance()->getSupportedLanguageCodes(); + + // 1. from Cookie + $Cookie = new Cookie(__SYM_COOKIE_PREFIX_ . 'language-redirect', TWO_WEEKS, __SYM_COOKIE_PATH__); + $cookie_language_code = $Cookie->get('language'); + if( strlen($cookie_language_code) > 0 && in_array($cookie_language_code, $supported_language_codes) ){ + return $Cookie->get('region') ? $cookie_language_code.'-'.$Cookie->get('region') : $cookie_language_code; + } + + // 2. from Geolocation Service + if( Symphony::ExtensionManager()->fetchStatus('geolocation_service') == EXTENSION_ENABLED ){ + require_once(EXTENSIONS . '/geolocation_service/extension.driver.php'); + + $ip = $this->_env['param']['env'] == 'dev' ? '92.81.156.219' : $_SERVER['REMOTE_ADDR']; + + $info = extension_geolocation_service::lookup($ip); + + if( $info instanceof XMLElement ){ + $language_code = ''; + + foreach( $info->getChildren() as $child ){ + /*@var $child XMLElement */ + + if( $child->getName() == 'country' ){ + $language_code = strtolower($child->getValue()); + break; + } + } + + if( !empty($language_code) && in_array($language_code, $supported_language_codes) ){ + return $language_code; + } + } + } + + // 3. from Brower Languages + foreach( $this->_getBrowserLanguages() as $language ){ + if( in_array($language, $supported_language_codes) ){ + return $language; + }; + } + + // 4. try english + if( in_array('en', $supported_language_codes) ){ + return 'en'; + } + + // 5. defaults to Reference language + return $supported_language_codes[0]; + } + /*------------------------------------------------------------------------------------------------*/ @@ -265,4 +331,35 @@ public function cleanLanguageCodes($language_codes){ return $clean; } + + + + /** + * Get browser languages + * + * Return languages accepted by browser as an array sorted by priority + * @return array language codes, e. g. 'en' + */ + private function _getBrowserLanguages() { + $languages = array(); + + if(strlen(trim($_SERVER['HTTP_ACCEPT_LANGUAGE'])) < 1) return $languages; + if(!preg_match_all('/(\w+(?:-\w+)?,?)+(?:;q=(?:\d+\.\d+))?/', preg_replace('/\s+/', '', $_SERVER['HTTP_ACCEPT_LANGUAGE']), $matches)) return $languages; + + $priority = 1.0; + $languages = array(); + foreach($matches[0] as $def){ + list($list, $q) = explode(';q=', $def); + if(!empty($q)) $priority=floatval($q); + $list = explode(',', $list); + foreach($list as $lang){ + $languages[$lang] = $priority; + $priority -= 0.000000001; + } + } + arsort($languages); + $languages = array_keys($languages); + // return list sorted by descending priority, e.g., array('en-gb','en'); + return $languages; + } }