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
    • ContentSitemapHtml
    • ContentSitemapXml
    • ContentUserForum
    • NavigationTop
    • ScriptCookieDirective
  • mpAutoloaderClassMap
  • None
  • PHP
  • Plugin
    • ContentAllocation
    • CronjobOverview
    • FormAssistant
    • FrontendLogic
    • FrontendUsers
    • Linkchecker
    • ModRewrite
    • Newsletter
    • Repository
      • FrontendNavigation
      • KeywordDensity
    • SIWECOS
    • 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:      *
 80:      * @return string
 81:      *         Returns the translation
 82:      *
 83:      * @throws cException
 84:      */
 85:     public static function __($string, $domain = 'contenido') {
 86:         return self::translate($string, $domain);
 87:     }
 88: 
 89:     /**
 90:      * Returns translation of a specific text
 91:      *
 92:      * @param string $string
 93:      *         The string to translate
 94:      * @param string $domain [optional]
 95:      *         The domain to look up
 96:      *
 97:      * @return string
 98:      *         Returns the translation
 99:      *
100:      * @throws cException
101:      *         if this is the backend mode and the $belang is not set
102:      */
103:     public static function translate($string, $domain = 'contenido') {
104:         global $cfg, $belang, $contenido;
105: 
106:         // Auto initialization
107:         if (!self::$_i18nData['language']) {
108:             if (!isset($belang)) {
109:                 if ($contenido) {
110:                     throw new cException('init $belang is not set');
111:                 }
112:                 // Needed - otherwise this won't work
113:                 $belang = false;
114:             }
115: 
116:             // CON-2165
117:             // initialise localisation of plugins correctly in frontend
118:             if ($domain === 'contenido') {
119:                 self::init($cfg['path']['contenido_locale'], $belang, $domain);
120:             } else {
121:                 if (empty($belang)) {
122:                     $oApiLang = cRegistry::getLanguage();
123:                     $language = $oApiLang->getProperty('language', 'code');
124:                     $country = $oApiLang->getProperty('country', 'code');
125: 
126:                     $locale = $language . '_' . cString::toUpperCase($country);
127:                     self::init($cfg['path']['contenido'] . $cfg['path']['plugins'] . $domain . '/locale/', $locale, $domain);
128:                 } else {
129:                     self::init($cfg['path']['contenido'] . $cfg['path']['plugins'] . $domain . '/locale/', $belang, $domain);
130:                 }
131:             }
132:         }
133: 
134:         // Is emulator to use?
135:         if (!$cfg['native_i18n']) {
136:             $ret = self::emulateGettext($string, $domain);
137:             // hopefully a proper replacement for
138:             // mb_convert_encoding($string, 'HTML-ENTITIES', 'utf-8');
139:             // see http://stackoverflow.com/q/11974008
140:             $ret = htmlspecialchars_decode(utf8_decode(conHtmlentities($ret, ENT_COMPAT, 'utf-8')));
141:             return $ret;
142:         }
143: 
144:         // Try to use native gettext implementation
145:         if (extension_loaded('gettext')) {
146:             if (function_exists('dgettext')) {
147:                 if ($domain != 'contenido') {
148:                     $translation = dgettext($domain, $string);
149:                     return $translation;
150:                 } else {
151:                     return gettext($string);
152:                 }
153:             }
154:         }
155: 
156:         // Emulator as fallback
157:         $ret = self::emulateGettext($string, $domain);
158:         if (cString::isUtf8($ret)) {
159:             $ret = utf8_decode($ret);
160:         }
161:         return $ret;
162:     }
163: 
164:     /**
165:      * Returns the current language (if already defined)
166:      *
167:      * @return string|false
168:      */
169:     public static function getLanguage() {
170:         return (self::$_i18nData['language']) ? self::$_i18nData['language'] : false;
171:     }
172: 
173:     /**
174:      * Returns list of registered domains
175:      *
176:      * @return array
177:      */
178:     public static function getDomains() {
179:         return self::$_i18nData['domains'];
180:     }
181: 
182:     /**
183:      * Returns list of cached tranlation files
184:      *
185:      * @return array
186:      */
187:     public static function getFiles() {
188:         return self::$_i18nData['files'];
189:     }
190: 
191:     /**
192:      * Returns list of cached tranlations
193:      *
194:      * @return array
195:      */
196:     public static function getCache() {
197:         return self::$_i18nData['cache'];
198:     }
199: 
200:     /**
201:      * Resets cached translation data (language, domains, files, and cache)
202:      */
203:     public static function reset() {
204:         self::$_i18nData['language'] = NULL;
205:         self::$_i18nData['domains'] = array();
206:         self::$_i18nData['files'] = array();
207:         self::$_i18nData['cache'] = array();
208:     }
209: 
210:     /**
211:      * Emulates GNU gettext
212:      *
213:      * @param string $string
214:      *                       The string to translate
215:      * @param string $domain [optional]
216:      *                       The domain to look up
217:      *
218:      * @return string
219:      *         Returns the translation
220:      *
221:      * @throws cInvalidArgumentException
222:      */
223:     public static function emulateGettext($string, $domain = 'contenido') {
224:         if ($string == '') {
225:             return '';
226:         }
227: 
228:         if (!isset(self::$_i18nData['cache'][$domain])) {
229:             self::$_i18nData['cache'][$domain] = array();
230:         }
231:         if (isset(self::$_i18nData['cache'][$domain][$string])) {
232:             return self::$_i18nData['cache'][$domain][$string];
233:         }
234:         if (!isset(self::$_i18nData['domains'][$domain])) {
235:             return $string;
236:         }
237: 
238:         $translationFile = self::$_i18nData['domains'][$domain] . self::$_i18nData['language'] . '/LC_MESSAGES/' . $domain . '.po';
239:         if (!cFileHandler::exists($translationFile)) {
240:             return $string;
241:         }
242: 
243:         if (!isset(self::$_i18nData['files'][$domain])) {
244:             self::$_i18nData['files'][$domain] = self::_loadTranslationFile($translationFile);
245:         }
246: 
247:         $stringStart = cString::findFirstPos(self::$_i18nData['files'][$domain], '"' . str_replace(array(
248:             "\n",
249:             "\r",
250:             "\t"
251:         ), array(
252:             '\n',
253:             '\r',
254:             '\t'
255:         ), $string) . '"');
256:         if ($stringStart === false) {
257:             return $string;
258:         }
259: 
260:         $matches = array();
261:         $quotedString = preg_quote(str_replace(array(
262:             "\n",
263:             "\r",
264:             "\t"
265:         ), array(
266:             '\n',
267:             '\r',
268:             '\t'
269:         ), $string), '/');
270:         $result = preg_match("/msgid.*\"(" . $quotedString . ")\"(?:\s*)?\nmsgstr(?:\s*)\"(.*)\"/", self::$_i18nData['files'][$domain], $matches);
271:         // Old:
272:         // preg_match("/msgid.*\"".preg_quote($string,"/")."\".*\nmsgstr(\s*)\"(.*)\"/",
273:         // self::$_i18nData['files'][$domain], $matches);
274: 
275:         if ($result && !empty($matches[2])) {
276:             // Translation found, cache it
277:             self::$_i18nData['cache'][$domain][$string] = stripslashes(str_replace(array(
278:                 '\n',
279:                 '\r',
280:                 '\t'
281:             ), array(
282:                 "\n",
283:                 "\r",
284:                 "\t"
285:             ), $matches[2]));
286:         } else {
287:             // Translation not found, cache original string
288:             self::$_i18nData['cache'][$domain][$string] = $string;
289:         }
290: 
291:         return self::$_i18nData['cache'][$domain][$string];
292:     }
293: 
294:     /**
295:      * Registers a new i18n domain.
296:      *
297:      * @param string $domain
298:      *         Domain to bind to
299:      * @param string $localePath
300:      *         Path to the locales
301:      */
302:     public static function registerDomain($domain, $localePath) {
303:         if (function_exists('bindtextdomain')) {
304:             // Bind the domain 'contenido' to our locale path
305:             bindtextdomain($domain, $localePath);
306:         }
307:         self::$_i18nData['domains'][$domain] = $localePath;
308:     }
309: 
310:     /**
311:      * Loads gettext translation and file does some operations like stripping
312:      * comments on the content.
313:      *
314:      * @param string $translationFile
315:      *
316:      * @return string
317:      *         The preparend translation file content
318:      *
319:      * @throws cInvalidArgumentException
320:      */
321:     protected static function _loadTranslationFile($translationFile) {
322:         $content = cFileHandler::read($translationFile);
323: 
324:         // Normalize eol chars
325:         $content = str_replace("\n\r", "\n", $content);
326:         $content = str_replace("\r\n", "\n", $content);
327: 
328:         // Remove comment lines
329:         $content = preg_replace('/^#.+\n/m', '', $content);
330: 
331:         // Prepare for special po edit format
332:         /*
333:          * Something like: #, php-format msgid "" "Hello %s,\n" "\n" "you've got
334:          * a new reminder for the client '%s' at\n" "%s:\n" "\n" "%s" msgstr ""
335:          * "Hallo %s,\n" "\n" "du hast eine Wiedervorlage erhalten für den
336:          * Mandanten '%s' at\n" "%s:\n" "\n" "%s" has to be converted to: msgid
337:          * "Hello %s,\n\nyou've got a new reminder for the client '%s'
338:          * at\n%s:\n\n%s" msgstr "Hallo %s,\n\ndu hast eine Wiedervorlage
339:          * erhalten für den Mandanten '%s' at\n%s:\n\n%s"
340:          */
341:         // assemble broken long message lines (remove double quotes with a line
342:         // break in between, e.g. "\n")
343:         $content = preg_replace('/(""\\s+")/m', '"', $content);
344:         // replace line breaks followed by a whitespace character against a line
345:         // break
346:         $content = preg_replace('/\\n"\\s+"/m', '\\n', $content);
347:         // remove multiple line breaks
348:         $content = preg_replace('/("\n+")/m', '', $content);
349:         // remove the backslash from double quotes (\"foobar\" -> "foobar")
350:         $content = preg_replace('/(\\\")/m', '"', $content);
351: 
352:         return $content;
353:     }
354: }
CMS CONTENIDO 4.10.1 API documentation generated by ApiGen 2.8.0