* @license LGPL * @link http://www.php-tools.net */ /** * patI18n * * Modular translation interface * * Inspired by GNU Gettext {@link http://www.gnu.org/software/gettext/} * and it's PHP implementation, this packages provides an sort of compatible * but modular interface for translation. * * Both, text domains and plural forms are supported as Gettext does it. The * names of the static functions are the same as in Gettext. This way using * it is piece of cake when you are familiar with Gettext. Besides providing * the same interface, allows you to use the normal Gettext tools like * "xgettext" to extract to-be-translated strings etc. * * Still, patI18n is not fixed to utilise Gettext at all. The modular design * allows to connect any kind of translation tool. Furthermore, you can even * use more than one module at the same time. * * @version 0.1.0 * @package patI18n */ class patI18n { /** * complete locale name, e.g. "de_DE@euro" or "de_DE.UTF-8" */ const LOCALE_TYPE_COMPLETE = '1'; /** * just the language name with variant, like "de_DE" */ const LOCALE_TYPE_LANG = '2'; /** * just the short (two) letter language name, like "de" */ const LOCALE_TYPE_SHORT = '4'; /** * List of concrete translation modules * @var array * @see addModule() */ static protected $modules = array(); /** * List of folders where to find modules * @var array */ static protected $includeDirs = array(); /** * Current locale * @var string */ static protected $locale = 'C'; /** * init this class * */ static function staticConstruct() { // standard module folder self::$includeDirs = array(); $dir = dirname(__FILE__) . '/patI18n'; self::addIncludeDir($dir); // Load module base class if (!class_exists('patI18n_Module', false)) { require $dir . '/Module.php'; } } /** * add custom include folder * * Prepend another location of modules and importer classes * * @param $dir * @return int */ static public function addIncludeDir($dir) { self::$includeDirs[] = $dir; return count(self::$includeDirs); } /** * Add localisation module * * Append named module into module chain. The module will be loaded * automatically and configured with the given settings. * * @param string $name module name * @param string $settings list of configuation options * @return bool true on success */ static public function addModule($name, $settings = array()) { $m = self::createModule($name, $settings); self::$modules[] = $m; return true; } /** * Instantiate localization module * * Create module and configure it * * @param string $name * @param array $settings * @return patI18n_Module */ static public function createModule($name, $settings = array()) { return self::create('Module', $name, $settings); } /** * Instantiate message importer * * Create importer * * @param string $name * @param array $settings * @return patI18n_Importer */ static public function createImporter($name, $settings = array()) { // Load module base class if (!interface_exists('patI18n_Importer', false)) { require self::$includeDirs[0] . '/Importer.php'; } return self::create('Importer', $name, $settings); } /** * Instantiate any module or importer * * @param $type * @param $name * @param $settings * @return mixed */ static protected function create($type, $name, $settings = array()) { $clazz = self::load($type, $name); $m = new $clazz(); $m->configure($settings); return $m; } /** * Class loading function * * Load class from any include folder * * @param string $type * @param string $name * @return string $clazz */ static public function load($type, $name) { $type = ucfirst(strtolower($type)); $clazz = sprintf('patI18n_%s_%s', $type, $name); if (class_exists($clazz, false)) { return $clazz; } $incDirs = array_reverse(self::$includeDirs); for ($i = 0; $i < count($incDirs); ++$i) { $file = sprintf('%s/%s/%s.php', $incDirs[$i], $type, $name); if (file_exists($file)) { include $file; break; } } if (class_exists($clazz, false)) { return $clazz; } throw new Exception('Could not load patI18n "' . $name . '" of type "' . $type . '".'); } /** * Switch to language * * This is the replacement for PHP's (@link setLocale()}. This call * gets forwared to all modules. * * Each module is expected to return "true". In case one module returns * "false", further processing is canceled. * * @param string $string * @return bool true on success */ static public function setLocale($locale) { foreach( self::$modules as $m ) { if( !$m->setLocale($locale) ) { return false; } } self::$locale = $locale; return true; } /** * Simple getter * * @param int $type, on of: LOCALE_TYPE_COMPLETE, LOCALE_TYPE_LANG or LOCALE_TYPE_SHORT * @return string $locale * @todo this is just lazy implementation */ static public function getLocale($type = self::LOCALE_TYPE_COMPLETE) { switch( $type ) { case self::LOCALE_TYPE_COMPLETE: return self::$locale; case self::LOCALE_TYPE_LANG: return substr( self::$locale, 0, 5 ); case self::LOCALE_TYPE_SHORT: return substr( self::$locale, 0, 2 ); default: break; } // this is not suposed to happen throw new Exception( 'The used type is unknown' ); } /** * Gettext interface * * This is the most simple version. It does neithere support domains nor * plural forms. * * Again, this is just a forward to each module. The modules will * will be called in the same order as they were added. The first module * that returns "true" - which means the string was translated, ends * the process - the rest of the modules won't be called at all! * * @param string $string To be translated string * @return string translated string */ static public function gettext($string) { foreach (self::$modules as $m) { if ($m->gettext($string)) { return $string; } } return $string; } /** * Gettext interface using domain * * This is pretty much the same as {@link gettext()}. The only difference * is that the "domain" is passed to the module as well. * * @param string $domain name of text domain * @param string $string To be translated string * @return string translated string * @see gettext() */ static public function dgettext($domain, $string) { foreach (self::$modules as $m) { if ($m->gettext($string, $domain)) { return $string; } } return $string; } /** * Gettext plural version interface * * This is pretty much the same as {@link gettext()}, but there are * two strings to be translated. The second one is the plural form. * * Some languages have more than one form for plural messages dependent * on the count. * * @param string $string To be translated string * @param string $stringPlural the plural form of the translated string * @param int $count The numnber of items the plural should be applied for * @return string translated string * @see gettext() */ static public function ngettext($string, $stringPlural, $count) { foreach (self::$modules as $m) { if ($m->ngettext($string, $stringPlural, $count)) { return $string; } } return $string; } /** * Gettext plural version interface using domain * * Pretty much the same as {@link ngettext()}, but it requires a "domain" * * @param string $domain name of text domain * @param string $string To be translated string * @param string $stringPlural the plural form of the translated string * @param int $count The numnber of items the plural should be applied for * @return string translated string * @see ngettext() */ static public function dngettext($domain, $string, $stringPlural, $count) { foreach (self::$modules as $m) { if ($m->ngettext($string, $stringPlural, $count, $domain)) { return $string; } } return $string; } } /** * init patI18n */ patI18n::staticConstruct();