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