Overview

Packages

  • CONTENIDO
  • Core
    • Authentication
    • Backend
    • Cache
    • CEC
    • Chain
    • ContentType
    • Database
    • Debug
    • Exception
    • Frontend
      • Search
      • URI
      • Util
    • GenericDB
      • Model
    • GUI
      • HTML
    • I18N
    • LayoutHandler
    • Log
    • Security
    • Session
    • Util
    • Validation
    • Versioning
    • XML
  • Module
    • ContentRssCreator
    • ContentSitemapHtml
    • ContentSitemapXml
    • ContentUserForum
    • NavigationTop
    • ScriptCookieDirective
  • mpAutoloaderClassMap
  • None
  • Plugin
    • ContentAllocation
    • CronjobOverview
    • FormAssistant
    • FrontendLogic
    • FrontendUsers
    • Linkchecker
    • ModRewrite
    • Newsletter
    • Repository
      • FrontendNavigation
      • KeywordDensity
    • SearchSolr
    • SmartyWrapper
    • UrlShortener
    • UserForum
    • Workflow
  • PluginManager
  • Setup
    • Form
    • GUI
    • Helper
      • Environment
      • Filesystem
      • MySQL
      • PHP
    • UpgradeJob

Classes

  • cI18n

Functions

  • i18n
  • i18nEmulateGettext
  • i18nGetAvailableLanguages
  • i18nInit
  • i18nMatchBrowserAccept
  • i18nRegisterDomain
  • i18nStripAcceptLanguages
  • mi18n
  • trans
  • Overview
  • Package
  • Class
  • Tree
  • Deprecated
  • Todo
  1: <?php
  2: /**
  3:  * This file contains the the I18N class.
  4:  *
  5:  * @package Core
  6:  * @subpackage I18N
  7:  * @author Murat Purc <murat@purc.de>
  8:  * @copyright four for business AG <www.4fb.de>
  9:  * @license http://www.contenido.org/license/LIZENZ.txt
 10:  * @link http://www.4fb.de
 11:  * @link http://www.contenido.org
 12:  */
 13: 
 14: defined('CON_FRAMEWORK') || die('Illegal call: Missing framework initialization - request aborted.');
 15: 
 16: /**
 17:  * Internationalization (i18n) class.
 18:  *
 19:  * @package Core
 20:  * @subpackage I18N
 21:  */
 22: class cI18n {
 23: 
 24:     /**
 25:      * i18n related assoziative data cache.
 26:      *
 27:      * @var array
 28:      */
 29:     protected static $_i18nData = array(
 30:         'language' => NULL,
 31:         'domains' => array(),
 32:         'files' => array(),
 33:         'cache' => array()
 34:     );
 35: 
 36:     /**
 37:      * Initializes the i18n.
 38:      *
 39:      * @param string $localePath
 40:      *         Path to the locales
 41:      * @param string $langCode
 42:      *         Language code to set
 43:      * @param string $domain [optional]
 44:      *         Language domain
 45:      */
 46:     public static function init($localePath, $langCode, $domain = 'contenido') {
 47:         if (function_exists('bindtextdomain')) {
 48:             // Bind the domain 'contenido' to our locale path
 49:             bindtextdomain($domain, $localePath);
 50: 
 51:             // Set the default text domain to 'contenido'
 52:             textdomain($domain);
 53: 
 54:             // Half brute-force to set the locale.
 55:             if (!ini_get('safe_mode')) {
 56:                 putenv("LANG=$langCode");
 57:             }
 58: 
 59:             if (defined('LC_MESSAGES')) {
 60:                 setlocale(LC_MESSAGES, $langCode);
 61:             }
 62: 
 63:             if (false === empty($langCode)) {
 64:                 setlocale(LC_CTYPE, $langCode);
 65:             }
 66:         }
 67: 
 68:         self::$_i18nData['domains'][$domain] = $localePath;
 69:         self::$_i18nData['language'] = $langCode;
 70:     }
 71: 
 72:     /**
 73:      * Returns translation of a specific text, wrapper for translate().
 74:      *
 75:      * @param string $string
 76:      *         The string to translate
 77:      * @param string $domain [optional]
 78:      *         The domain to look up
 79:      * @return string
 80:      *         Returns the translation
 81:      */
 82:     public static function __($string, $domain = 'contenido') {
 83:         return self::translate($string, $domain);
 84:     }
 85: 
 86:     /**
 87:      * Returns translation of a specific text
 88:      *
 89:      * @param string $string
 90:      *         The string to translate
 91:      * @param string $domain [optional]
 92:      *         The domain to look up
 93:      * @throws cException
 94:      *         if this is the backend mode and the $belang is not set
 95:      * @return string
 96:      *         Returns the translation
 97:      */
 98:     public static function translate($string, $domain = 'contenido') {
 99:         global $cfg, $belang, $contenido;
100: 
101:         // Auto initialization
102:         if (!self::$_i18nData['language']) {
103:             if (!isset($belang)) {
104:                 if ($contenido) {
105:                     throw new cException('init $belang is not set');
106:                 }
107:                 // Needed - otherwise this won't work
108:                 $belang = false;
109:             }
110: 
111:             // CON-2165
112:             // initialise localisation of plugins correctly in frontend
113:             if ($domain === 'contenido') {
114:                 self::init($cfg['path']['contenido_locale'], $belang, $domain);
115:             } else {
116:                 if (empty($belang)) {
117:                     $oApiLang = cRegistry::getLanguage();
118:                     $language = $oApiLang->getProperty('language', 'code');
119:                     $country = $oApiLang->getProperty('country', 'code');
120: 
121:                     $locale = $language . '_' . strtoupper($country);
122:                     self::init($cfg['path']['contenido'] . $cfg['path']['plugins'] . $domain . '/locale/', $locale, $domain);
123:                 } else {
124:                     self::init($cfg['path']['contenido'] . $cfg['path']['plugins'] . $domain . '/locale/', $belang, $domain);
125:                 }
126:             }
127:         }
128: 
129:         // Is emulator to use?
130:         if (!$cfg['native_i18n']) {
131:             $ret = self::emulateGettext($string, $domain);
132:             // hopefully a proper replacement for
133:             // mb_convert_encoding($string, 'HTML-ENTITIES', 'utf-8');
134:             // see http://stackoverflow.com/q/11974008
135:             $ret = htmlspecialchars_decode(utf8_decode(conHtmlentities($ret, ENT_COMPAT, 'utf-8', false)));
136:             return $ret;
137:         }
138: 
139:         // Try to use native gettext implementation
140:         if (extension_loaded('gettext')) {
141:             if (function_exists('dgettext')) {
142:                 if ($domain != 'contenido') {
143:                     $translation = dgettext($domain, $string);
144:                     return $translation;
145:                 } else {
146:                     return gettext($string);
147:                 }
148:             }
149:         }
150: 
151:         // Emulator as fallback
152:         $ret = self::emulateGettext($string, $domain);
153:         if (cString::isUtf8($ret)) {
154:             $ret = utf8_decode($ret);
155:         }
156:         return $ret;
157:     }
158: 
159:     /**
160:      * Returns the current language (if already defined)
161:      *
162:      * @return string|false
163:      */
164:     public static function getLanguage() {
165:         return (self::$_i18nData['language']) ? self::$_i18nData['language'] : false;
166:     }
167: 
168:     /**
169:      * Returns list of registered domains
170:      *
171:      * @return array
172:      */
173:     public static function getDomains() {
174:         return self::$_i18nData['domains'];
175:     }
176: 
177:     /**
178:      * Returns list of cached tranlation files
179:      *
180:      * @return array
181:      */
182:     public static function getFiles() {
183:         return self::$_i18nData['files'];
184:     }
185: 
186:     /**
187:      * Returns list of cached tranlations
188:      *
189:      * @return array
190:      */
191:     public static function getCache() {
192:         return self::$_i18nData['cache'];
193:     }
194: 
195:     /**
196:      * Resets cached translation data (language, domains, files, and cache)
197:      */
198:     public static function reset() {
199:         self::$_i18nData['language'] = NULL;
200:         self::$_i18nData['domains'] = array();
201:         self::$_i18nData['files'] = array();
202:         self::$_i18nData['cache'] = array();
203:     }
204: 
205:     /**
206:      * Emulates GNU gettext
207:      *
208:      * @param string $string
209:      *         The string to translate
210:      * @param string $domain [optional]
211:      *         The domain to look up
212:      * @return string
213:      *         Returns the translation
214:      */
215:     public static function emulateGettext($string, $domain = 'contenido') {
216:         if ($string == '') {
217:             return '';
218:         }
219: 
220:         if (!isset(self::$_i18nData['cache'][$domain])) {
221:             self::$_i18nData['cache'][$domain] = array();
222:         }
223:         if (isset(self::$_i18nData['cache'][$domain][$string])) {
224:             return self::$_i18nData['cache'][$domain][$string];
225:         }
226: 
227:         $translationFile = self::$_i18nData['domains'][$domain] . self::$_i18nData['language'] . '/LC_MESSAGES/' . $domain . '.po';
228:         if (!cFileHandler::exists($translationFile)) {
229:             return $string;
230:         }
231: 
232:         if (!isset(self::$_i18nData['files'][$domain])) {
233:             self::$_i18nData['files'][$domain] = self::_loadTranslationFile($translationFile);
234:         }
235: 
236:         $stringStart = strpos(self::$_i18nData['files'][$domain], '"' . str_replace(array(
237:             "\n",
238:             "\r",
239:             "\t"
240:         ), array(
241:             '\n',
242:             '\r',
243:             '\t'
244:         ), $string) . '"');
245:         if ($stringStart === false) {
246:             return $string;
247:         }
248: 
249:         $matches = array();
250:         $quotedString = preg_quote(str_replace(array(
251:             "\n",
252:             "\r",
253:             "\t"
254:         ), array(
255:             '\n',
256:             '\r',
257:             '\t'
258:         ), $string), '/');
259:         $result = preg_match("/msgid.*\"(" . $quotedString . ")\"(?:\s*)?\nmsgstr(?:\s*)\"(.*)\"/", self::$_i18nData['files'][$domain], $matches);
260:         // Old:
261:         // preg_match("/msgid.*\"".preg_quote($string,"/")."\".*\nmsgstr(\s*)\"(.*)\"/",
262:         // self::$_i18nData['files'][$domain], $matches);
263: 
264:         if ($result && !empty($matches[2])) {
265:             // Translation found, cache it
266:             self::$_i18nData['cache'][$domain][$string] = stripslashes(str_replace(array(
267:                 '\n',
268:                 '\r',
269:                 '\t'
270:             ), array(
271:                 "\n",
272:                 "\r",
273:                 "\t"
274:             ), $matches[2]));
275:         } else {
276:             // Translation not found, cache original string
277:             self::$_i18nData['cache'][$domain][$string] = $string;
278:         }
279: 
280:         return self::$_i18nData['cache'][$domain][$string];
281:     }
282: 
283:     /**
284:      * Registers a new i18n domain.
285:      *
286:      * @param string $localePath
287:      *         Path to the locales
288:      * @param string $domain
289:      *         Domain to bind to
290:      */
291:     public static function registerDomain($domain, $localePath) {
292:         if (function_exists('bindtextdomain')) {
293:             // Bind the domain 'contenido' to our locale path
294:             bindtextdomain($domain, $localePath);
295:         }
296:         self::$_i18nData['domains'][$domain] = $localePath;
297:     }
298: 
299:     /**
300:      * Loads gettext translation and file does some operations like stripping
301:      * comments on the content.
302:      *
303:      * @param string $translationFile
304:      * @return string
305:      *         The preparend translation file content
306:      */
307:     protected static function _loadTranslationFile($translationFile) {
308:         $content = cFileHandler::read($translationFile);
309: 
310:         // Normalize eol chars
311:         $content = str_replace("\n\r", "\n", $content);
312:         $content = str_replace("\r\n", "\n", $content);
313: 
314:         // Remove comment lines
315:         $content = preg_replace('/^#.+\n/m', '', $content);
316: 
317:         // Prepare for special po edit format
318:         /*
319:          * Something like: #, php-format msgid "" "Hello %s,\n" "\n" "you've got
320:          * a new reminder for the client '%s' at\n" "%s:\n" "\n" "%s" msgstr ""
321:          * "Hallo %s,\n" "\n" "du hast eine Wiedervorlage erhalten für den
322:          * Mandanten '%s' at\n" "%s:\n" "\n" "%s" has to be converted to: msgid
323:          * "Hello %s,\n\nyou've got a new reminder for the client '%s'
324:          * at\n%s:\n\n%s" msgstr "Hallo %s,\n\ndu hast eine Wiedervorlage
325:          * erhalten für den Mandanten '%s' at\n%s:\n\n%s"
326:          */
327:         // assemble broken long message lines (remove double quotes with a line
328:         // break in between, e.g. "\n")
329:         $content = preg_replace('/(""\\s+")/m', '"', $content);
330:         // replace line breaks followed by a whitespace character against a line
331:         // break
332:         $content = preg_replace('/\\n"\\s+"/m', '\\n', $content);
333:         // remove multiple line breaks
334:         $content = preg_replace('/("\n+")/m', '', $content);
335:         // remove the backslash from double quotes (\"foobar\" -> "foobar")
336:         $content = preg_replace('/(\\\")/m', '"', $content);
337: 
338:         return $content;
339:     }
340: }
CMS CONTENIDO 4.9.11 API documentation generated by ApiGen 2.8.0