diff --git a/Admin.php b/Translation2/Admin.php
similarity index 100%
rename from Admin.php
rename to Translation2/Admin.php
diff --git a/Translation2/Admin/Container/dataobjectsimple.php b/Translation2/Admin/Container/dataobjectsimple.php
new file mode 100644
index 0000000..7a158c9
--- /dev/null
+++ b/Translation2/Admin/Container/dataobjectsimple.php
@@ -0,0 +1,241 @@
+
+ * @copyright 2004-2007 Alan Knowles
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @version CVS: $Id$
+ * @link http://pear.php.net/package/Translation2
+ */
+
+/**
+ * require Translation2_Container_dataobjectsimple class
+ */
+require_once 'Translation2/Container/dataobjectsimple.php';
+
+/**
+ * Storage driver for storing/fetching data to/from a database
+ *
+ * This storage driver can use all databases which are supported
+ * by PEAR::DB_DataObject to fetch data.
+ *
+ * Database Structure:
+ *
+ *
+ * // meta data etc. not supported
+ *
+ * table: translations
+ * id // not null primary key autoincrement..
+ * string_id // translation id
+ * page // indexed varchar eg. (mytemplate.html)
+ * lang // index varchar (eg. en|fr|.....)
+ * translation // the translated value in language lang.
+ *
+ *
+ * @category Internationalization
+ * @package Translation2
+ * @author Alan Knowles
+ * @copyright 2004-2007 Alan Knowles
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @link http://pear.php.net/package/Translation2
+ */
+class Translation2_Admin_Container_dataobjectsimple extends Translation2_Container_dataobjectsimple
+{
+ // {{{ addLang()
+
+ /**
+ * Creates a new table to store the strings in this language.
+ * If the table is shared with other langs, it is ALTERed to
+ * hold strings in this lang too.
+ *
+ * @param array $langData array('lang_id' => 'en',
+ * 'table_name' => 'i18n',
+ * 'name' => 'english',
+ * 'meta' => 'some meta info',
+ * 'error_text' => 'not available');
+ * @param array $options DB_DataObject options
+ *
+ * @return true|PEAR_Error
+ */
+ function addLang($langData, $options = array())
+ {
+ $do = DB_DataObject::factory($this->options['table']);
+ $do->lang = $langData['lang_id'];
+ if (!$do->find()) {
+ $do->insert();
+ }
+ }
+
+ // }}}
+ // {{{ addLangToList()
+
+ /**
+ * Creates a new entry in the langsAvail table.
+ * If the table doesn't exist yet, it is created.
+ *
+ * @param array $langData array('lang_id' => 'en',
+ * 'table_name' => 'i18n',
+ * 'name' => 'english',
+ * 'meta' => 'some meta info',
+ * 'error_text' => 'not available');
+ *
+ * @return true|PEAR_Error
+ */
+ function addLangToList($langData)
+ {
+ return true;
+ }
+
+ // }}}
+ // {{{ add()
+
+ /**
+ * Add a new entry in the strings table.
+ *
+ * @param string $string string
+ * @param string $pageID page/group ID
+ * @param array $stringArray Associative array with string translations.
+ * Sample format: array('en' => 'sample', 'it' => 'esempio')
+ *
+ * @return true|PEAR_Error
+ */
+ function add($string, $pageID, $stringArray)
+ {
+ //look up the string id first..
+ $do = DB_DataObject::factory($this->options['table']);
+ $do->lang = '-';
+ $do->translation = $string;
+ $do->page = $page;
+ if ($do->find(true)) {
+ $stringID = $do->string_id;
+ } else {
+ // insert it and use the 'id' as the string id
+ $stringID = $do->insert();
+ $do->string_id = $stringID;
+ $do->update();
+ }
+
+ foreach ($stringArray as $lang=>$value) {
+ $do = DB_DataObject::factory($this->options['table']);
+ $do->string_id = $stringID;
+ $do->page = $pageID;
+ $do->lang = $lang;
+ if ($do->find(true)) {
+ $do->translation = $value;
+ $do->update();
+ continue;
+ }
+ $do->translation = $value;
+ $do->insert();
+ }
+
+ return true;
+ }
+
+ // }}}
+ // {{{ update()
+
+ /**
+ * Update an existing entry in the strings table.
+ *
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ * @param array $stringArray Associative array with string translations.
+ * Sample format: array('en' => 'sample', 'it' => 'esempio')
+ *
+ * @return true|PEAR_Error
+ */
+ function update($stringID, $pageID, $stringArray)
+ {
+ $this->add($stringID, $pageID, $stringArray);
+ return true;
+ }
+
+ // }}}
+ // {{{ remove()
+
+ /**
+ * Remove an entry from the strings table.
+ *
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ *
+ * @return true|PEAR_Error
+ */
+ function remove($stringID, $pageID)
+ {
+ // get the string id
+ $do = DB_DataObject::factory($this->options['table']);
+ $do->page = $pageID;
+ $do->translation = $stringID;
+ // we don't have the base language translation..
+ if (!$do->find()) {
+ return '';
+ }
+
+ while ($do->fetch()) {
+ $do2 = DB_DataObject::factory($this->options['table']);
+ $do2->get($do->id);
+ $do2->delete();
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ removePage()
+
+ /**
+ * Remove all the strings in the given page/group
+ *
+ * @param string $pageID page/group ID
+ *
+ * @return mixed true on success, PEAR_Error on failure
+ */
+ function removePage($pageID = null)
+ {
+ $do = DB_DataObject::factory($this->options['table']);
+ $do->page = $pageID;
+ // we don't have the base language translation..
+ if (!$do->find()) {
+ return '';
+ }
+
+ while ($do->fetch()) {
+ $do2 = DB_DataObject::factory($this->options['table']);
+ $do2->get($do->id);
+ $do2->delete();
+ }
+ return true;
+ }
+
+ // }}}
+}
+?>
\ No newline at end of file
diff --git a/Translation2/Admin/Container/db.php b/Translation2/Admin/Container/db.php
new file mode 100644
index 0000000..525abfe
--- /dev/null
+++ b/Translation2/Admin/Container/db.php
@@ -0,0 +1,721 @@
+
+ * @author Ian Eure
+ * @copyright 2004-2007 Lorenzo Alberton, Ian Eure
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @version CVS: $Id$
+ * @link http://pear.php.net/package/Translation2
+ */
+
+/**
+ * require Translation2_Container_db class
+ */
+require_once 'Translation2/Container/db.php';
+
+/**
+ * Storage driver for storing/fetching data to/from a database
+ *
+ * This storage driver can use all databases which are supported
+ * by the PEAR::DB abstraction layer to store and fetch data.
+ *
+ * @category Internationalization
+ * @package Translation2
+ * @author Lorenzo Alberton
+ * @author Ian Eure
+ * @copyright 2004-2007 Lorenzo Alberton, Ian Eure
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @link http://pear.php.net/package/Translation2
+ */
+class Translation2_Admin_Container_db extends Translation2_Container_db
+{
+
+ // {{{ class vars
+
+ // }}}
+ // {{{ addLang()
+
+ /**
+ * Creates a new table to store the strings in this language.
+ * If the table is shared with other langs, it is ALTERed to
+ * hold strings in this lang too.
+ *
+ * @param array $langData language data
+ * @param array $options options
+ *
+ * @return true|PEAR_Error
+ */
+ function addLang($langData, $options = array())
+ {
+ $tables = $this->db->getListOf('tables');
+ if (PEAR::isError($tables)) {
+ return $tables;
+ }
+
+ $lang_col = $this->_getLangCol($langData['lang_id']);
+
+ if (in_array($langData['table_name'], $tables)) {
+ // table exists
+ $query = sprintf('ALTER TABLE %s ADD %s%s TEXT',
+ $this->db->quoteIdentifier($langData['table_name']),
+ $this->db->phptype == 'mssql' ? '' : 'COLUMN ',
+ $this->db->quoteIdentifier($lang_col)
+ );
+ ++$this->_queries;
+ return $this->db->query($query);
+ }
+
+ //table does not exist
+ $queries = array();
+ $queries[] = sprintf('CREATE TABLE %s ( '
+ .'%s VARCHAR(%d) default NULL, '
+ .'%s TEXT NOT NULL, '
+ .'%s TEXT )',
+ $this->db->quoteIdentifier($langData['table_name']),
+ $this->db->quoteIdentifier($this->options['string_page_id_col']),
+ (int)$this->options['string_page_id_col_length'],
+ $this->db->quoteIdentifier($this->options['string_id_col']),
+ $this->db->quoteIdentifier($lang_col)
+ );
+ $mysqlClause = ($this->db->phptype == 'mysql') ? '(255)' : '';
+
+ $index_name = sprintf('%s_%s_%s_index',
+ $langData['table_name'],
+ $this->options['string_page_id_col'],
+ $this->options['string_id_col']
+ );
+ $queries[] = sprintf('CREATE UNIQUE INDEX %s ON %s (%s, %s%s)',
+ $this->db->quoteIdentifier($index_name),
+ $this->db->quoteIdentifier($langData['table_name']),
+ $this->db->quoteIdentifier($this->options['string_page_id_col']),
+ $this->db->quoteIdentifier($this->options['string_id_col']),
+ $mysqlClause
+ );
+
+ $index_name = sprintf('%s_%s_index',
+ $langData['table_name'],
+ $this->options['string_page_id_col']
+ );
+ $queries[] = sprintf('CREATE INDEX %s ON %s (%s)',
+ $this->db->quoteIdentifier($index_name),
+ $this->db->quoteIdentifier($langData['table_name']),
+ $this->db->quoteIdentifier($this->options['string_page_id_col'])
+ );
+
+ $index_name = sprintf('%s_%s_index',
+ $langData['table_name'],
+ $this->options['string_id_col']
+ );
+ $queries[] = sprintf('CREATE INDEX %s ON %s (%s%s)',
+ $this->db->quoteIdentifier($index_name),
+ $this->db->quoteIdentifier($langData['table_name']),
+ $this->db->quoteIdentifier($this->options['string_id_col']),
+ $mysqlClause
+ );
+
+ foreach ($queries as $query) {
+ ++$this->_queries;
+ $res = $this->db->query($query);
+ if (PEAR::isError($res)) {
+ return $res;
+ }
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ addLangToList()
+
+ /**
+ * Creates a new entry in the langsAvail table.
+ * If the table doesn't exist yet, it is created.
+ *
+ * @param array $langData array('lang_id' => 'en',
+ * 'table_name' => 'i18n',
+ * 'name' => 'english',
+ * 'meta' => 'some meta info',
+ * 'error_text' => 'not available',
+ * 'encoding' => 'iso-8859-1');
+ *
+ * @return true|PEAR_Error
+ */
+ function addLangToList($langData)
+ {
+ $tables = $this->db->getListOf('tables');
+ if (PEAR::isError($tables)) {
+ return $tables;
+ }
+
+ if (!in_array($this->options['langs_avail_table'], $tables)) {
+ $queries = array();
+ $queries[] = sprintf('CREATE TABLE %s ('
+ .'%s VARCHAR(16), '
+ .'%s VARCHAR(200), '
+ .'%s TEXT, '
+ .'%s VARCHAR(250), '
+ .'%s VARCHAR(16) )',
+ $this->db->quoteIdentifier($this->options['langs_avail_table']),
+ $this->db->quoteIdentifier($this->options['lang_id_col']),
+ $this->db->quoteIdentifier($this->options['lang_name_col']),
+ $this->db->quoteIdentifier($this->options['lang_meta_col']),
+ $this->db->quoteIdentifier($this->options['lang_errmsg_col']),
+ $this->db->quoteIdentifier($this->options['lang_encoding_col'])
+ );
+ $queries[] = sprintf('CREATE UNIQUE INDEX %s_%s_index ON %s (%s)',
+ $this->options['langs_avail_table'],
+ $this->options['lang_id_col'],
+ $this->db->quoteIdentifier($this->options['langs_avail_table']),
+ $this->db->quoteIdentifier($this->options['lang_id_col'])
+ );
+
+ foreach ($queries as $query) {
+ ++$this->_queries;
+ $res = $this->db->query($query);
+ if (PEAR::isError($res)) {
+ return $res;
+ }
+ }
+ }
+
+ $query = sprintf('INSERT INTO %s (%s, %s, %s, %s, %s) VALUES (%s, %s, %s, %s, %s)',
+ $this->db->quoteIdentifier($this->options['langs_avail_table']),
+ $this->db->quoteIdentifier($this->options['lang_id_col']),
+ $this->db->quoteIdentifier($this->options['lang_name_col']),
+ $this->db->quoteIdentifier($this->options['lang_meta_col']),
+ $this->db->quoteIdentifier($this->options['lang_errmsg_col']),
+ $this->db->quoteIdentifier($this->options['lang_encoding_col']),
+ $this->db->quote($langData['lang_id']),
+ $this->db->quote($langData['name']),
+ $this->db->quote($langData['meta']),
+ $this->db->quote($langData['error_text']),
+ $this->db->quote($langData['encoding'])
+ );
+
+ ++$this->_queries;
+ $success = $this->db->query($query);
+ $this->options['strings_tables'][$langData['lang_id']] = $langData['table_name'];
+ return $success;
+ }
+
+ // }}}
+ // {{{ removeLang()
+
+ /**
+ * Remove the lang from the langsAvail table and drop the strings table.
+ * If the strings table holds other langs and $force==false, then
+ * only the lang column is dropped. If $force==true the whole
+ * table is dropped without any check
+ *
+ * @param string $langID language ID
+ * @param boolean $force if true, the whole table is dropped without checks
+ *
+ * @return true|PEAR_Error
+ */
+ function removeLang($langID, $force)
+ {
+ //remove from langsAvail
+ $query = sprintf('DELETE FROM %s WHERE %s = %s',
+ $this->db->quoteIdentifier($this->options['langs_avail_table']),
+ $this->db->quoteIdentifier($this->options['lang_id_col']),
+ $this->db->quote($langID)
+ );
+ ++$this->_queries;
+ $res = $this->db->query($query);
+ if (PEAR::isError($res)) {
+ return $res;
+ }
+
+ $lang_table = $this->_getLangTable($langID);
+ if ($force) {
+ //remove the whole table
+ ++$this->_queries;
+ return $this->db->query('DROP TABLE ' . $this->db->quoteIdentifier($lang_table));
+ }
+
+ //drop only the column for this lang
+ $query = sprintf('ALTER TABLE %s DROP COLUMN %s',
+ $lang_table,
+ $this->_getLangCol($langID)
+ );
+ ++$this->_queries;
+ return $this->db->query($query);
+ }
+
+ // }}}
+ // {{{ updateLang()
+
+ /**
+ * Update the lang info in the langsAvail table
+ *
+ * @param array $langData language data
+ *
+ * @return true|PEAR_Error
+ */
+ function updateLang($langData)
+ {
+ $allFields = array(
+ //'lang_id' => 'lang_id_col',
+ 'name' => 'lang_name_col',
+ 'meta' => 'lang_meta_col',
+ 'error_text' => 'lang_errmsg_col',
+ 'encoding' => 'lang_encoding_col',
+ );
+ $updateFields = array_keys($langData);
+ $langSet = array();
+ foreach ($allFields as $field => $col) {
+ if (in_array($field, $updateFields)) {
+ $langSet[] = $this->db->quoteIdentifier($this->options[$col]) . ' = ' .
+ $this->db->quote($langData[$field]);
+ }
+ }
+ $query = sprintf('UPDATE %s SET %s WHERE %s=%s',
+ $this->db->quoteIdentifier($this->options['langs_avail_table']),
+ implode(', ', $langSet),
+ $this->db->quoteIdentifier($this->options['lang_id_col']),
+ $this->db->quote($langData['lang_id'])
+ );
+
+ ++$this->_queries;
+ $success = $this->db->query($query);
+ $this->fetchLangs(); //update memory cache
+ return $success;
+ }
+
+ // }}}
+ // {{{ add()
+
+ /**
+ * Add a new entry in the strings table.
+ *
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ * @param array $stringArray Associative array with string translations.
+ * Sample format: array('en' => 'sample', 'it' => 'esempio')
+ *
+ * @return true|PEAR_Error
+ */
+ function add($stringID, $pageID, $stringArray)
+ {
+ $langs = array_intersect(
+ array_keys($stringArray),
+ $this->getLangs('ids')
+ );
+
+ if (!count($langs)) {
+ //return error: no valid lang provided
+ return true;
+ }
+
+ // Langs may be in different tables - we need to split up queries along
+ // table lines, so we can keep DB traffic to a minimum.
+
+ $unquoted_stringID = $stringID;
+ $unquoted_pageID = $pageID;
+ $stringID = $this->db->quote($stringID);
+ $pageID = is_null($pageID) ? 'NULL' : $this->db->quote($pageID);
+ // Loop over the tables we need to insert into.
+ foreach ($this->_tableLangs($langs) as $table => $tableLangs) {
+ $exists = $this->_recordExists($unquoted_stringID, $unquoted_pageID, $table);
+ if (PEAR::isError($exists)) {
+ return $exists;
+ }
+ $func = $exists ? '_getUpdateQuery' : '_getInsertQuery';
+ $query = $this->$func($table, $tableLangs, $stringID, $pageID, $stringArray);
+
+ ++$this->_queries;
+ $res = $this->db->query($query);
+ if (PEAR::isError($res)) {
+ return $res;
+ }
+ }
+
+ return true;
+ }
+
+ // }}}
+ // {{{ update()
+
+ /**
+ * Update an existing entry in the strings table.
+ *
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ * @param array $stringArray Associative array with string translations.
+ * Sample format: array('en' => 'sample', 'it' => 'esempio')
+ *
+ * @return true|PEAR_Error
+ */
+ function update($stringID, $pageID, $stringArray)
+ {
+ return $this->add($stringID, $pageID, $stringArray);
+ }
+
+ // }}}
+ // {{{ _getInsertQuery()
+
+ /**
+ * Build a SQL query to INSERT a record
+ *
+ * @param string $table table name
+ * @param array &$tableLangs tables containing the languages
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ * @param array &$stringArray array of strings
+ *
+ * @return string INSERT query
+ * @access private
+ */
+ function _getInsertQuery($table, &$tableLangs, $stringID, $pageID, &$stringArray)
+ {
+ $tableCols = $this->_getLangCols($tableLangs);
+ $langData = array();
+ foreach ($tableLangs as $lang) {
+ $langData[$lang] = $this->db->quote($stringArray[$lang]);
+ }
+ foreach (array_keys($tableCols) as $k) {
+ $tableCols[$k] = $this->db->quoteIdentifier($tableCols[$k]);
+ }
+
+ return sprintf('INSERT INTO %s (%s, %s, %s) VALUES (%s, %s, %s)',
+ $this->db->quoteIdentifier($table),
+ $this->db->quoteIdentifier($this->options['string_id_col']),
+ $this->db->quoteIdentifier($this->options['string_page_id_col']),
+ implode(', ', $tableCols),
+ $stringID,
+ $pageID,
+ implode(', ', $langData)
+ );
+ }
+
+ // }}}
+ // {{{ _getUpdateQuery()
+
+ /**
+ * Build a SQL query to UPDATE a record
+ *
+ * @param string $table table name
+ * @param array &$tableLangs tables containing the languages
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ * @param array &$stringArray array of strings
+ *
+ * @return string UPDATE query
+ * @access private
+ */
+ function _getUpdateQuery($table, &$tableLangs, $stringID, $pageID, &$stringArray)
+ {
+ $tableCols = $this->_getLangCols($tableLangs);
+ $langSet = array();
+ foreach ($tableLangs as $lang) {
+ $langSet[] = $this->db->quoteIdentifier($tableCols[$lang]) . ' = ' .
+ $this->db->quote($stringArray[$lang]);
+ }
+
+ return sprintf('UPDATE %s SET %s WHERE %s = %s AND %s = %s',
+ $this->db->quoteIdentifier($table),
+ implode(', ', $langSet),
+ $this->db->quoteIdentifier($this->options['string_id_col']),
+ $stringID,
+ $this->db->quoteIdentifier($this->options['string_page_id_col']),
+ $pageID
+ );
+ }
+
+ // }}}
+ // {{{ remove()
+
+ /**
+ * Remove an entry from the strings table.
+ *
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ *
+ * @return true|PEAR_Error
+ */
+ function remove($stringID, $pageID)
+ {
+ $tables = array_unique($this->_getLangTables());
+
+ $stringID = $this->db->quote($stringID);
+ // get the tables and skip the non existent ones
+ $dbTables = $this->db->getListOf('tables');
+ foreach ($tables as $table) {
+ if (!in_array($table, $dbTables)) {
+ continue;
+ }
+ $query = sprintf('DELETE FROM %s WHERE %s = %s AND %s',
+ $this->db->quoteIdentifier($table),
+ $this->db->quoteIdentifier($this->options['string_id_col']),
+ $stringID,
+ $this->db->quoteIdentifier($this->options['string_page_id_col'])
+ );
+ if (is_null($pageID)) {
+ $query .= ' IS NULL';
+ } else {
+ $query .= ' = ' . $this->db->quote($pageID);
+ }
+
+ ++$this->_queries;
+ $res = $this->db->query($query);
+ if (PEAR::isError($res)) {
+ return $res;
+ }
+ }
+
+ return true;
+ }
+
+ // }}}
+ // {{{ removePage
+
+ /**
+ * Remove all the strings in the given page/group
+ *
+ * @param string $pageID page/group ID
+ *
+ * @return mixed true on success, PEAR_Error on failure
+ */
+ function removePage($pageID = null)
+ {
+ $tables = array_unique($this->_getLangTables());
+
+ // get the tables and skip the non existent ones
+ $dbTables = $this->db->getListOf('tables');
+ foreach ($tables as $table) {
+ if (!in_array($table, $dbTables)) {
+ continue;
+ }
+ $query = sprintf('DELETE FROM %s WHERE %s',
+ $this->db->quoteIdentifier($table, true),
+ $this->db->quoteIdentifier($this->options['string_page_id_col'])
+ );
+ if (is_null($pageID)) {
+ $query .= ' IS NULL';
+ } else {
+ $query .= ' = ' . $this->db->quote($pageID, 'text');
+ }
+
+ ++$this->_queries;
+ $res = $this->db->query($query);
+ if (PEAR::isError($res)) {
+ return $res;
+ }
+ }
+
+ return true;
+ }
+
+ // }}}
+ // {{{ getPageNames()
+
+ /**
+ * Get a list of all the pageIDs in any table.
+ *
+ * @return array
+ */
+ function getPageNames()
+ {
+ $pages = array();
+ foreach ($this->_getLangTables() as $table) {
+ $query = sprintf('SELECT DISTINCT %s FROM %s',
+ $this->db->quoteIdentifier($this->options['string_page_id_col']),
+ $this->db->quoteIdentifier($table)
+ );
+ ++$this->_queries;
+ $res = $this->db->getCol($query);
+ if (PEAR::isError($res)) {
+ return $res;
+ }
+ $pages = array_merge($pages, $res);
+ }
+ return array_unique($pages);
+ }
+
+ // }}}
+ // {{{ _tableLangs()
+
+ /**
+ * Get table -> language mapping
+ *
+ * The key of the array is the table that a language is stored in;
+ * the value is an /array/ of languages stored in that table.
+ *
+ * @param array $langs Languages to get mapping for
+ *
+ * @return array Table -> language mapping
+ * @access private
+ * @see Translation2_Container_DB::_getLangTable()
+ */
+ function &_tableLangs($langs)
+ {
+ $tables = array();
+ foreach ($langs as $lang) {
+ $table = $this->_getLangTable($lang);
+ $tables[$table][] = $lang;
+ }
+ return $tables;
+ }
+
+ // }}}
+ // {{{ _getLangTables()
+
+ /**
+ * Get tables for languages
+ *
+ * This is like _getLangTable(), but it returns an array of the tables for
+ * multiple languages.
+ *
+ * @param array $langs Languages to get tables for
+ *
+ * @return array
+ * @access private
+ */
+ function &_getLangTables($langs = null)
+ {
+ $tables = array();
+ $langs = !is_array($langs) ? $this->getLangs('ids') : $langs;
+ foreach ($langs as $lang) {
+ $tables[] = $this->_getLangTable($lang);
+ }
+ $tables = array_unique($tables);
+ return $tables;
+ }
+
+ // }}}
+ // {{{ _getLangCols()
+
+ /**
+ * Get table columns strings are stored in
+ *
+ * This is like _getLangCol(), except it returns an array which contains
+ * the mapping for multiple languages.
+ *
+ * @param array $langs Languages to get mapping for
+ *
+ * @return array Language -> column mapping
+ * @access private
+ * @see Translation2_Container_DB::_getLangCol()
+ */
+ function &_getLangCols($langs)
+ {
+ $cols = array();
+ foreach ($langs as $lang) {
+ $cols[$lang] = $this->_getLangCol($lang);
+ }
+ return $cols;
+ }
+
+ // }}}
+ // {{{ _recordExists()
+
+ /**
+ * Check if there's already a record in the table with the
+ * given (pageID, stringID) pair.
+ *
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ * @param string $table table name
+ *
+ * @return boolean
+ * @access private
+ */
+ function _recordExists($stringID, $pageID, $table)
+ {
+ $stringID = $this->db->quote($stringID, 'text');
+ $pageID = is_null($pageID) ? ' IS NULL' : ' = ' . $this->db->quote($pageID);
+ $query = sprintf('SELECT COUNT(*) FROM %s WHERE %s=%s AND %s%s',
+ $this->db->quoteIdentifier($table),
+ $this->db->quoteIdentifier($this->options['string_id_col']),
+ $stringID,
+ $this->db->quoteIdentifier($this->options['string_page_id_col']),
+ $pageID
+ );
+ ++$this->_queries;
+ $res = $this->db->getOne($query);
+ if (PEAR::isError($res)) {
+ return $res;
+ }
+ return ($res > 0);
+ }
+
+ // }}}
+ // {{{ _filterStringsByTable()
+
+ /**
+ * Get only the strings for the langs in the given table
+ *
+ * @param array $stringArray Associative array with string translations.
+ * Sample format: array('en' => 'sample', 'it' => 'esempio')
+ * @param string $table table name
+ *
+ * @return array
+ * @access private
+ */
+ function &_filterStringsByTable($stringArray, $table)
+ {
+ $strings = array();
+ foreach ($stringArray as $lang => $string) {
+ if ($table == $this->_getLangTable($lang)) {
+ $strings[$lang] = $string;
+ }
+ }
+ return $strings;
+ }
+
+ // }}}
+ // {{{ _getLangsInTable()
+
+ /**
+ * Get the languages sharing the given table
+ *
+ * @param string $table table name
+ *
+ * @return array
+ */
+ function &_getLangsInTable($table)
+ {
+ $this->fetchLangs(); // force cache refresh
+ $langsInTable = array();
+ foreach (array_keys($this->langs) as $lang) {
+ if ($table == $this->_getLangTable($lang)) {
+ $langsInTable[] = $lang;
+ }
+ }
+ return $langsInTable;
+ }
+
+ // }}}
+}
+?>
\ No newline at end of file
diff --git a/Translation2/Admin/Container/gettext.php b/Translation2/Admin/Container/gettext.php
new file mode 100644
index 0000000..d66f55e
--- /dev/null
+++ b/Translation2/Admin/Container/gettext.php
@@ -0,0 +1,640 @@
+
+ * @author Michael Wallner
+ * @copyright 2004-2007 Lorenzo Alberton, Michael Wallner
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @version CVS: $Id$
+ * @link http://pear.php.net/package/Translation2
+ */
+
+/**
+ * require Translation2_Container_gettext class
+ */
+require_once 'Translation2/Container/gettext.php';
+
+/**
+ * Storage driver for storing/fetching data to/from a gettext file
+ *
+ * This storage driver requires the gettext extension
+ *
+ * @category Internationalization
+ * @package Translation2
+ * @author Lorenzo Alberton
+ * @author Michael Wallner
+ * @copyright 2004-2007 Lorenzo Alberton, Michael Wallner
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @version CVS: $Id$
+ * @link http://pear.php.net/package/Translation2
+ */
+class Translation2_Admin_Container_gettext extends Translation2_Container_gettext
+{
+ // {{{ class vars
+
+ var $_bulk = false;
+ var $_queue = array();
+ var $_fields = array('name', 'meta', 'error_text', 'encoding');
+
+ // }}}
+ // {{{ addLang()
+
+ /**
+ * Creates a new entry in the langs_avail .ini file.
+ *
+ * @param array $langData language data
+ * @param string $path path to gettext data dir
+ *
+ * @return mixed Returns true on success or PEAR_Error on failure.
+ */
+ function addLang($langData, $path = null)
+ {
+ if (!isset($path) || !is_string($path)) {
+ $path = $this->_domains[$this->options['default_domain']];
+ }
+
+ $path .= '/'. $langData['lang_id'] . '/LC_MESSAGES';
+
+ if (!is_dir($path)) {
+ include_once 'System.php';
+ if (!System::mkdir(array('-p', $path))) {
+ return $this->raiseError(sprintf(
+ 'Cannot create new language in path "%s"', $path
+ ),
+ TRANSLATION2_ERROR_CANNOT_CREATE_DIR
+ );
+ }
+ }
+
+ return true;
+ }
+
+ // }}}
+ // {{{ addLangToList()
+
+ /**
+ * Creates a new entry in the langsAvail .ini file.
+ * If the file doesn't exist yet, it is created.
+ *
+ * @param array $langData array('lang_id' => 'en',
+ * 'name' => 'english',
+ * 'meta' => 'some meta info',
+ * 'error_text' => 'not available'
+ * 'encoding' => 'iso-8859-1',
+ * );
+ *
+ * @return true|PEAR_Error on failure
+ */
+ function addLangToList($langData)
+ {
+ if (PEAR::isError($changed = $this->_updateLangData($langData))) {
+ return $changed;
+ }
+ return $changed ? $this->_writeLangsAvailFile() : true;
+ }
+
+ // }}}
+ // {{{ add()
+
+ /**
+ * Add a new entry in the strings domain.
+ *
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ * @param array $strings Associative array with string translations.
+ * Sample format: array('en' => 'sample', 'it' => 'esempio')
+ *
+ * @return true|PEAR_Error on failure
+ */
+ function add($stringID, $pageID, $strings)
+ {
+ if (!isset($pageID)) {
+ $pageID = $this->options['default_domain'];
+ }
+
+ $langs = array_intersect(array_keys($strings), $this->getLangs('ids'));
+
+ if (!count($langs)) {
+ return true; // really?
+ }
+
+ if ($this->_bulk) {
+ foreach ($strings as $lang => $string) {
+ if (in_array($lang, $langs)) {
+ $this->_queue['add'][$pageID][$lang][$stringID] = $string;
+ }
+ }
+ return true;
+ } else {
+ $add = array();
+ foreach ($strings as $lang => $string) {
+ if (in_array($lang, $langs)) {
+ $add[$pageID][$lang][$stringID] = $string;
+ }
+ }
+ return $this->_add($add);
+ }
+ }
+
+ // }}}
+ // {{{ remove()
+
+ /**
+ * Remove an entry from the domain.
+ *
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ *
+ * @return true|PEAR_Error on failure
+ */
+ function remove($stringID, $pageID)
+ {
+ if (!isset($pageID)) {
+ $pageID = $this->options['default_domain'];
+ }
+
+ if ($this->_bulk) {
+ $this->_queue['remove'][$pageID][$stringID] = true;
+ return true;
+ } else {
+ $tmp = array($pageID => array($stringID => true));
+ return $this->_remove($tmp);
+ }
+
+ }
+
+ // }}}
+ // {{{ removePage
+
+ /**
+ * Remove all the strings in the given page/group (domain)
+ *
+ * @param string $pageID page/group ID
+ * @param string $path path to gettext data dir
+ *
+ * @return mixed true on success, PEAR_Error on failure
+ */
+ function removePage($pageID = null, $path = null)
+ {
+ if (!isset($pageID)) {
+ $pageID = $this->options['default_domain'];
+ }
+
+ if (!isset($path)) {
+ if (!empty($this->_domains[$pageID])) {
+ $path = $this->_domains[$pageID];
+ } else {
+ $path = $this->_domains[$this->options['default_domain']];
+ }
+ }
+
+ if (PEAR::isError($e = $this->_removeDomain($pageID))) {
+ return $e;
+ }
+
+ $this->fetchLangs();
+ foreach ($this->langs as $langID => $lang) {
+ $domain_file = $path .'/'. $langID .'/LC_MESSAGES/'. $pageID .'.';
+ if (!@unlink($domain_file.'mo') || !@unlink($domain_file.'po')) {
+ return $this->raiseError('Cannot delete page ' . $pageID. ' (file '.$domain_file.'.*)',
+ TRANSLATION2_ERROR
+ );
+ }
+ }
+
+ return true;
+ }
+
+ // }}}
+ // {{{ update()
+
+ /**
+ * Update
+ *
+ * Alias for Translation2_Admin_Container_gettext::add()
+ *
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ * @param array $strings strings
+ *
+ * @return mixed
+ * @access public
+ * @see add()
+ */
+ function update($stringID, $pageID, $strings)
+ {
+ return $this->add($stringID, $pageID, $strings);
+ }
+
+ // }}}
+ // {{{ removeLang()
+
+ /**
+ * Remove Language
+ *
+ * @param string $langID language ID
+ * @param bool $force (unused)
+ *
+ * @return true|PEAR_Error
+ * @access public
+ */
+ function removeLang($langID, $force = false)
+ {
+ include_once 'System.php';
+ foreach ((array) $this->_domains as $domain => $path) {
+ if (is_dir($fp = $path .'/'. $langID)) {
+ if (PEAR::isError($e = System::rm(array('-rf', $fp))) || !$e) {
+ return $e ? $e : PEAR::raiseError(sprintf(
+ 'Could not remove language "%s" from domain "%s" '.
+ 'in path "%s" (probably insufficient permissions)',
+ $langID, $domain, $path
+ ),
+ TRANSLATION2_ERROR
+ );
+ }
+ }
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ updateLang()
+
+ /**
+ * Update the lang info in the langs_avail file
+ *
+ * @param array $langData language data
+ *
+ * @return mixed Returns true on success or PEAR_Error on failure.
+ * @access public
+ */
+ function updateLang($langData)
+ {
+ if (PEAR::isError($changed = $this->_updateLangData($langData))) {
+ return $changed;
+ }
+ return $changed ? $this->_writeLangsAvailFile() : true;
+ }
+
+ // }}}
+ // {{{ getPageNames()
+
+ /**
+ * Get a list of all the domains
+ *
+ * @return array
+ * @access public
+ */
+ function getPageNames()
+ {
+ return array_keys($this->_domains);
+ }
+
+ // }}}
+ // {{{ begin()
+
+ /**
+ * Begin
+ *
+ * @return void
+ * @access public
+ */
+ function begin()
+ {
+ $this->_bulk = true;
+ }
+
+ // }}}
+ // {{{ commit()
+
+ /**
+ * Commit
+ *
+ * @return true|PEAR_Error on failure.
+ * @access public
+ */
+ function commit()
+ {
+ $this->_bulk = false;
+ if (isset($this->_queue['remove'])) {
+ if (PEAR::isError($e = $this->_remove($this->_queue['remove']))) {
+ return $e;
+ }
+ }
+ if (isset($this->_queue['add'])) {
+ if (PEAR::isError($e = $this->_add($this->_queue['add']))) {
+ return $e;
+ }
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ _add()
+
+ /**
+ * Add
+ *
+ * @param array &$bulk array('pageID' => array([languages]))
+ *
+ * @return true|PEAR_Error on failure.
+ * @access private
+ */
+ function _add(&$bulk)
+ {
+ include_once 'File/Gettext.php';
+ $gtFile = &File_Gettext::factory($this->options['file_type']);
+ $langs = $this->getLangs('array');
+
+ foreach ((array) $bulk as $pageID => $languages) {
+ //create the new domain on demand
+ if (!isset($this->_domains[$pageID])) {
+ if (PEAR::isError($e = $this->_addDomain($pageID))) {
+ return $e;
+ }
+ }
+ $path = $this->_domains[$pageID];
+ if ($path[strlen($path)-1] != '/' && $path[strlen($path)-1] != '\\') {
+ $path .= '/';
+ }
+ $file = '/LC_MESSAGES/'. $pageID .'.'. $this->options['file_type'];
+
+ foreach ($languages as $lang => $strings) {
+
+ if (is_file($path . $lang . $file)) {
+ if (PEAR::isError($e = $gtFile->load($path . $lang . $file))) {
+ return $e;
+ }
+ }
+
+ if (!isset($gtFile->meta['Content-Type'])) {
+ $gtFile->meta['Content-Type'] = 'text/plain; charset=';
+ if (isset($langs[$lang]['encoding'])) {
+ $gtFile->meta['Content-Type'] .= $langs[$lang]['encoding'];
+ } else {
+ $gtFile->meta['Content-Type'] .= $this->options['default_encoding'];
+ }
+ }
+
+ foreach ($strings as $stringID => $string) {
+ $gtFile->strings[$stringID] = $string;
+ }
+
+ if (PEAR::isError($e = $gtFile->save($path . $lang . $file))) {
+ return $e;
+ }
+
+ //refresh cache
+ $this->cachedDomains[$lang][$pageID] = $gtFile->strings;
+ }
+ }
+
+ $bulk = null;
+ return true;
+ }
+
+ // }}}
+ // {{{ _remove()
+
+ /**
+ * Remove
+ *
+ * @param array &$bulk array('pageID' => array([languages]))
+ *
+ * @return true|PEAR_Error on failure.
+ * @access private
+ */
+ function _remove(&$bulk)
+ {
+ include_once 'File/Gettext.php';
+ $gtFile = &File_Gettext::factory($this->options['file_type']);
+
+ foreach ($this->getLangs('ids') as $lang) {
+ foreach ((array) $bulk as $pageID => $stringIDs) {
+ $file = sprintf(
+ '%s/%s/LC_MESSAGES/%s.%s',
+ $this->_domains[$pageID],
+ $lang,
+ $pageID,
+ $this->options['file_type']
+ );
+
+ if (is_file($file)) {
+ if (PEAR::isError($e = $gtFile->load($file))) {
+ return $e;
+ }
+
+ foreach (array_keys($stringIDs) as $stringID) {
+ unset($gtFile->strings[$stringID]);
+ }
+
+ if (PEAR::isError($e = $gtFile->save($file))) {
+ return $e;
+ }
+
+ //refresh cache
+ $this->cachedDomains[$lang][$pageID] = $gtFile->strings;
+ }
+ }
+ }
+
+ $bulk = null;
+ return true;
+ }
+
+ // }}}
+ // {{{ _addDomain()
+
+ /**
+ * Add the path-to-the-new-domain to the domains-path-INI-file
+ *
+ * @param string $pageID domain name
+ *
+ * @return true|PEAR_Error on failure
+ * @access private
+ */
+ function _addDomain($pageID)
+ {
+ $domain_path = count($this->_domains) ? reset($this->_domains) : 'locale/';
+
+ if (!is_resource($f = fopen($this->options['domains_path_file'], 'a'))) {
+ return $this->raiseError(sprintf(
+ 'Cannot write to domains path INI file "%s"',
+ $this->options['domains_path_file']
+ ),
+ TRANSLATION2_ERROR_CANNOT_WRITE_FILE
+ );
+ }
+
+ $CRLF = $this->options['carriage_return'];
+
+ while (true) {
+ if (@flock($f, LOCK_EX)) {
+ fwrite($f, $CRLF . $pageID . ' = ' . $domain_path . $CRLF);
+ @flock($f, LOCK_UN);
+ fclose($f);
+ break;
+ }
+ }
+
+ $this->_domains[$pageID] = $domain_path;
+
+ return true;
+ }
+
+ // }}}
+ // {{{ _removeDomain()
+
+ /**
+ * Remove the path-to-the-domain from the domains-path-INI-file
+ *
+ * @param string $pageID domain name
+ *
+ * @return true|PEAR_Error on failure
+ * @access private
+ */
+ function _removeDomain($pageID)
+ {
+ $domain_path = count($this->_domains) ? reset($this->_domains) : 'locale/';
+
+ if (!is_resource($f = fopen($this->options['domains_path_file'], 'r+'))) {
+ return $this->raiseError(sprintf(
+ 'Cannot write to domains path INI file "%s"',
+ $this->options['domains_path_file']
+ ),
+ TRANSLATION2_ERROR_CANNOT_WRITE_FILE
+ );
+ }
+
+ $CRLF = $this->options['carriage_return'];
+
+ while (true) {
+ if (@flock($f, LOCK_EX)) {
+ $pages = file($this->options['domains_path_file']);
+ foreach ($pages as $page) {
+ if (preg_match('/^'.$pageID.'\s*=/', $page)) {
+ //skip
+ continue;
+ }
+ fwrite($f, $page . $CRLF);
+ }
+ fflush($f);
+ ftruncate($f, ftell($f));
+ @flock($f, LOCK_UN);
+ fclose($f);
+ break;
+ }
+ }
+
+ unset($this->_domains[$pageID]);
+
+ return true;
+ }
+
+ // }}}
+ // {{{ _writeLangsAvailFile()
+
+ /**
+ * Write the langs_avail INI file
+ *
+ * @return true|PEAR_Error on failure.
+ * @access private
+ */
+ function _writeLangsAvailFile()
+ {
+ if (PEAR::isError($langs = $this->getLangs())) {
+ return $langs;
+ }
+
+ if (!is_resource($f = fopen($this->options['langs_avail_file'], 'w'))) {
+ return $this->raiseError(sprintf(
+ 'Cannot write to available langs INI file "%s"',
+ $this->options['langs_avail_file']
+ ),
+ TRANSLATION2_ERROR_CANNOT_WRITE_FILE
+ );
+ }
+ $CRLF = $this->options['carriage_return'];
+
+ @flock($f, LOCK_EX);
+
+ foreach ($langs as $id => $data) {
+ fwrite($f, '['. $id .']'. $CRLF);
+ foreach ($this->_fields as $k) {
+ if (isset($data[$k])) {
+ fwrite($f, $k . ' = ' . $data[$k] . $CRLF);
+ }
+ }
+ fwrite($f, $CRLF);
+ }
+
+ @flock($f, LOCK_UN);
+ fclose($f);
+ return true;
+ }
+
+ // }}}
+ // {{{ _updateLangData()
+
+ /**
+ * Update Lang Data
+ *
+ * @param array $langData language data
+ *
+ * @return true|PEAR_Error on failure.
+ * @access private
+ */
+ function _updateLangData($langData)
+ {
+ if (PEAR::isError($langs = $this->getLangs())) {
+ return $langs;
+ }
+
+ $lang = &$langs[$langData['lang_id']];
+ $changed = false;
+ foreach ($this->_fields as $k) {
+ if ( isset($langData[$k]) &&
+ (!isset($lang[$k]) || $langData[$k] != $lang[$k])) {
+ $lang[$k] = $langData[$k];
+ $changed = true;
+ }
+ }
+
+ if ($changed) {
+ $lang['id'] = $langData['lang_id'];
+ $this->langs = $langs;
+ }
+ return $changed;
+ }
+
+ // }}}
+}
+?>
\ No newline at end of file
diff --git a/Translation2/Admin/Container/mdb.php b/Translation2/Admin/Container/mdb.php
new file mode 100644
index 0000000..d010ae5
--- /dev/null
+++ b/Translation2/Admin/Container/mdb.php
@@ -0,0 +1,714 @@
+
+ * @author Ian Eure
+ * @copyright 2004-2007 Lorenzo Alberton, Ian Eure
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @version CVS: $Id$
+ * @link http://pear.php.net/package/Translation2
+ */
+
+/**
+ * require Translation2_Container_mdb class
+ */
+require_once 'Translation2/Container/mdb.php';
+
+/**
+ * Storage driver for storing/fetching data to/from a database
+ *
+ * This storage driver can use all databases which are supported
+ * by the PEAR::MDB abstraction layer to store and fetch data.
+ *
+ * @category Internationalization
+ * @package Translation2
+ * @author Lorenzo Alberton
+ * @author Ian Eure
+ * @copyright 2004-2007 Lorenzo Alberton, Ian Eure
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @link http://pear.php.net/package/Translation2
+ */
+class Translation2_Admin_Container_mdb extends Translation2_Container_mdb
+{
+ // {{{ addLang()
+
+ /**
+ * Creates a new table to store the strings in this language.
+ * If the table is shared with other langs, it is ALTERed to
+ * hold strings in this lang too.
+ *
+ * @param array $langData language data
+ * @param array $options options
+ *
+ * @return true|PEAR_Error
+ */
+ function addLang($langData, $options = array())
+ {
+ $tables = $this->db->listTables();
+ if (PEAR::isError($tables)) {
+ return $tables;
+ }
+
+ $lang_col = $this->_getLangCol($langData['lang_id']);
+
+ if (in_array($langData['table_name'], $tables)) {
+ //table exists
+ $query = sprintf('ALTER TABLE %s ADD %s%s TEXT',
+ $this->db->quoteIdentifier($langData['table_name']),
+ $this->db->phptype == 'mssql' ? '' : 'COLUMN ',
+ $this->db->quoteIdentifier($lang_col)
+ );
+ ++$this->_queries;
+ return $this->db->query($query);
+ }
+
+ //table does not exist
+ $queries = array();
+ $queries[] = sprintf('CREATE TABLE %s ( '
+ .'%s VARCHAR(%d) default NULL, '
+ .'%s TEXT NOT NULL, '
+ .'%s TEXT )',
+ $this->db->quoteIdentifier($langData['table_name']),
+ $this->db->quoteIdentifier($this->options['string_page_id_col']),
+ (int)$this->options['string_page_id_col_length'],
+ $this->db->quoteIdentifier($this->options['string_id_col']),
+ $this->db->quoteIdentifier($lang_col)
+ );
+ $mysqlClause = ($this->db->phptype == 'mysql') ? '(255)' : '';
+
+ $index_name = sprintf('%s_%s_%s_index',
+ $langData['table_name'],
+ $this->options['string_page_id_col'],
+ $this->options['string_id_col']
+ );
+ $queries[] = sprintf('CREATE UNIQUE INDEX %s ON %s (%s, %s%s)',
+ $this->db->quoteIdentifier($index_name),
+ $this->db->quoteIdentifier($langData['table_name']),
+ $this->db->quoteIdentifier($this->options['string_page_id_col']),
+ $this->db->quoteIdentifier($this->options['string_id_col']),
+ $mysqlClause
+ );
+
+ $index_name = sprintf('%s_%s_index',
+ $langData['table_name'],
+ $this->options['string_page_id_col']
+ );
+ $queries[] = sprintf('CREATE INDEX %s ON %s (%s)',
+ $this->db->quoteIdentifier($index_name),
+ $this->db->quoteIdentifier($langData['table_name']),
+ $this->db->quoteIdentifier($this->options['string_page_id_col'])
+ );
+
+ $index_name = sprintf('%s_%s_index',
+ $langData['table_name'],
+ $this->options['string_id_col']
+ );
+ $queries[] = sprintf('CREATE INDEX %s ON %s (%s%s)',
+ $this->db->quoteIdentifier($index_name),
+ $this->db->quoteIdentifier($langData['table_name']),
+ $this->db->quoteIdentifier($this->options['string_id_col']),
+ $mysqlClause
+ );
+
+ foreach ($queries as $query) {
+ ++$this->_queries;
+ $res = $this->db->query($query);
+ if (PEAR::isError($res)) {
+ return $res;
+ }
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ addLangToList()
+
+ /**
+ * Creates a new entry in the langsAvail table.
+ * If the table doesn't exist yet, it is created.
+ *
+ * @param array $langData array('lang_id' => 'en',
+ * 'table_name' => 'i18n',
+ * 'name' => 'english',
+ * 'meta' => 'some meta info',
+ * 'error_text' => 'not available',
+ * 'encoding' => 'iso-8859-1');
+ *
+ * @return true|PEAR_Error
+ */
+ function addLangToList($langData)
+ {
+ $tables = $this->db->listTables();
+ if (PEAR::isError($tables)) {
+ return $tables;
+ }
+
+ if (!in_array($this->options['langs_avail_table'], $tables)) {
+ $queries = array();
+ $queries[] = sprintf('CREATE TABLE %s ('
+ .'%s VARCHAR(16), '
+ .'%s VARCHAR(200), '
+ .'%s TEXT, '
+ .'%s VARCHAR(250), '
+ .'%s VARCHAR(16) )',
+ $this->db->quoteIdentifier($this->options['langs_avail_table']),
+ $this->db->quoteIdentifier($this->options['lang_id_col']),
+ $this->db->quoteIdentifier($this->options['lang_name_col']),
+ $this->db->quoteIdentifier($this->options['lang_meta_col']),
+ $this->db->quoteIdentifier($this->options['lang_errmsg_col']),
+ $this->db->quoteIdentifier($this->options['lang_encoding_col'])
+ );
+ $queries[] = sprintf('CREATE UNIQUE INDEX %s_%s_index ON %s (%s)',
+ $this->options['langs_avail_table'],
+ $this->options['lang_id_col'],
+ $this->db->quoteIdentifier($this->options['langs_avail_table']),
+ $this->db->quoteIdentifier($this->options['lang_id_col'])
+ );
+
+ foreach ($queries as $query) {
+ ++$this->_queries;
+ $res = $this->db->query($query);
+ if (PEAR::isError($res)) {
+ return $res;
+ }
+ }
+ }
+
+ $query = sprintf('INSERT INTO %s (%s, %s, %s, %s, %s) VALUES (%s, %s, %s, %s, %s)',
+ $this->db->quoteIdentifier($this->options['langs_avail_table']),
+ $this->db->quoteIdentifier($this->options['lang_id_col']),
+ $this->db->quoteIdentifier($this->options['lang_name_col']),
+ $this->db->quoteIdentifier($this->options['lang_meta_col']),
+ $this->db->quoteIdentifier($this->options['lang_errmsg_col']),
+ $this->db->quoteIdentifier($this->options['lang_encoding_col']),
+ $this->db->getTextValue($langData['lang_id']),
+ $this->db->getTextValue($langData['name']),
+ $this->db->getTextValue($langData['meta']),
+ $this->db->getTextValue($langData['error_text']),
+ $this->db->getTextValue($langData['encoding'])
+ );
+
+ ++$this->_queries;
+ $success = $this->db->query($query);
+ $this->options['strings_tables'][$langData['lang_id']] = $langData['table_name'];
+ return $success;
+ }
+
+ // }}}
+ // {{{ removeLang()
+
+ /**
+ * Remove the lang from the langsAvail table and drop the strings table.
+ * If the strings table holds other langs and $force==false, then
+ * only the lang column is dropped. If $force==true the whole
+ * table is dropped without any check
+ *
+ * @param string $langID language ID
+ * @param boolean $force if true, drop the whole table without further checks
+ *
+ * @return true|PEAR_Error
+ */
+ function removeLang($langID, $force)
+ {
+ //remove from langsAvail
+ $query = sprintf('DELETE FROM %s WHERE %s = %s',
+ $this->db->quoteIdentifier($this->options['langs_avail_table']),
+ $this->db->quoteIdentifier($this->options['lang_id_col']),
+ $this->db->getTextValue($langID)
+ );
+ ++$this->_queries;
+ $res = $this->db->query($query);
+ if (PEAR::isError($res)) {
+ return $res;
+ }
+
+ $lang_table = $this->_getLangTable($langID);
+ if ($force) {
+ //remove the whole table
+ ++$this->_queries;
+ return $this->db->query('DROP TABLE ' . $this->db->quoteIdentifier($lang_table));
+ }
+
+ //drop only the column for this lang
+ $query = sprintf('ALTER TABLE %s DROP COLUMN %s',
+ $this->db->quoteIdentifier($lang_table),
+ $this->db->quoteIdentifier($this->_getLangCol($langID))
+ );
+ ++$this->_queries;
+ return $this->db->query($query);
+ }
+
+ // }}}
+ // {{{ updateLang()
+
+ /**
+ * Update the lang info in the langsAvail table
+ *
+ * @param array $langData array of language data
+ *
+ * @return true|PEAR_Error
+ */
+ function updateLang($langData)
+ {
+ $allFields = array(
+ //'lang_id' => 'lang_id_col',
+ 'name' => 'lang_name_col',
+ 'meta' => 'lang_meta_col',
+ 'error_text' => 'lang_errmsg_col',
+ 'encoding' => 'lang_encoding_col',
+ );
+ $updateFields = array_keys($langData);
+ $langSet = array();
+ foreach ($allFields as $field => $col) {
+ if (in_array($field, $updateFields)) {
+ $langSet[] = $this->db->quoteIdentifier($this->options[$col]) . ' = ' .
+ $this->db->getTextValue($langData[$field]);
+ }
+ }
+ $query = sprintf('UPDATE %s SET %s WHERE %s=%s',
+ $this->db->quoteIdentifier($this->options['langs_avail_table']),
+ implode(', ', $langSet),
+ $this->db->quoteIdentifier($this->options['lang_id_col']),
+ $this->db->getTextValue($langData['lang_id'])
+ );
+
+ ++$this->_queries;
+ $success = $this->db->query($query);
+ $this->fetchLangs(); //update memory cache
+ return $success;
+ }
+
+ // }}}
+ // {{{ add()
+
+ /**
+ * Add a new entry in the strings table.
+ *
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ * @param array $stringArray Associative array with string translations.
+ * Sample format: array('en' => 'sample', 'it' => 'esempio')
+ *
+ * @return true|PEAR_Error
+ */
+ function add($stringID, $pageID, $stringArray)
+ {
+ $langs = array_intersect(
+ array_keys($stringArray),
+ $this->getLangs('ids')
+ );
+
+ if (!count($langs)) {
+ //return error: no valid lang provided
+ return true;
+ }
+
+ // Langs may be in different tables - we need to split up queries along
+ // table lines, so we can keep DB traffic to a minimum.
+
+ $unquoted_stringID = $stringID;
+ $unquoted_pageID = $pageID;
+ $stringID = $this->db->getTextValue($stringID);
+ $pageID = is_null($pageID) ? 'NULL' : $this->db->getTextValue($pageID);
+ // Loop over the tables we need to insert into.
+ foreach ($this->_tableLangs($langs) as $table => $tableLangs) {
+ $exists = $this->_recordExists($unquoted_stringID, $unquoted_pageID, $table);
+ if (PEAR::isError($exists)) {
+ return $exists;
+ }
+ $func = $exists ? '_getUpdateQuery' : '_getInsertQuery';
+ $query = $this->$func($table, $tableLangs, $stringID, $pageID, $stringArray);
+
+ ++$this->_queries;
+ $res = $this->db->query($query);
+ if (PEAR::isError($res)) {
+ return $res;
+ }
+ }
+
+ return true;
+ }
+
+ // }}}
+ // {{{ update()
+
+ /**
+ * Update an existing entry in the strings table.
+ *
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ * @param array $stringArray Associative array with string translations.
+ * Sample format: array('en' => 'sample', 'it' => 'esempio')
+ *
+ * @return true|PEAR_Error
+ */
+ function update($stringID, $pageID, $stringArray)
+ {
+ return $this->add($stringID, $pageID, $stringArray);
+ }
+
+ // }}}
+ // {{{ _getInsertQuery()
+
+ /**
+ * Build a SQL query to INSERT a record
+ *
+ * @param string $table table name
+ * @param array &$tableLangs tables containing the languages
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ * @param array &$stringArray array of strings
+ *
+ * @return string INSERT query
+ * @access private
+ */
+ function _getInsertQuery($table, &$tableLangs, $stringID, $pageID, &$stringArray)
+ {
+ $tableCols = $this->_getLangCols($tableLangs);
+ $langData = array();
+ foreach ($tableLangs as $lang) {
+ $langData[$lang] = $this->db->getTextValue($stringArray[$lang]);
+ }
+ foreach (array_keys($tableCols) as $k) {
+ $tableCols[$k] = $this->db->quoteIdentifier($tableCols[$k]);
+ }
+
+ return sprintf('INSERT INTO %s (%s, %s, %s) VALUES (%s, %s, %s)',
+ $this->db->quoteIdentifier($table),
+ $this->db->quoteIdentifier($this->options['string_id_col']),
+ $this->db->quoteIdentifier($this->options['string_page_id_col']),
+ implode(', ', $tableCols),
+ $stringID,
+ $pageID,
+ implode(', ', $langData)
+ );
+ }
+
+ // }}}
+ // {{{ _getUpdateQuery()
+
+ /**
+ * Build a SQL query to UPDATE a record
+ *
+ * @param string $table table name
+ * @param array &$tableLangs tables containing the languages
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ * @param array &$stringArray array of strings
+ *
+ * @return string UPDATE query
+ * @access private
+ */
+ function _getUpdateQuery($table, &$tableLangs, $stringID, $pageID, &$stringArray)
+ {
+ $tableCols = $this->_getLangCols($tableLangs);
+ $langSet = array();
+ foreach ($tableLangs as $lang) {
+ $langSet[] = $this->db->quoteIdentifier($tableCols[$lang]) . ' = ' .
+ $this->db->getTextValue($stringArray[$lang]);
+ }
+
+ return sprintf('UPDATE %s SET %s WHERE %s = %s AND %s = %s',
+ $this->db->quoteIdentifier($table),
+ implode(', ', $langSet),
+ $this->db->quoteIdentifier($this->options['string_id_col']),
+ $stringID,
+ $this->db->quoteIdentifier($this->options['string_page_id_col']),
+ $pageID
+ );
+ }
+
+ // }}}
+ // {{{ remove()
+
+ /**
+ * Remove an entry from the strings table.
+ *
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ *
+ * @return boolean|PEAR_Error
+ */
+ function remove($stringID, $pageID)
+ {
+ $tables = array_unique($this->_getLangTables());
+
+ $stringID = $this->db->getTextValue($stringID);
+ // get the tables and skip the non existent ones
+ $dbTables = $this->db->listTables();
+ foreach ($tables as $table) {
+ if (!in_array($table, $dbTables)) {
+ continue;
+ }
+ $query = sprintf('DELETE FROM %s WHERE %s = %s AND %s',
+ $this->db->quoteIdentifier($table),
+ $this->db->quoteIdentifier($this->options['string_id_col']),
+ $stringID,
+ $this->db->quoteIdentifier($this->options['string_page_id_col'])
+ );
+ if (is_null($pageID)) {
+ $query .= ' IS NULL';
+ } else {
+ $query .= ' = ' . $this->db->getTextValue($pageID);
+ }
+
+ ++$this->_queries;
+ $res = $this->db->query($query);
+ if (PEAR::isError($res)) {
+ return $res;
+ }
+ }
+
+ return true;
+ }
+
+ // }}}
+ // {{{ removePage
+
+ /**
+ * Remove all the strings in the given page/group
+ *
+ * @param string $pageID page/group ID
+ *
+ * @return mixed true on success, PEAR_Error on failure
+ */
+ function removePage($pageID = null)
+ {
+ $tables = array_unique($this->_getLangTables());
+
+ // get the tables and skip the non existent ones
+ $dbTables = $this->db->listTables();
+ foreach ($tables as $table) {
+ if (!in_array($table, $dbTables)) {
+ continue;
+ }
+ $query = sprintf('DELETE FROM %s WHERE %s',
+ $this->db->quoteIdentifier($table, true),
+ $this->db->quoteIdentifier($this->options['string_page_id_col'], true)
+ );
+ if (is_null($pageID)) {
+ $query .= ' IS NULL';
+ } else {
+ $query .= ' = ' . $this->db->getTextValue($pageID);
+ }
+
+ ++$this->_queries;
+ $res = $this->db->query($query);
+ if (PEAR::isError($res)) {
+ return $res;
+ }
+ }
+
+ return true;
+ }
+
+ // }}}
+ // {{{ getPageNames()
+
+ /**
+ * Get a list of all the pageIDs in any table.
+ *
+ * @return array
+ */
+ function getPageNames()
+ {
+ $pages = array();
+ foreach ($this->_getLangTables() as $table) {
+ $query = sprintf('SELECT DISTINCT %s FROM %s',
+ $this->db->quoteIdentifier($this->options['string_page_id_col']),
+ $this->db->quoteIdentifier($table)
+ );
+ ++$this->_queries;
+ $res = $this->db->getCol($query);
+ if (PEAR::isError($res)) {
+ return $res;
+ }
+ $pages = array_merge($pages, $res);
+ }
+ return array_unique($pages);
+ }
+
+ // }}}
+ // {{{ _tableLangs()
+
+ /**
+ * Get table -> language mapping
+ *
+ * The key of the array is the table that a language is stored in;
+ * the value is an /array/ of languages stored in that table.
+ *
+ * @param array $langs Languages to get mapping for
+ *
+ * @return array Table -> language mapping
+ * @access private
+ * @see Translation2_Container_MDB::_getLangTable()
+ */
+ function &_tableLangs($langs)
+ {
+ $tables = array();
+ foreach ($langs as $lang) {
+ $table = $this->_getLangTable($lang);
+ $tables[$table][] = $lang;
+ }
+ return $tables;
+ }
+
+ // }}}
+ // {{{ _getLangTables()
+
+ /**
+ * Get tables for languages
+ *
+ * This is like _getLangTable(), but it returns an array of the tables for
+ * multiple languages.
+ *
+ * @param array $langs Languages to get tables for
+ *
+ * @return array
+ * @access private
+ */
+ function &_getLangTables($langs = null)
+ {
+ $tables = array();
+ $langs = !is_array($langs) ? $this->getLangs('ids') : $langs;
+ foreach ($langs as $lang) {
+ $tables[] = $this->_getLangTable($lang);
+ }
+ $tables = array_unique($tables);
+ return $tables;
+ }
+
+ // }}}
+ // {{{ _getLangCols()
+
+ /**
+ * Get table columns strings are stored in
+ *
+ * This is like _getLangCol(), except it returns an array which contains
+ * the mapping for multiple languages.
+ *
+ * @param array $langs Languages to get mapping for
+ *
+ * @return array Language -> column mapping
+ * @access private
+ * @see Translation2_Container_MDB::_getLangCol()
+ */
+ function &_getLangCols($langs)
+ {
+ $cols = array();
+ foreach ($langs as $lang) {
+ $cols[$lang] = $this->_getLangCol($lang);
+ }
+ return $cols;
+ }
+
+ // }}}
+ // {{{ _recordExists()
+
+ /**
+ * Check if there's already a record in the table with the
+ * given (pageID, stringID) pair.
+ *
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ * @param string $table table name
+ *
+ * @return boolean
+ * @access private
+ */
+ function _recordExists($stringID, $pageID, $table)
+ {
+ $stringID = $this->db->getTextValue($stringID);
+ $pageID = is_null($pageID) ? ' IS NULL' : ' = ' . $this->db->getTextValue($pageID);
+ $query = sprintf('SELECT COUNT(*) FROM %s WHERE %s=%s AND %s%s',
+ $this->db->quoteIdentifier($table),
+ $this->db->quoteIdentifier($this->options['string_id_col']),
+ $stringID,
+ $this->db->quoteIdentifier($this->options['string_page_id_col']),
+ $pageID
+ );
+ ++$this->_queries;
+ $res = $this->db->getOne($query);
+ if (PEAR::isError($res)) {
+ return $res;
+ }
+ return ($res > 0);
+ }
+
+ // }}}
+ // {{{ _filterStringsByTable()
+
+ /**
+ * Get only the strings for the langs in the given table
+ *
+ * @param array $stringArray Associative array with string translations.
+ * Sample format: array('en' => 'sample', 'it' => 'esempio')
+ * @param string $table table name
+ *
+ * @return array strings
+ * @access private
+ */
+ function &_filterStringsByTable($stringArray, $table)
+ {
+ $strings = array();
+ foreach ($stringArray as $lang => $string) {
+ if ($table == $this->_getLangTable($lang)) {
+ $strings[$lang] = $string;
+ }
+ }
+ return $strings;
+ }
+
+ // }}}
+ // {{{ _getLangsInTable()
+
+ /**
+ * Get the languages sharing the given table
+ *
+ * @param string $table table name
+ *
+ * @return array
+ */
+ function &_getLangsInTable($table)
+ {
+ $this->fetchLangs(); // force cache refresh
+ $langsInTable = array();
+ foreach (array_keys($this->langs) as $lang) {
+ if ($table == $this->_getLangTable($lang)) {
+ $langsInTable[] = $lang;
+ }
+ }
+ return $langsInTable;
+ }
+
+ // }}}
+}
+?>
\ No newline at end of file
diff --git a/Translation2/Admin/Container/mdb2.php b/Translation2/Admin/Container/mdb2.php
new file mode 100644
index 0000000..4d3da17
--- /dev/null
+++ b/Translation2/Admin/Container/mdb2.php
@@ -0,0 +1,758 @@
+
+ * @copyright 2004-2007 Lorenzo Alberton
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @version CVS: $Id$
+ * @link http://pear.php.net/package/Translation2
+ */
+
+/**
+ * require Translation2_Container_mdb2 class
+ */
+require_once 'Translation2/Container/mdb2.php';
+
+/**
+ * Storage driver for storing/fetching data to/from a database
+ *
+ * This storage driver can use all databases which are supported
+ * by the PEAR::MDB2 abstraction layer to store and fetch data.
+ *
+ * @category Internationalization
+ * @package Translation2
+ * @author Lorenzo Alberton
+ * @copyright 2004-2007 Lorenzo Alberton
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @link http://pear.php.net/package/Translation2
+ */
+class Translation2_Admin_Container_mdb2 extends Translation2_Container_mdb2
+{
+ // {{{
+
+ /**
+ * Fetch the table names from the db
+ *
+ * @access private
+ * @return array|PEAR_Error
+ */
+ function _fetchTableNames()
+ {
+ $this->db->loadModule('Manager');
+ return $this->db->manager->listTables();
+ }
+
+ // }}}
+ // {{{ addLang()
+
+ /**
+ * Creates a new table to store the strings in this language.
+ * If the table is shared with other langs, it is ALTERed to
+ * hold strings in this lang too.
+ *
+ * @param array $langData array('lang_id' => 'en',
+ * 'table_name' => 'i18n',
+ * 'name' => 'english',
+ * 'meta' => 'some meta info',
+ * 'error_text' => 'not available');
+ * @param array $options array('charset' => 'utf8',
+ * 'collation' => 'utf8_general_ci');
+ *
+ * @return true|PEAR_Error
+ */
+ function addLang($langData, $options = array())
+ {
+ $tables = $this->_fetchTableNames();
+ if (PEAR::isError($tables)) {
+ return $tables;
+ }
+
+ $lang_col = $this->_getLangCol($langData['lang_id']);
+ $charset = empty($options['charset']) ? null : $options['charset'];
+ $collation = empty($options['collation']) ? null : $options['collation'];
+ $this->db->loadModule('Manager');
+
+ if (in_array($langData['table_name'], $tables)) {
+ //table exists
+ $table_changes = array(
+ 'add' => array(
+ $lang_col => array(
+ 'type' => 'text',
+ 'charset' => $charset,
+ 'collation' => $collation,
+ )
+ )
+ );
+ ++$this->_queries;
+ return $this->db->manager->alterTable($langData['table_name'], $table_changes, false);
+ }
+
+ //table does not exist
+ $table_definition = array(
+ $this->options['string_page_id_col'] => array(
+ 'type' => 'text',
+ 'length' => $this->options['string_page_id_col_length'],
+ 'default' => null,
+ 'charset' => $charset,
+ 'collation' => $collation,
+ ),
+ $this->options['string_id_col'] => array(
+ 'type' => 'text',
+ 'notnull' => 1,
+ 'charset' => $charset,
+ 'collation' => $collation,
+ ),
+ $lang_col => array(
+ 'type' => 'text',
+ 'charset' => $charset,
+ 'collation' => $collation,
+ ),
+ );
+ ++$this->_queries;
+ $table_options = array(
+ 'charset' => $charset,
+ 'collate' => $collation,
+ );
+ $res = $this->db->manager->createTable($langData['table_name'], $table_definition, $table_options);
+ if (PEAR::isError($res)) {
+ return $res;
+ }
+ $mysqlClause = ($this->db->phptype == 'mysql') ? '(255)' : '';
+
+ $constraint_name = $langData['table_name']
+ .'_'. $this->options['string_page_id_col']
+ .'_'. $this->options['string_id_col'];
+ $constraint_definition = array(
+ 'fields' => array(
+ $this->options['string_page_id_col'] => array(),
+ $this->options['string_id_col'].$mysqlClause => array(),
+ ),
+ 'unique' => true,
+ );
+ ++$this->_queries;
+ $res = $this->db->manager->createConstraint($langData['table_name'], $constraint_name, $constraint_definition);
+ if (PEAR::isError($res)) {
+ return $res;
+ }
+
+ $index_name = $langData['table_name'] .'_'. $this->options['string_page_id_col'];
+ $index_definition = array(
+ 'fields' => array($this->options['string_page_id_col'] => array())
+ );
+ ++$this->_queries;
+ $res = $this->db->manager->createIndex($langData['table_name'], $index_name, $index_definition);
+ if (PEAR::isError($res)) {
+ return $res;
+ }
+
+ $index_name = $langData['table_name'] .'_'. $this->options['string_id_col'];
+ $index_definition = array(
+ 'fields' => array($this->options['string_id_col'] => array('length' => 255))
+ );
+ ++$this->_queries;
+ $res = $this->db->manager->createIndex($langData['table_name'], $index_name, $index_definition);
+ if (PEAR::isError($res)) {
+ return $res;
+ }
+
+ return true;
+ }
+
+ // }}}
+ // {{{ addLangToList()
+
+ /**
+ * Creates a new entry in the langsAvail table.
+ * If the table doesn't exist yet, it is created.
+ *
+ * @param array $langData array('lang_id' => 'en',
+ * 'table_name' => 'i18n',
+ * 'name' => 'english',
+ * 'meta' => 'some meta info',
+ * 'error_text' => 'not available',
+ * 'encoding' => 'iso-8859-1');
+ *
+ * @return true|PEAR_Error
+ */
+ function addLangToList($langData)
+ {
+ $tables = $this->_fetchTableNames();
+ if (PEAR::isError($tables)) {
+ return $tables;
+ }
+
+ if (!in_array($this->options['langs_avail_table'], $tables)) {
+ $queries = array();
+ $queries[] = sprintf('CREATE TABLE %s ('
+ .'%s VARCHAR(16), '
+ .'%s VARCHAR(200), '
+ .'%s TEXT, '
+ .'%s VARCHAR(250), '
+ .'%s VARCHAR(16) )',
+ $this->db->quoteIdentifier($this->options['langs_avail_table'], true),
+ $this->db->quoteIdentifier($this->options['lang_id_col'], true),
+ $this->db->quoteIdentifier($this->options['lang_name_col'], true),
+ $this->db->quoteIdentifier($this->options['lang_meta_col'], true),
+ $this->db->quoteIdentifier($this->options['lang_errmsg_col'], true),
+ $this->db->quoteIdentifier($this->options['lang_encoding_col'], true)
+ );
+ $queries[] = sprintf('CREATE UNIQUE INDEX %s_%s_index ON %s (%s)',
+ $this->db->quoteIdentifier($this->options['langs_avail_table'], true),
+ $this->db->quoteIdentifier($this->options['lang_id_col'], true),
+ $this->db->quoteIdentifier($this->options['langs_avail_table'], true),
+ $this->db->quoteIdentifier($this->options['lang_id_col'], true)
+ );
+
+ foreach ($queries as $query) {
+ ++$this->_queries;
+ $res = $this->db->exec($query);
+ if (PEAR::isError($res)) {
+ return $res;
+ }
+ }
+ }
+
+ $query = sprintf('INSERT INTO %s (%s, %s, %s, %s, %s) VALUES (%s, %s, %s, %s, %s)',
+ $this->db->quoteIdentifier($this->options['langs_avail_table'], true),
+ $this->db->quoteIdentifier($this->options['lang_id_col'], true),
+ $this->db->quoteIdentifier($this->options['lang_name_col'], true),
+ $this->db->quoteIdentifier($this->options['lang_meta_col'], true),
+ $this->db->quoteIdentifier($this->options['lang_errmsg_col'], true),
+ $this->db->quoteIdentifier($this->options['lang_encoding_col'], true),
+ $this->db->quote($langData['lang_id']),
+ $this->db->quote($langData['name']),
+ $this->db->quote($langData['meta']),
+ $this->db->quote($langData['error_text']),
+ $this->db->quote($langData['encoding'])
+ );
+
+ ++$this->_queries;
+ $res = $this->db->exec($query);
+ $this->options['strings_tables'][$langData['lang_id']] = $langData['table_name'];
+ if (PEAR::isError($res)) {
+ return $res;
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ removeLang()
+
+ /**
+ * Remove the lang from the langsAvail table and drop the strings table.
+ * If the strings table holds other langs and $force==false, then
+ * only the lang column is dropped. If $force==true the whole
+ * table is dropped without any check
+ *
+ * @param string $langID language ID
+ * @param boolean $force if true, the whole table is dropped without checks
+ *
+ * @return true|PEAR_Error
+ */
+ function removeLang($langID, $force)
+ {
+ //remove from langsAvail
+ $query = sprintf('DELETE FROM %s WHERE %s = %s',
+ $this->db->quoteIdentifier($this->options['langs_avail_table'], true),
+ $this->db->quoteIdentifier($this->options['lang_id_col'], true),
+ $this->db->quote($langID, 'text')
+ );
+ ++$this->_queries;
+ $res = $this->db->exec($query);
+ if (PEAR::isError($res)) {
+ return $res;
+ }
+
+ $this->db->loadModule('Manager');
+ $lang_table = $this->_getLangTable($langID);
+ if ($force) {
+ //remove the whole table
+ ++$this->_queries;
+ return $this->db->manager->dropTable($lang_table);
+ }
+
+ //drop only the column for this lang
+ $table_changes = array(
+ 'remove' => array($this->_getLangCol($langID) => array())
+ );
+ ++$this->_queries;
+ return $this->db->manager->alterTable($lang_table, $table_changes, false);
+ }
+
+ // }}}
+ // {{{ updateLang()
+
+ /**
+ * Update the lang info in the langsAvail table
+ *
+ * @param array $langData language data
+ *
+ * @return true|PEAR_Error
+ */
+ function updateLang($langData)
+ {
+ $allFields = array(
+ //'lang_id' => 'lang_id_col',
+ 'name' => 'lang_name_col',
+ 'meta' => 'lang_meta_col',
+ 'error_text' => 'lang_errmsg_col',
+ 'encoding' => 'lang_encoding_col',
+ );
+ $updateFields = array_keys($langData);
+ $langSet = array();
+ foreach ($allFields as $field => $col) {
+ if (in_array($field, $updateFields)) {
+ $langSet[] = $this->db->quoteIdentifier($this->options[$col], true) . ' = ' .
+ $this->db->quote($langData[$field]);
+ }
+ }
+ $query = sprintf('UPDATE %s SET %s WHERE %s=%s',
+ $this->db->quoteIdentifier($this->options['langs_avail_table'], true),
+ implode(', ', $langSet),
+ $this->db->quoteIdentifier($this->options['lang_id_col'], true),
+ $this->db->quote($langData['lang_id'])
+ );
+
+ ++$this->_queries;
+ $res = $this->db->exec($query);
+ $this->fetchLangs(); //update memory cache
+ if (PEAR::isError($res)) {
+ return $res;
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ add()
+
+ /**
+ * Add a new entry in the strings table.
+ *
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ * @param array $stringArray Associative array with string translations.
+ * Sample format: array('en' => 'sample', 'it' => 'esempio')
+ *
+ * @return true|PEAR_Error
+ */
+ function add($stringID, $pageID, $stringArray)
+ {
+ $langs = array_intersect(
+ array_keys($stringArray),
+ $this->getLangs('ids')
+ );
+
+ if (!count($langs)) {
+ //return error: no valid lang provided
+ return true;
+ }
+
+ // Langs may be in different tables - we need to split up queries along
+ // table lines, so we can keep DB traffic to a minimum.
+
+ $unquoted_stringID = $stringID;
+ $unquoted_pageID = $pageID;
+ $stringID = $this->db->quote($stringID, 'text');
+ $pageID = is_null($pageID) ? 'NULL' : $this->db->quote($pageID, 'text');
+ // Loop over the tables we need to insert into.
+ foreach ($this->_tableLangs($langs) as $table => $tableLangs) {
+ $exists = $this->_recordExists($unquoted_stringID, $unquoted_pageID, $table);
+ if (PEAR::isError($exists)) {
+ return $exists;
+ }
+ $func = $exists ? '_getUpdateQuery' : '_getInsertQuery';
+ $query = $this->$func($table, $tableLangs, $stringID, $pageID, $stringArray);
+
+ ++$this->_queries;
+ $res = $this->db->exec($query);
+ if (PEAR::isError($res)) {
+ return $res;
+ }
+ }
+
+ return true;
+ }
+
+ // }}}
+ // {{{ update()
+
+ /**
+ * Update an existing entry in the strings table.
+ *
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ * @param array $stringArray Associative array with string translations.
+ * Sample format: array('en' => 'sample', 'it' => 'esempio')
+ *
+ * @return true|PEAR_Error
+ */
+ function update($stringID, $pageID, $stringArray)
+ {
+ return $this->add($stringID, $pageID, $stringArray);
+ }
+
+ // }}}
+ // {{{ _getInsertQuery()
+
+ /**
+ * Build a SQL query to INSERT a record
+ *
+ * @param string $table table name
+ * @param array &$tableLangs tables containing the languages
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ * @param array &$stringArray array of strings
+ *
+ * @return string INSERT query
+ * @access private
+ */
+ function _getInsertQuery($table, &$tableLangs, $stringID, $pageID, &$stringArray)
+ {
+ $tableCols = $this->_getLangCols($tableLangs);
+ $langData = array();
+ foreach ($tableLangs as $lang) {
+ $langData[$lang] = $this->db->quote($stringArray[$lang], 'text');
+ }
+ foreach (array_keys($tableCols) as $k) {
+ $tableCols[$k] = $this->db->quoteIdentifier($tableCols[$k], true);
+ }
+
+ return sprintf('INSERT INTO %s (%s, %s, %s) VALUES (%s, %s, %s)',
+ $this->db->quoteIdentifier($table, true),
+ $this->db->quoteIdentifier($this->options['string_id_col'], true),
+ $this->db->quoteIdentifier($this->options['string_page_id_col'], true),
+ implode(', ', $tableCols),
+ $stringID,
+ $pageID,
+ implode(', ', $langData)
+ );
+ }
+
+ // }}}
+ // {{{ _getUpdateQuery()
+
+ /**
+ * Build a SQL query to UPDATE a record
+ *
+ * @param string $table table name
+ * @param array &$tableLangs tables containing the languages
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ * @param array &$stringArray array of strings
+ *
+ * @return string UPDATE query
+ * @access private
+ */
+ function _getUpdateQuery($table, &$tableLangs, $stringID, $pageID, &$stringArray)
+ {
+ $tableCols = $this->_getLangCols($tableLangs);
+ $langSet = array();
+ foreach ($tableLangs as $lang) {
+ $langSet[] = $this->db->quoteIdentifier($tableCols[$lang], true) . ' = ' .
+ $this->db->quote($stringArray[$lang], 'text');
+ }
+
+ return sprintf('UPDATE %s SET %s WHERE %s = %s AND %s = %s',
+ $this->db->quoteIdentifier($table, true),
+ implode(', ', $langSet),
+ $this->db->quoteIdentifier($this->options['string_id_col'], true),
+ $stringID,
+ $this->db->quoteIdentifier($this->options['string_page_id_col'], true),
+ $pageID
+ );
+ }
+
+ // }}}
+ // {{{ remove()
+
+ /**
+ * Remove an entry from the strings table.
+ *
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ *
+ * @return mixed true on success, PEAR_Error on failure
+ */
+ function remove($stringID, $pageID)
+ {
+ $tables = array_unique($this->_getLangTables());
+
+ $stringID = $this->db->quote($stringID, 'text');
+ // get the tables and skip the non existent ones
+ $dbTables = $this->_fetchTableNames();
+ foreach ($tables as $table) {
+ if (!in_array($table, $dbTables)) {
+ continue;
+ }
+ $query = sprintf('DELETE FROM %s WHERE %s = %s AND %s',
+ $this->db->quoteIdentifier($table, true),
+ $this->db->quoteIdentifier($this->options['string_id_col'], true),
+ $stringID,
+ $this->db->quoteIdentifier($this->options['string_page_id_col'], true)
+ );
+ if (is_null($pageID)) {
+ $query .= ' IS NULL';
+ } else {
+ $query .= ' = ' . $this->db->quote($pageID, 'text');
+ }
+
+ ++$this->_queries;
+ $res = $this->db->exec($query);
+ if (PEAR::isError($res)) {
+ return $res;
+ }
+ }
+
+ return true;
+ }
+
+ // }}}
+ // {{{ removePage
+
+ /**
+ * Remove all the strings in the given page/group
+ *
+ * @param string $pageID page/group ID
+ *
+ * @return mixed true on success, PEAR_Error on failure
+ */
+ function removePage($pageID = null)
+ {
+ $tables = array_unique($this->_getLangTables());
+
+ // get the tables and skip the non existent ones
+ $dbTables = $this->_fetchTableNames();
+ foreach ($tables as $table) {
+ if (!in_array($table, $dbTables)) {
+ continue;
+ }
+ $query = sprintf('DELETE FROM %s WHERE %s',
+ $this->db->quoteIdentifier($table, true),
+ $this->db->quoteIdentifier($this->options['string_page_id_col'], true)
+ );
+ if (is_null($pageID)) {
+ $query .= ' IS NULL';
+ } else {
+ $query .= ' = ' . $this->db->quote($pageID, 'text');
+ }
+
+ ++$this->_queries;
+ $res = $this->db->exec($query);
+ if (PEAR::isError($res)) {
+ return $res;
+ }
+ }
+
+ return true;
+ }
+
+ // }}}
+ // {{{ getPageNames()
+
+ /**
+ * Get a list of all the pageIDs in any table.
+ *
+ * @return array
+ */
+ function getPageNames()
+ {
+ $pages = array();
+ foreach ($this->_getLangTables() as $table) {
+ $query = sprintf('SELECT DISTINCT %s FROM %s',
+ $this->db->quoteIdentifier($this->options['string_page_id_col'], true),
+ $this->db->quoteIdentifier($table, true)
+ );
+ ++$this->_queries;
+ $res = $this->db->queryCol($query);
+ if (PEAR::isError($res)) {
+ return $res;
+ }
+ $pages = array_merge($pages, $res);
+ }
+ return array_unique($pages);
+ }
+
+ // }}}
+ // {{{ _tableLangs()
+
+ /**
+ * Get table -> language mapping
+ *
+ * The key of the array is the table that a language is stored in;
+ * the value is an /array/ of languages stored in that table.
+ *
+ * @param array $langs Languages to get mapping for
+ *
+ * @return array Table -> language mapping
+ * @access private
+ * @see Translation2_Container_MDB2::_getLangTable()
+ */
+ function &_tableLangs($langs)
+ {
+ $tables = array();
+ foreach ($langs as $lang) {
+ $table = $this->_getLangTable($lang);
+ $tables[$table][] = $lang;
+ }
+ return $tables;
+ }
+
+ // }}}
+ // {{{ _getLangTables()
+
+ /**
+ * Get tables for languages
+ *
+ * This is like _getLangTable(), but it returns an array of the tables for
+ * multiple languages.
+ *
+ * @param array $langs Languages to get tables for
+ *
+ * @return array
+ * @access private
+ */
+ function &_getLangTables($langs = null)
+ {
+ $tables = array();
+ $langs = !is_array($langs) ? $this->getLangs('ids') : $langs;
+ foreach ($langs as $lang) {
+ $tables[] = $this->_getLangTable($lang);
+ }
+ $tables = array_unique($tables);
+ return $tables;
+ }
+
+ // }}}
+ // {{{ _getLangCols()
+
+ /**
+ * Get table columns strings are stored in
+ *
+ * This is like _getLangCol(), except it returns an array which contains
+ * the mapping for multiple languages.
+ *
+ * @param array $langs Languages to get mapping for
+ *
+ * @return array Language -> column mapping
+ * @access private
+ * @see Translation2_Container_DB::_getLangCol()
+ */
+ function &_getLangCols($langs)
+ {
+ $cols = array();
+ foreach ($langs as $lang) {
+ $cols[$lang] = $this->_getLangCol($lang);
+ }
+ return $cols;
+ }
+
+ // }}}
+ // {{{ _recordExists()
+
+ /**
+ * Check if there's already a record in the table with the
+ * given (pageID, stringID) pair.
+ *
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ * @param string $table table name
+ *
+ * @return boolean|PEAR_Error
+ * @access private
+ */
+ function _recordExists($stringID, $pageID, $table)
+ {
+ $stringID = $this->db->quote($stringID, 'text');
+ $pageID = is_null($pageID) ? ' IS NULL' : ' = ' . $this->db->quote($pageID, 'text');
+ $query = sprintf('SELECT COUNT(*) FROM %s WHERE %s=%s AND %s%s',
+ $this->db->quoteIdentifier($table, true),
+ $this->db->quoteIdentifier($this->options['string_id_col'], true),
+ $stringID,
+ $this->db->quoteIdentifier($this->options['string_page_id_col'], true),
+ $pageID
+ );
+ ++$this->_queries;
+ $res = $this->db->queryOne($query);
+ if (PEAR::isError($res)) {
+ return $res;
+ }
+ return ($res > 0);
+ }
+
+ // }}}
+ // {{{ _filterStringsByTable()
+
+ /**
+ * Get only the strings for the langs in the given table
+ *
+ * @param array $stringArray Associative array with string translations.
+ * Sample format: array('en' => 'sample', 'it' => 'esempio')
+ * @param string $table table name
+ *
+ * @return array strings
+ * @access private
+ */
+ function &_filterStringsByTable($stringArray, $table)
+ {
+ $strings = array();
+ foreach ($stringArray as $lang => $string) {
+ if ($table == $this->_getLangTable($lang)) {
+ $strings[$lang] = $string;
+ }
+ }
+ return $strings;
+ }
+
+ // }}}
+ // {{{ _getLangsInTable()
+
+ /**
+ * Get the languages sharing the given table
+ *
+ * @param string $table table name
+ *
+ * @return array
+ */
+ function &_getLangsInTable($table)
+ {
+ $this->fetchLangs(); // force cache refresh
+ $langsInTable = array();
+ foreach (array_keys($this->langs) as $lang) {
+ if ($table == $this->_getLangTable($lang)) {
+ $langsInTable[] = $lang;
+ }
+ }
+ return $langsInTable;
+ }
+
+ // }}}
+}
+?>
\ No newline at end of file
diff --git a/Translation2/Admin/Container/xml.php b/Translation2/Admin/Container/xml.php
new file mode 100644
index 0000000..8f99326
--- /dev/null
+++ b/Translation2/Admin/Container/xml.php
@@ -0,0 +1,408 @@
+
+ * @author Olivier Guilyardi
+ * @copyright 2004-2007 Lorenzo Alberton, Olivier Guilyardi
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @version CVS: $Id$
+ * @link http://pear.php.net/package/Translation2
+ */
+
+/**
+ * require Translation2_Container_xml class
+ */
+require_once 'Translation2/Container/xml.php';
+
+require_once 'XML/Util.php';
+
+/**
+ * Storage driver for storing/fetching data to/from a XML file
+ *
+ * @category Internationalization
+ * @package Translation2
+ * @author Lorenzo Alberton
+ * @author Olivier Guilyardi
+ * @copyright 2004-2007 Lorenzo Alberton, Olivier Guilyardi
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @link http://pear.php.net/package/Translation2
+ */
+class Translation2_Admin_Container_xml extends Translation2_Container_xml
+{
+ // {{{ class vars
+
+ /**
+ * Whether _saveData() is already registered at shutdown or not
+ * @var boolean
+ */
+ var $_isScheduledSaving = false;
+
+ // }}}
+ // {{{ addLang()
+
+ /**
+ * Does nothing (here for compatibility with the container interface)
+ *
+ * @param array $langData language data
+ * @param array $options language options
+ *
+ * @return true|PEAR_Error
+ */
+ function addLang($langData, $options = array())
+ {
+ return true;
+ }
+
+ // }}}
+ // {{{ addLangToList()
+
+ /**
+ * Creates a new entry in the section
+ *
+ * @param array $langData array('lang_id' => 'en',
+ * 'name' => 'english',
+ * 'meta' => 'some meta info',
+ * 'error_text' => 'not available',
+ * 'encoding' => 'iso-8859-1',
+ * );
+ *
+ * @return true|PEAR_Error
+ */
+ function addLangToList($langData)
+ {
+ $validInput = array(
+ 'name' => '',
+ 'meta' => '',
+ 'error_text' => '',
+ 'encoding' => 'iso-8859-1',
+ );
+
+ foreach ($validInput as $key => $val) {
+ if (isset($langData[$key])) $validInput[$key] = $langData[$key];
+ }
+
+ $this->_data['languages'][$langData['lang_id']] = $validInput;
+ return $this->_scheduleSaving();
+ }
+
+ // }}}
+ // {{{ updateLang()
+
+ /**
+ * Update the lang info in the langsAvail table
+ *
+ * @param array $langData array [@see addLangToList()]
+ *
+ * @return true|PEAR_Error
+ */
+ function updateLang($langData)
+ {
+ $allFields = array( //'lang_id',
+ 'name', 'meta', 'error_text', 'encoding',
+ );
+ foreach ($allFields as $field) {
+ if (isset($this->_data['languages'][$langData['lang_id']][$field])) {
+ $this->_data['languages'][$langData['lang_id']][$field] = $langData[$field];
+ }
+ }
+ $success = $this->_scheduleSaving();
+ $this->fetchLangs(); //update memory cache
+ return $success;
+ }
+
+ // }}}
+ // {{{ add()
+
+ /**
+ * Add a new entry in the strings table.
+ *
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ * @param array $stringArray Associative array with string translations.
+ * Sample format: array('en' => 'sample', 'it' => 'esempio')
+ *
+ * @return true|PEAR_Error
+ */
+ function add($stringID, $pageID, $stringArray)
+ {
+ $langs = array_intersect(
+ array_keys($stringArray),
+ $this->getLangs('ids')
+ );
+
+ $pageID = is_null($pageID) ? '#NULL' : $pageID;
+ $pageID = empty($pageID) ? '#EMPTY' : $pageID;
+
+ if (!array_key_exists($pageID, $this->_data['pages'])) {
+ $this->_data['pages'][$pageID] = array();
+ }
+ if (!array_key_exists($stringID, $this->_data['pages'][$pageID])) {
+ $this->_data['pages'][$pageID][$stringID] = array();
+ }
+ foreach ($langs as $lang) {
+ $this->_data['pages'][$pageID][$stringID][$lang] = $stringArray[$lang];
+ }
+
+ return $this->_scheduleSaving();
+ }
+
+ // }}}
+ // {{{ update()
+
+ /**
+ * Update an existing entry in the strings table.
+ *
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ * @param array $stringArray Associative array with string translations.
+ * Sample format: array('en' => 'sample', 'it' => 'esempio')
+ *
+ * @return true|PEAR_Error
+ */
+ function update($stringID, $pageID, $stringArray)
+ {
+ return $this->add($stringID, $pageID, $stringArray);
+ }
+
+ // }}}
+ // {{{ remove()
+
+ /**
+ * Remove an entry from the strings table.
+ *
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ *
+ * @return true|PEAR_Error
+ */
+ function remove($stringID, $pageID)
+ {
+ $pageID = is_null($pageID) ? '#NULL' : $pageID;
+ $pageID = empty($pageID) ? '#EMPTY' : $pageID;
+
+ unset ($this->_data['pages'][$pageID][$stringID]);
+ if (!count($this->_data['pages'][$pageID])) {
+ unset ($this->_data['pages'][$pageID]);
+ }
+
+ return $this->_scheduleSaving();
+ }
+
+ // }}}
+ // {{{ removeLang()
+
+ /**
+ * Remove all the entries for the given lang from the strings table.
+ *
+ * @param string $langID language ID
+ * @param boolean $force (ignored)
+ *
+ * @return true|PEAR_Error
+ */
+ function removeLang($langID, $force = true)
+ {
+ // remove lang metadata
+ unset($this->_data['languages'][$langID]);
+
+ // remove the entries
+ foreach (array_keys($this->_data['pages']) as $pageID) {
+ foreach (array_keys($this->_data['pages'][$pageID]) as $stringID) {
+ if (array_key_exists($langID, $this->_data['pages'][$pageID][$stringID])) {
+ unset($this->_data['pages'][$pageID][$stringID][$langID]);
+ }
+ }
+ }
+ return $this->_scheduleSaving();
+ }
+
+ // }}}
+ // {{{ removePage()
+
+ /**
+ * Remove all the strings in the given page/group
+ *
+ * @param string $pageID page/group ID
+ *
+ * @return true|PEAR_Error
+ */
+ function removePage($pageID = null)
+ {
+ $pageID = is_null($pageID) ? '#NULL' : $pageID;
+ $pageID = empty($pageID) ? '#EMPTY' : $pageID;
+
+ unset ($this->_data['pages'][$pageID]);
+
+ return $this->_scheduleSaving();
+ }
+
+ // }}}
+ // {{{ getPageNames()
+
+ /**
+ * Get a list of all the pageIDs.
+ *
+ * @return array
+ */
+ function getPageNames()
+ {
+ $pages = array_keys($this->_data['pages']);
+ $k = array_search('#NULL', $pages);
+ if ($k !== false && !is_null($k)) {
+ $pages[$k] = null;
+ }
+ $k = array_search('#EMPTY', $pages);
+ if ($k !== false && !is_null($k)) {
+ $pages[$k] = '';
+ }
+ return $pages;
+ }
+
+ // }}}
+ // {{{ _scheduleSaving()
+
+ /**
+ * Prepare data saving
+ *
+ * This methods registers _saveData() as a PEAR shutdown function. This
+ * is to avoid saving multiple times if the programmer makes several
+ * changes.
+ *
+ * @return true|PEAR_Error
+ * @access private
+ * @see Translation2_Admin_Container_xml::_saveData()
+ */
+ function _scheduleSaving()
+ {
+ if ($this->options['save_on_shutdown']) {
+ if (!$this->_isScheduledSaving) {
+ // save the changes on shutdown
+ register_shutdown_function(array(&$this, '_saveData'));
+ $this->_isScheduledSaving = true;
+ }
+ return true;
+ }
+
+ // save the changes now
+ return $this->_saveData();
+ }
+
+ // }}}
+ // {{{ _saveData()
+
+ /**
+ * Serialize and save the updated tranlation data to the XML file
+ *
+ * @return boolean | PEAR_Error
+ * @access private
+ * @see Translation2_Admin_Container_xml::_scheduleSaving()
+ */
+ function _saveData()
+ {
+ if ($this->options['save_on_shutdown']) {
+ $data =& $this->_data;
+ } else {
+ $data = $this->_data;
+ }
+
+ $this->_convertEncodings('to_xml', $data);
+ $this->_convertLangEncodings('to_xml', $data);
+
+ // Serializing
+
+ $xml = "\n\n" .
+ "\n\n" .
+ "\n" .
+ " \n";
+
+ foreach ($data['languages'] as $lang => $spec) {
+ extract ($spec);
+ $xml .= " \n" .
+ " " .
+ ($name ? ' ' . XML_Util::replaceEntities($name) . ' ' : '') .
+ "\n" .
+ " " .
+ ($meta ? ' ' . XML_Util::replaceEntities($meta) . ' ' : "") .
+ "\n" .
+ " " .
+ ($error_text
+ ? ' ' . XML_Util::replaceEntities($error_text) . ' '
+ : "") .
+ "\n" .
+ " " . ($encoding ? " $encoding " : "") .
+ "\n" .
+ " \n";
+ }
+
+ $xml .= " \n" .
+ " \n";
+
+ foreach ($data['pages'] as $page => $strings) {
+ $xml .= " \n";
+ foreach ($strings as $str_id => $translations) {
+ $xml .= " \n";
+ foreach ($translations as $lang => $str) {
+ $xml .= " " .
+ XML_Util::replaceEntities($str) . "
\n";
+ }
+ $xml .= " \n";
+ }
+ $xml .= " \n";
+ }
+
+ $xml .= " \n" .
+ "\n";
+
+ unset ($data);
+
+ // Saving
+
+ if (!$f = fopen ($this->_filename, 'w')) {
+ return $this->raiseError(sprintf(
+ 'Unable to open the XML file ("%s") for writing',
+ $this->_filename
+ ),
+ TRANSLATION2_ERROR_CANNOT_WRITE_FILE,
+ PEAR_ERROR_TRIGGER,
+ E_USER_ERROR
+ );
+ }
+ @flock($f, LOCK_EX);
+ fwrite ($f, $xml);
+ //@flock($f, LOCK_UN);
+ fclose ($f);
+ return true;
+ }
+
+ // }}}
+}
+?>
\ No newline at end of file
diff --git a/Translation2/Admin/Decorator.php b/Translation2/Admin/Decorator.php
new file mode 100644
index 0000000..5a04433
--- /dev/null
+++ b/Translation2/Admin/Decorator.php
@@ -0,0 +1,221 @@
+
+ * @copyright 2004-2007 Lorenzo Alberton
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @version CVS: $Id$
+ * @link http://pear.php.net/package/Translation2
+ */
+
+/**
+ * Load Translation2_Decorator class
+ */
+require_once 'Translation2/Decorator.php';
+
+/**
+ * Decorates a Translation2_Admin class.
+ *
+ * Create a subclass of this class for your own "decoration".
+ *
+ * @category Internationalization
+ * @package Translation2
+ * @author Lorenzo Alberton
+ * @copyright 2004-2007 Lorenzo Alberton
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @link http://pear.php.net/package/Translation2
+ * @abstract
+ * @todo Don't allow stacking on top of regular Decorators,
+ * since that will break things.
+ */
+class Translation2_Admin_Decorator extends Translation2_Decorator
+{
+ // {{{ addLang()
+
+ /**
+ * Prepare the storage container for a new lang.
+ * If the langsAvail table doesn't exist yet, it is created.
+ *
+ * @param array $langData array('lang_id' => 'en',
+ * 'table_name' => 'i18n',
+ * 'name' => 'english',
+ * 'meta' => 'some meta info',
+ * 'error_text' => 'not available');
+ * @param array $options array('charset' => 'utf8',
+ * 'collation' => 'utf8_general_ci');
+ *
+ * @return mixed true on success, PEAR_Error on failure
+ * @see Translation2_Admin::addLang()
+ */
+ function addLang($langData, $options = array())
+ {
+ return $this->translation2->addLang($langData, $options);
+ }
+
+ // }}}
+ // {{{ removeLang()
+
+ /**
+ * Remove the lang from the langsAvail table and drop the strings table.
+ * If the strings table holds other langs and $force==false, then
+ * only the lang column is dropped. If $force==true the whole
+ * table is dropped without any check
+ *
+ * @param string $langID language ID
+ * @param boolean $force remove the language info without further checks
+ *
+ * @return mixed true on success, PEAR_Error on failure
+ * @see Translation2_Admin::removeLang()
+ */
+ function removeLang($langID = null, $force = false)
+ {
+ return $this->translation2->removeLang($langID, $force);
+ }
+
+ // }}}
+ // {{{ updateLang()
+
+ /**
+ * Update the lang info in the langsAvail table
+ *
+ * @param array $langData array containing language info
+ *
+ * @return mixed true on success, PEAR_Error on failure
+ * @see Translation2_Admin::updateLang()
+ */
+ function updateLang($langData)
+ {
+ return $this->translation2->updateLang($langData);
+ }
+
+ // }}}
+ // {{{ add()
+
+ /**
+ * Add a new translation
+ *
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ * @param array $stringArray Associative array with string translations.
+ * Sample format: array('en' => 'sample', 'it' => 'esempio')
+ *
+ * @return mixed true on success, PEAR_Error on failure
+ * @see Translation2_Admin::add()
+ */
+ function add($stringID, $pageID, $stringArray)
+ {
+ return $this->translation2->add($stringID, $pageID, $stringArray);
+ }
+
+ // }}}
+ // {{{ update()
+
+ /**
+ * Update an existing translation
+ *
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ * @param array $stringArray Associative array with string translations.
+ * Sample format: array('en' => 'sample', 'it' => 'esempio')
+ *
+ * @return mixed true on success, PEAR_Error on failure
+ * @see Translation2_Admin::update()
+ */
+ function update($stringID, $pageID, $stringArray)
+ {
+ return $this->translation2->update($stringID, $pageID, $stringArray);
+ }
+
+ // }}}
+ // {{{ remove()
+
+ /**
+ * Remove a translated string
+ *
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ *
+ * @return mixed true on success, PEAR_Error on failure
+ * @see Translation2_Admin::remove()
+ */
+ function remove($stringID, $pageID = null)
+ {
+ return $this->translation2->remove($stringID, $pageID);
+ }
+
+ // }}}
+ // {{{ removePage
+
+ /**
+ * Remove all the strings in the given page/group
+ *
+ * @param string $pageID page/group ID
+ *
+ * @return mixed true on success, PEAR_Error on failure
+ * @see Translation2_Admin::removePage()
+ */
+ function removePage($pageID = null)
+ {
+ return $this->translation2->removePager($pageID);
+ }
+
+ // }}}
+ // {{{ getPageNames()
+
+ /**
+ * Get a list of all the pageIDs in any table.
+ *
+ * @return array
+ * @see Translation2_Admin::getPageNames()
+ */
+ function getPageNames()
+ {
+ return $this->translation2->getPageNames();
+ }
+
+ // }}}
+ // {{{ cleanCache()
+
+ /**
+ * If you use the CacheLiteFunction decorator, you may want to invalidate
+ * the cache after a change in the data base.
+ *
+ * @return void
+ * @see Translation2_Admin::cleanCache()
+ */
+ function cleanCache()
+ {
+ return $this->translation2->cleanCache();
+ }
+
+ // }}}
+}
+?>
\ No newline at end of file
diff --git a/Translation2/Admin/Decorator/Autoadd.php b/Translation2/Admin/Decorator/Autoadd.php
new file mode 100644
index 0000000..4c8e2a5
--- /dev/null
+++ b/Translation2/Admin/Decorator/Autoadd.php
@@ -0,0 +1,112 @@
+
+ * @author Ian Eure
+ * @copyright 2004-2007 Lorenzo Alberton, Ian Eure
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @version CVS: $Id$
+ * @link http://pear.php.net/package/Translation2
+ */
+
+/**
+ * Load Translation2_Decorator class
+ */
+require_once 'Translation2/Admin/Decorator.php';
+
+/**
+ * Automatically add requested strings
+ *
+ * This Decorator will add strings to a language when a request for them to be
+ * translated happens. The 'autoaddlang' option must be set to the language the
+ * strings will be added as.
+ *
+ * Example:
+ *
+ * $tr =& Translation2_Admin::factory(...);
+ * $tr->setLang('en');
+ * $tr =& $tr->getAdminDecorator('Autoadd');
+ * $tr->setOption('autoaddlang', 'en');
+ * ...
+ * $tr->get('Entirely new string', 'samplePage', 'de');
+ *
+ *
+ * 'Entirely new string' will be added to the English language table.
+ *
+ * @category Internationalization
+ * @package Translation2
+ * @author Ian Eure
+ * @copyright 2004-2007 Ian Eure
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @link http://pear.php.net/package/Translation2
+ * @since 2.0.0beta3
+ */
+class Translation2_Admin_Decorator_Autoadd extends Translation2_Admin_Decorator
+{
+ /**
+ * Language to add strings in
+ *
+ * @var string
+ */
+ var $autoaddlang = '';
+
+ /**
+ * Get a translated string
+ *
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ * @param string $langID language ID
+ *
+ * @return string
+ * @see Translation2::get()
+ */
+ function get($stringID, $pageID = TRANSLATION2_DEFAULT_PAGEID, $langID = null)
+ {
+ $pageID = ($pageID == TRANSLATION2_DEFAULT_PAGEID ? $this->translation2->currentPageID : $pageID);
+ $string = $this->translation2->get($stringID, $pageID, $langID);
+ if (PEAR::isError($string)
+ || !strlen($string)
+ && !empty($this->autoaddlang)
+ && $langID == $this->autoaddlang) {
+ // Make sure we add a stub for all languages we know about.
+ $langs = array();
+ foreach ($this->translation2->getLangs('ids') as $lang) {
+ $langs[$lang] = '';
+ }
+ $langs[$this->autoaddlang] = $stringID;
+
+ // Add the string
+ $this->translation2->add($stringID, $pageID, $langs);
+ }
+ return $string;
+ }
+}
+?>
\ No newline at end of file
diff --git a/Container.php b/Translation2/Container.php
similarity index 100%
rename from Container.php
rename to Translation2/Container.php
diff --git a/Translation2/Container/dataobjectsimple.php b/Translation2/Container/dataobjectsimple.php
new file mode 100644
index 0000000..d1bac9f
--- /dev/null
+++ b/Translation2/Container/dataobjectsimple.php
@@ -0,0 +1,255 @@
+
+ * @copyright 2004-2008 Alan Knowles
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @version CVS: $Id$
+ * @link http://pear.php.net/package/Translation2
+ */
+
+/**
+ * require Translation2_Container class and DB_DataObjects
+ */
+require_once 'Translation2/Container.php';
+require_once 'DB/DataObject.php';
+
+/**
+ * Simple storage driver for fetching data from a db with DB_DataObject
+ *
+ * This storage driver can use all databases which are supported
+ * by the PEAR::DB abstraction layer to fetch data.
+ *
+ * Database Structure:
+ *
+ * // meta data etc. not supported yet...
+ *
+ * create table translations (
+ * id int(11) auto_increment not null primary key,
+ * string_id int(11),
+ * page varchar(128),
+ * lang varchar(10),
+ * translation text
+ * );
+ * alter table translations add index page (page);
+ * alter table translations add index lang (lang);
+ * alter table translations add index string_id (string_id);
+ *
+ *
+ * - then just run the dataobjects createtables script.
+ *
+ * @category Internationalization
+ * @package Translation2
+ * @author Alan Knowles
+ * @copyright 2004-2008 Alan Knowles
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @version CVS: $Id$
+ * @link http://pear.php.net/package/Translation2
+ */
+class Translation2_Container_dataobjectsimple extends Translation2_Container
+{
+ // {{{ init
+
+ /**
+ * Initialize the container
+ *
+ * @param string $table table name
+ *
+ * @return boolean true
+ */
+ function init($table = null)
+ {
+ $this->_setDefaultOptions();
+ if (!empty($table)) {
+ $this->options['table'] = $table;
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ _setDefaultOptions()
+
+ /**
+ * Set some default options
+ *
+ * @return void
+ * @access private
+ */
+ function _setDefaultOptions()
+ {
+ $this->options['table'] = 'translations';
+ }
+
+ // }}}
+ // {{{ fetchLangs()
+
+ /**
+ * Fetch the available langs if they're not cached yet.
+ *
+ * @return void
+ */
+ function fetchLangs()
+ {
+ $do = DB_DataObject::factory($this->options['table']);
+ $do->selectAdd();
+ $do->selectAdd('distinct lang');
+ $do->find();
+
+ $ret = array();
+ while ($do->fetch()) {
+ $l = $do->lang;
+ $ret[$l] = array(
+ 'id' => $l,
+ 'name' => $l,
+ 'meta' => '',
+ 'error_text' => '',
+ );
+ }
+ $this->langs = $ret;
+ }
+
+ // }}}
+ // {{{ getPage()
+
+ /**
+ * Returns an array of the strings in the selected page
+ *
+ * @param string $pageID page/group ID
+ * @param string $langID language ID
+ *
+ * @return array
+ */
+ function getPage($pageID = null, $langID = null)
+ {
+ $langID = $this->_getLangID($langID);
+ if (PEAR::isError($langID)) {
+ return $langID;
+ }
+
+ // First get the array of string IDs
+ $do = DB_DataObject::factory($this->options['table']);
+ $do->lang = '-';
+ $do->page = $pageID;
+ $do->find();
+
+ $stringIDs = array();
+ while ($do->fetch()) {
+ $stringIDs[$do->string_id] = $do->translation;
+ }
+
+ // Now get the array of strings
+ $do = DB_DataObject::factory($this->options['table']);
+ $do->page = $pageID;
+ $do->lang = $langID;
+
+ $do->find();
+ $translations = array();
+ while ($do->fetch()) {
+ $translations[$do->string_id] = $do->translation;
+ }
+
+ // Construct an associative array of stringIDs and translations
+ $strings = array();
+ foreach ($translations as $key => $value) {
+ $strings[$stringIDs[$key]] = $value;
+ }
+
+ return $strings;
+ }
+
+ // }}}
+ // {{{ getOne()
+
+ /**
+ * Get a single item from the container, without caching the whole page
+ *
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ * @param string $langID language ID
+ *
+ * @return string
+ */
+ function getOne($string, $pageID = null, $langID = null)
+ {
+ $langID = $langID ? $langID : (isset($this->currentLang['id']) ? $this->currentLang['id'] : '-');
+ // get the string id
+ $do = DB_DataObject::factory($this->options['table']);
+ $do->lang = '-';
+ $do->page = $pageID;
+ $do->translation = $string;
+ // we dont have the base language translation..
+ if (!$do->find(true)) {
+ return '';
+ }
+ $stringID = $do->string_id;
+
+ $do = DB_DataObject::factory($this->options['table']);
+ $do->lang = $langID;
+ $do->page = $pageID;
+ $do->string_id = $stringID;
+ //print_r($do);
+ $do->selectAdd();
+ $do->selectAdd('translation');
+ if (!$do->find(true)) {
+ return '';
+ }
+ return $do->translation;
+
+ }
+
+ // }}}
+ // {{{ getStringID()
+
+ /**
+ * Get the stringID for the given string
+ *
+ * @param string $string string
+ * @param string $pageID page/group ID
+ *
+ * @return string
+ */
+ function getStringID($string, $pageID = null)
+ {
+ // get the english version...
+
+ $do = DB_DataObject::factory($this->options['table']);
+ $do->lang = $this->currentLang['id'];
+ $do->page = $pageID;
+ $do->translation = $string;
+ if ($do->find(true)) {
+ return '';
+ }
+ return $do->string_id;
+ }
+
+ // }}}
+}
+?>
\ No newline at end of file
diff --git a/Translation2/Container/db.php b/Translation2/Container/db.php
new file mode 100644
index 0000000..8143023
--- /dev/null
+++ b/Translation2/Container/db.php
@@ -0,0 +1,359 @@
+
+ * @author Ian Eure
+ * @copyright 2004-2008 Lorenzo Alberton, Ian Eure
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @version CVS: $Id$
+ * @link http://pear.php.net/package/Translation2
+ */
+
+/**
+ * require Translation2_Container class
+ */
+require_once 'Translation2/Container.php';
+
+/**
+ * Storage driver for fetching data from a database
+ *
+ * This storage driver can use all databases which are supported
+ * by the PEAR::DB abstraction layer to fetch data.
+ *
+ * @category Internationalization
+ * @package Translation2
+ * @author Lorenzo Alberton
+ * @author Ian Eure
+ * @copyright 2004-2008 Lorenzo Alberton, Ian Eure
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @version CVS: $Id$
+ * @link http://pear.php.net/package/Translation2
+ */
+class Translation2_Container_db extends Translation2_Container
+{
+ // {{{ class vars
+
+ /**
+ * DB object
+ * @var object
+ */
+ var $db = null;
+
+ /**
+ * query counter
+ * @var integer
+ * @access private
+ */
+ var $_queries = 0;
+
+ // }}}
+ // {{{ init
+
+ /**
+ * Initialize the container
+ *
+ * @param mixed &$db string DSN or object DB instance
+ *
+ * @return boolean|PEAR_Error object if something went wrong
+ */
+ function init(&$db)
+ {
+ $this->_setDefaultOptions();
+ if (PEAR::isError($err = $this->_connect($db))) {
+ return $err;
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ _connect()
+
+ /**
+ * Connect to database by using the given DSN string
+ *
+ * @param mixed &$db string DSN or object DB instance
+ *
+ * @return boolean|PEAR_Error object if something went wrong
+ * @access private
+ */
+ function _connect(&$db)
+ {
+ if (is_object($db) && is_a($db, 'DB_Common')) {
+ // Passed an existing instance
+ $this->db =& $db;
+ } else if (is_string($db) || is_array($db)) {
+ // Passed a DSN
+ include_once 'DB.php';
+ $this->db =& DB::connect($db);
+ } else {
+ // Passed something invalid
+ return PEAR::raiseError('The given dsn was not valid in file '
+ . __FILE__ . ' at line ' . __LINE__,
+ TRANSLATION2_ERROR_CANNOT_CONNECT,
+ PEAR_ERROR_RETURN);
+ }
+
+ if (PEAR::isError($this->db)) {
+ return $this->db;
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ _setDefaultOptions()
+
+ /**
+ * Set some default options
+ *
+ * @access private
+ * @return void
+ */
+ function _setDefaultOptions()
+ {
+ $this->options['langs_avail_table'] = 'langs';
+ $this->options['lang_id_col'] = 'id';
+ $this->options['lang_name_col'] = 'name';
+ $this->options['lang_meta_col'] = 'meta';
+ $this->options['lang_errmsg_col'] = 'error_text';
+ $this->options['lang_encoding_col'] = 'encoding';
+
+ $this->options['strings_default_table'] = 'i18n';
+ $this->options['strings_tables'] = array(); // 'lang_id' => 'table_name'
+ $this->options['string_id_col'] = 'id';
+ $this->options['string_page_id_col'] = 'page_id';
+ $this->options['string_page_id_col_length'] = 50;
+ $this->options['string_text_col'] = '%s'; // col_name if one table per lang is used,
+ // or a pattern (i.e. "tr_%s" => "tr_EN_US")
+ }
+
+ // }}}
+ // {{{ setCharset()
+
+ /**
+ * Set charset used to read/store the translations
+ *
+ * @param string $charset character set (encoding)
+ *
+ * @return PEAR_Error on failure
+ */
+ function setCharset($charset)
+ {
+ if (in_array('setcharset', array_map('strtolower', get_class_methods($this->db)))) {
+ return $this->db->setCharset($charset);
+ }
+ return $this->db->query('SET NAMES ' .$this->db->quoteSmart($charset));
+ }
+
+ // }}}
+ // {{{ fetchLangs()
+
+ /**
+ * Fetch the available langs if they're not cached yet.
+ *
+ * @return PEAR_Error on failure
+ */
+ function fetchLangs()
+ {
+ $query = sprintf('SELECT %s AS id, %s AS name, %s AS meta, %s AS error_text, %s AS encoding FROM %s',
+ $this->db->quoteIdentifier($this->options['lang_id_col']),
+ $this->db->quoteIdentifier($this->options['lang_name_col']),
+ $this->db->quoteIdentifier($this->options['lang_meta_col']),
+ $this->db->quoteIdentifier($this->options['lang_errmsg_col']),
+ $this->db->quoteIdentifier($this->options['lang_encoding_col']),
+ $this->db->quoteIdentifier($this->options['langs_avail_table'])
+ );
+
+ ++$this->_queries;
+ $res = $this->db->getAll($query, DB_FETCHMODE_ASSOC);
+ if (PEAR::isError($res)) {
+ return $res;
+ }
+ foreach ($res as $row) {
+ $row = array_change_key_case($row, CASE_LOWER);
+ $this->langs[$row['id']] = $row;
+ }
+ }
+
+ // }}}
+ // {{{ getPage()
+
+ /**
+ * Returns an array of the strings in the selected page
+ *
+ * @param string $pageID page/group ID
+ * @param string $langID language ID
+ *
+ * @return array
+ */
+ function getPage($pageID = null, $langID = null)
+ {
+ $langID = $this->_getLangID($langID);
+ if (PEAR::isError($langID)) {
+ return $langID;
+ }
+ $lang_col = $this->_getLangCol($langID);
+ $table = $this->_getLangTable($langID);
+
+ $query = sprintf('SELECT %s, %s FROM %s WHERE %s ',
+ $this->db->quoteIdentifier($this->options['string_id_col']),
+ $this->db->quoteIdentifier($lang_col),
+ $this->db->quoteIdentifier($table),
+ $this->db->quoteIdentifier($this->options['string_page_id_col'])
+ );
+
+ if (is_null($pageID)) {
+ $query .= 'IS NULL';
+ } else {
+ $query .= ' = ' . $this->db->quote($pageID);
+ }
+
+ ++$this->_queries;
+ $res = $this->db->getAssoc($query);
+ return $res;
+ }
+
+ // }}}
+ // {{{ getOne()
+
+ /**
+ * Get a single item from the container
+ *
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ * @param string $langID language ID
+ *
+ * @return string
+ */
+ function getOne($stringID, $pageID = null, $langID = null)
+ {
+ $langID = $this->_getLangID($langID);
+ if (PEAR::isError($langID)) {
+ return $langID;
+ }
+ $lang_col = $this->_getLangCol($langID);
+ $table = $this->_getLangTable($langID);
+
+ $query = sprintf('SELECT %s FROM %s WHERE %s = %s AND %s',
+ $this->db->quoteIdentifier($lang_col),
+ $this->db->quoteIdentifier($table),
+ $this->db->quoteIdentifier($this->options['string_id_col']),
+ $this->db->quote($stringID),
+ $this->db->quoteIdentifier($this->options['string_page_id_col'])
+ );
+
+ if (is_null($pageID)) {
+ $query .= ' IS NULL';
+ } else {
+ $query .= ' = ' . $this->db->quote($pageID);
+ }
+
+ ++$this->_queries;
+ return $this->db->getOne($query);
+ }
+
+ // }}}
+ // {{{ getStringID()
+
+ /**
+ * Get the stringID for the given string
+ *
+ * @param string $string string
+ * @param string $pageID page/group ID
+ *
+ * @return string
+ */
+ function getStringID($string, $pageID = null)
+ {
+ $lang_col = $this->_getLangCol($this->currentLang['id']);
+ $table = $this->_getLangTable($this->currentLang['id']);
+ $query = sprintf('SELECT %s FROM %s WHERE %s = %s AND %s',
+ $this->db->quoteIdentifier($this->options['string_id_col']),
+ $this->db->quoteIdentifier($table),
+ $this->db->quoteIdentifier($lang_col),
+ $this->db->quote($string),
+ $this->db->quoteIdentifier($this->options['string_page_id_col'])
+ );
+ if (is_null($pageID)) {
+ $query .= ' IS NULL';
+ } else {
+ $query .= ' = ' . $this->db->quote($pageID);
+ }
+ ++$this->_queries;
+ return $this->db->getOne($query);
+ }
+
+ // }}}
+ // {{{ _getLangTable()
+
+ /**
+ * Get the table a language is stored in
+ *
+ * @param string $langID language ID
+ *
+ * @return string table $langID is stored in
+ * @access private
+ */
+ function _getLangTable($langID)
+ {
+ if (isset($this->options['strings_tables'][$langID])) {
+ return $this->options['strings_tables'][$langID];
+ }
+ return str_replace('%s', $langID, $this->options['strings_default_table']);
+ }
+
+ // }}}
+ // {{{ _getLangCol()
+
+ /**
+ * Get the column a language's string is stored in
+ *
+ * @param string $langID Language
+ *
+ * @return string column $langID is stored in
+ * @access private
+ */
+ function _getLangCol($langID)
+ {
+ static $cols;
+ if (!isset($cols[$langID])) {
+ if (isset($this->options['string_text_col']) &&
+ !empty($this->options['string_text_col'])) {
+ $cols[$langID] = str_replace('%s', $langID, $this->options['string_text_col']);
+ } else {
+ $cols[$langID] = $langID;
+ }
+ }
+ return $cols[$langID];
+ }
+
+ // }}}
+}
+?>
\ No newline at end of file
diff --git a/Translation2/Container/gettext.php b/Translation2/Container/gettext.php
new file mode 100644
index 0000000..e39230b
--- /dev/null
+++ b/Translation2/Container/gettext.php
@@ -0,0 +1,351 @@
+
+ * @author Michael Wallner
+ * @copyright 2004-2008 Lorenzo Alberton, Michael Wallner
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @version CVS: $Id$
+ * @link http://pear.php.net/package/Translation2
+ */
+
+/**
+ * require Translation2_Container class
+ */
+require_once 'Translation2/Container.php';
+
+/**
+ * require I18Nv2 for locale handling
+ */
+require_once 'I18Nv2.php';
+
+/**
+ * Storage driver for fetching data with gettext
+ *
+ * This storage driver requires the gettext extension
+ * and the PEAR::I18Nv2 class for locale handling
+ *
+ * @category Internationalization
+ * @package Translation2
+ * @author Lorenzo Alberton
+ * @author Michael Wallner
+ * @copyright 2004-2008 Lorenzo Alberton, Michael Wallner
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @version CVS: $Id$
+ * @link http://pear.php.net/package/Translation2
+ * @see /docs/gettext_readme.txt for an usage example
+ */
+class Translation2_Container_gettext extends Translation2_Container
+{
+ // {{{ class vars
+
+ /**
+ * domain bindings
+ * @var array
+ * @access private
+ */
+ var $_domains = array();
+
+ /**
+ * @var array
+ * @access private
+ */
+ var $cachedDomains = array();
+
+ /**
+ * @var boolean
+ * @access private
+ */
+ var $_native = false;
+
+ // }}}
+ // {{{ init
+
+ /**
+ * Initialize the container
+ *
+ * @param array $options gettext parameters
+ *
+ * @return boolean|PEAR_Error object if domains INI file doesn't exist
+ */
+ function init($options)
+ {
+ $this->_setDefaultOptions();
+ $this->_parseOptions($options);
+ $this->_native = (
+ function_exists('gettext') &&
+ (strtolower($this->options['file_type']) == 'mo') &&
+ !$this->options['blank_on_missing']
+ );
+
+ $this->_domains = @parse_ini_file($this->options['domains_path_file']);
+
+ if (!$this->_domains) {
+ return $this->raiseError(sprintf(
+ 'Cannot find domains INI file "%s" [%s on line %d]',
+ $this->options['domains_path_file'], __FILE__, __LINE__
+ ),
+ TRANSLATION2_ERROR_CANNOT_FIND_FILE
+ );
+ }
+
+ if ($this->_native) {
+ foreach ((array) $this->_domains as $domain => $path) {
+ bindtextdomain($domain, $path);
+ }
+ textdomain($this->options['default_domain']);
+ }
+ $this->setLang($this->options['default_lang']);
+
+ return true;
+ }
+
+ // }}}
+ // {{{ _setDefaultOptions()
+
+ /**
+ * Set some default options
+ *
+ * @access private
+ * @return void
+ */
+ function _setDefaultOptions()
+ {
+ $this->options['langs_avail_file'] = 'langs.ini';
+ $this->options['domains_path_file'] = 'domains.ini';
+ $this->options['default_domain'] = 'messages';
+ $this->options['carriage_return'] = "\n";
+ $this->options['file_type'] = 'mo';
+ $this->options['default_lang'] = 'en';
+ $this->options['default_encoding'] = 'iso-8859-1';
+ $this->options['blank_on_missing'] = false;
+ }
+
+ // }}}
+ // {{{ _switchLang()
+
+ /**
+ * Set the given langID
+ *
+ * @param string $langID new langID
+ *
+ * @return string previous langID
+ * @access private
+ */
+ function _switchLang($langID)
+ {
+ $langID = $this->_getLangID($langID);
+ $oldLang = $this->currentLang['id'];
+ $this->setLang($langID);
+ return $oldLang;
+ }
+
+ // }}}
+ // {{{ fetchLangs()
+
+ /**
+ * Fetch the available langs if they're not cached yet.
+ *
+ * @return void
+ */
+ function fetchLangs()
+ {
+ $this->langs = @parse_ini_file($this->options['langs_avail_file'], true);
+ foreach ((array) $this->langs as $id => $lang) {
+ $this->langs[$id]['id'] = $id;
+ }
+ }
+
+ // }}}
+ // {{{ setLang()
+
+ /**
+ * Sets the current lang
+ *
+ * @param string $langID language ID
+ *
+ * @return array language data
+ */
+ function setLang($langID)
+ {
+ if (!PEAR::isError($langData = parent::setLang($langID))) {
+ I18Nv2::setLocale($langID);
+ }
+ return $langData;
+ }
+
+ // }}}
+ // {{{ getPage()
+
+ /**
+ * Get all the strings from a domain (parsing the .mo file)
+ *
+ * @param string $pageID page/group ID
+ * @param string $langID language ID
+ *
+ * @return array|PEAR_Error
+ */
+ function getPage($pageID = null, $langID = null)
+ {
+ $oldLang = $this->_switchLang($langID);
+ $curLang = $this->currentLang['id'];
+
+ if (empty($pageID) || $pageID == TRANSLATION2_DEFAULT_PAGEID) {
+ $pageID = $this->options['default_domain'];
+ }
+
+ if (isset($this->cachedDomains[$curLang][$pageID])) {
+ $this->_switchLang($oldLang);
+ return $this->cachedDomains[$curLang][$pageID];
+ }
+
+ if (!isset($this->_domains[$pageID])) {
+ $this->_switchLang($oldLang);
+ return $this->raiseError(sprintf(
+ 'The domain "%s" was not specified in the domains INI '.
+ 'file "%s" [%s on line %d]', $pageID,
+ $this->options['domains_path_file'], __FILE__, __LINE__
+ ),
+ TRANSLATION2_ERROR_DOMAIN_NOT_SET
+ );
+ }
+
+ include_once 'File/Gettext.php';
+ $gtFile = &File_Gettext::factory($this->options['file_type']);
+
+ $path = $this->_domains[$pageID] .'/'. $curLang .'/LC_MESSAGES/';
+ $file = $path . $pageID .'.'. $this->options['file_type'];
+
+ if (PEAR::isError($e = $gtFile->load($file))) {
+ if (is_file($file)) {
+ $this->_switchLang($oldLang);
+ return $this->raiseError(sprintf(
+ '%s [%s on line %d]', $e->getMessage(), __FILE__, __LINE__
+ ),
+ TRANSLATION2_ERROR, PEAR_ERROR_RETURN
+ );
+ }
+ $this->_switchLang($oldLang);
+ return $this->raiseError(sprintf(
+ 'Cannot find file "%s" [%s on line %d]',
+ $file, __FILE__, __LINE__
+ ),
+ TRANSLATION2_ERROR_CANNOT_FIND_FILE, PEAR_ERROR_RETURN
+ );
+ }
+
+ $this->cachedDomains[$curLang][$pageID] = $gtFile->strings;
+ $this->_switchLang($oldLang);
+ return $gtFile->strings;
+ }
+
+ // }}}
+ // {{{ getOne()
+
+ /**
+ * Get a single item from the container, without caching the whole page
+ *
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ * @param string $langID language ID
+ *
+ * @return string
+ */
+ function getOne($stringID, $pageID = null, $langID = null)
+ {
+ // native mode
+ if ($this->_native) {
+ $oldLang = $this->_switchLang($langID);
+ $curLang = $this->currentLang['id'];
+
+ if (empty($pageID) || $pageID == TRANSLATION2_DEFAULT_PAGEID) {
+ $pageID = $this->options['default_domain'];
+ }
+
+ $string = dgettext($pageID, $stringID);
+
+ $this->_switchLang($oldLang);
+ return $string;
+ }
+
+ // use File_Gettext
+ $page = $this->getPage($pageID, $langID);
+ if (PEAR::isError($page = $this->getPage($pageID, $langID))) {
+ if ($page->getCode() == TRANSLATION2_ERROR_CANNOT_FIND_FILE) {
+ $page = array();
+ } else {
+ return $this->raiseError($page->getMessage(), $page->getCode());
+ }
+ }
+
+ // return original string if there's no translation available
+ if (isset($page[$stringID]) && strlen($page[$stringID])) {
+ return $page[$stringID];
+ } else if (false == $this->options['blank_on_missing']) {
+ return $stringID;
+ } else {
+ return '';
+ }
+ }
+
+ // }}}
+ // {{{ getStringID()
+
+ /**
+ * Get the stringID for the given string
+ *
+ * @param string $string string
+ * @param string $pageID page/group ID
+ *
+ * @return string|PEAR_Error
+ */
+ function getStringID($string, $pageID = null)
+ {
+ if (empty($pageID) || $pageID == TRANSLATION2_DEFAULT_PAGEID) {
+ $pageID = $this->options['default_domain'];
+ }
+
+ if (!array_key_exists($pageID, $this->_domains)) {
+ return $this->raiseError(sprintf(
+ 'The domain "%s" was not specified in the domains '.
+ 'INI file "%s" [%s on line %d]', $pageID,
+ $this->options['domains_path_file'], __FILE__, __LINE__
+ ),
+ TRANSLATION2_ERROR_DOMAIN_NOT_SET
+ );
+ }
+
+ return array_search($string, $this->getPage($pageID));
+ }
+
+ // }}}
+}
+?>
\ No newline at end of file
diff --git a/Translation2/Container/mdb.php b/Translation2/Container/mdb.php
new file mode 100644
index 0000000..26c77ec
--- /dev/null
+++ b/Translation2/Container/mdb.php
@@ -0,0 +1,339 @@
+
+ * @copyright 2004-2008 Lorenzo Alberton
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @version CVS: $Id$
+ * @link http://pear.php.net/package/Translation2
+ */
+
+/**
+ * require Translation2_Container class
+ */
+require_once 'Translation2/Container.php';
+
+/**
+ * Storage driver for fetching data from a database
+ *
+ * This storage driver can use all databases which are supported
+ * by the PEAR::MDB abstraction layer to fetch data.
+ *
+ * @category Internationalization
+ * @package Translation2
+ * @author Lorenzo Alberton
+ * @copyright 2004-2008 Lorenzo Alberton
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @version CVS: $Id$
+ * @link http://pear.php.net/package/Translation2
+ */
+class Translation2_Container_mdb extends Translation2_Container
+{
+
+ // {{{ class vars
+
+ /**
+ * MDB object
+ * @var object
+ */
+ var $db = null;
+
+ /**
+ * query counter
+ * @var integer
+ * @access private
+ */
+ var $_queries = 0;
+
+ // }}}
+ // {{{ init
+
+ /**
+ * Initialize the container
+ *
+ * @param string &$db Connection data or MDB object
+ *
+ * @return boolean|PEAR_Error object if something went wrong
+ */
+ function init(&$db)
+ {
+ $this->_setDefaultOptions();
+ if (PEAR::isError($err = $this->_connect($db))) {
+ return $err;
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ _connect()
+
+ /**
+ * Connect to database by using the given DSN string
+ *
+ * @param mixed &$db DSN string | array | mdb object
+ *
+ * @return boolean|PEAR_Error on error
+ * @access private
+ */
+ function _connect(&$db)
+ {
+ if (is_object($db) && is_a($db, 'MDB_Common')) {
+ $this->db = &$db;
+ } elseif (is_string($db) || is_array($db)) {
+ include_once 'MDB.php';
+ $this->db =& MDB::connect($db);
+ } elseif (is_object($db) && MDB::isError($db)) {
+ return PEAR::raiseError($db->getMessage(), $db->code);
+ } else {
+ return PEAR::raiseError('The given dsn was not valid in file '
+ . __FILE__ . ' at line ' . __LINE__,
+ TRANSLATION2_ERROR_CANNOT_CONNECT,
+ PEAR_ERROR_RETURN);
+ }
+
+ if (PEAR::isError($this->db)) {
+ return $this->db;
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ _setDefaultOptions()
+
+ /**
+ * Set some default options
+ *
+ * @access private
+ * @return void
+ */
+ function _setDefaultOptions()
+ {
+ $this->options['langs_avail_table'] = 'langs';
+ $this->options['lang_id_col'] = 'id';
+ $this->options['lang_name_col'] = 'name';
+ $this->options['lang_meta_col'] = 'meta';
+ $this->options['lang_errmsg_col'] = 'error_text';
+ $this->options['lang_encoding_col'] = 'encoding';
+
+ $this->options['strings_default_table'] = 'i18n';
+ $this->options['strings_tables'] = array(); // 'lang_id' => 'table_name'
+ $this->options['string_id_col'] = 'id';
+ $this->options['string_page_id_col'] = 'page_id';
+ $this->options['string_page_id_col_length'] = 50;
+ $this->options['string_text_col'] = '%s'; // col_name if one table per lang is used,
+ // or a pattern (i.e. "tr_%s" => "tr_EN_US")
+ }
+
+ // }}}
+ // {{{ fetchLangs()
+
+ /**
+ * Fetch the available langs if they're not cached yet.
+ *
+ * @return PEAR_Error on error
+ */
+ function fetchLangs()
+ {
+ $query = sprintf('SELECT %s AS id, %s AS name, %s AS meta, %s AS error_text, %s AS encoding FROM %s',
+ $this->db->quoteIdentifier($this->options['lang_id_col']),
+ $this->db->quoteIdentifier($this->options['lang_name_col']),
+ $this->db->quoteIdentifier($this->options['lang_meta_col']),
+ $this->db->quoteIdentifier($this->options['lang_errmsg_col']),
+ $this->db->quoteIdentifier($this->options['lang_encoding_col']),
+ $this->db->quoteIdentifier($this->options['langs_avail_table'])
+ );
+
+ ++$this->_queries;
+ $res = $this->db->getAll($query, null, array(), null, MDB_FETCHMODE_ASSOC);
+ if (PEAR::isError($res)) {
+ return $res;
+ }
+ foreach ($res as $row) {
+ $row = array_change_key_case($row, CASE_LOWER);
+ $this->langs[$row['id']] = $row;
+ }
+ }
+
+ // }}}
+ // {{{ getPage()
+
+ /**
+ * Returns an array of the strings in the selected page
+ *
+ * @param string $pageID page/group ID
+ * @param string $langID language ID
+ *
+ * @return array|PEAR_Error on error
+ */
+ function getPage($pageID = null, $langID = null)
+ {
+ $langID = $this->_getLangID($langID);
+ if (PEAR::isError($langID)) {
+ return $langID;
+ }
+ $lang_col = $this->_getLangCol($langID);
+ $table = $this->_getLangTable($langID);
+
+ $query = sprintf('SELECT %s, %s FROM %s WHERE %s ',
+ $this->db->quoteIdentifier($this->options['string_id_col']),
+ $this->db->quoteIdentifier($lang_col),
+ $this->db->quoteIdentifier($table),
+ $this->db->quoteIdentifier($this->options['string_page_id_col'])
+ );
+
+ if (is_null($pageID)) {
+ $query .= 'IS NULL';
+ } else {
+ $query .= ' = ' . $this->db->getTextValue($pageID);
+ }
+
+ ++$this->_queries;
+ $res = $this->db->getAssoc($query);
+ return $res;
+ }
+
+ // }}}
+ // {{{ getOne()
+
+ /**
+ * Get a single item from the container
+ *
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ * @param string $langID language ID
+ *
+ * @return string
+ */
+ function getOne($stringID, $pageID = null, $langID = null)
+ {
+ $langID = $this->_getLangID($langID);
+ if (PEAR::isError($langID)) {
+ return $langID;
+ }
+ $lang_col = $this->_getLangCol($langID);
+ $table = $this->_getLangTable($langID);
+
+ $query = sprintf('SELECT %s FROM %s WHERE %s = %s AND %s',
+ $this->db->quoteIdentifier($lang_col),
+ $this->db->quoteIdentifier($table),
+ $this->db->quoteIdentifier($this->options['string_id_col']),
+ $this->db->getTextValue($stringID),
+ $this->db->quoteIdentifier($this->options['string_page_id_col'])
+ );
+
+ if (is_null($pageID)) {
+ $query .= ' IS NULL';
+ } else {
+ $query .= ' = ' . $this->db->getTextValue($pageID);
+ }
+
+ ++$this->_queries;
+ return $this->db->getOne($query);
+ }
+
+ // }}}
+ // {{{ getStringID()
+
+ /**
+ * Get the stringID for the given string
+ *
+ * @param string $string string
+ * @param string $pageID page/group ID
+ *
+ * @return string
+ */
+ function getStringID($string, $pageID = null)
+ {
+ $lang_col = $this->_getLangCol($this->currentLang['id']);
+ $table = $this->_getLangTable($this->currentLang['id']);
+ $query = sprintf('SELECT %s FROM %s WHERE %s = %s AND %s',
+ $this->db->quoteIdentifier($this->options['string_id_col']),
+ $this->db->quoteIdentifier($table),
+ $this->db->quoteIdentifier($lang_col),
+ $this->db->getTextValue($string),
+ $this->db->quoteIdentifier($this->options['string_page_id_col'])
+ );
+ if (is_null($pageID)) {
+ $query .= ' IS NULL';
+ } else {
+ $query .= ' = ' . $this->db->getTextValue($pageID);
+ }
+ ++$this->_queries;
+ return $this->db->getOne($query);
+ }
+
+ // }}}
+ // {{{ _getLangTable()
+
+ /**
+ * Get the table a language is stored in
+ *
+ * @param string $langID language ID
+ *
+ * @return string table $langID is stored in
+ * @access private
+ */
+ function _getLangTable($langID)
+ {
+ if (isset($this->options['strings_tables'][$langID])) {
+ return $this->options['strings_tables'][$langID];
+ }
+ return str_replace('%s', $langID, $this->options['strings_default_table']);
+ }
+
+ // }}}
+ // {{{ _getLangCol()
+
+ /**
+ * Get the column a language's string is stored in
+ *
+ * @param string $langID language ID
+ *
+ * @return string column $langID is stored in
+ * @access private
+ */
+ function _getLangCol($langID)
+ {
+ static $cols;
+ if (!isset($cols[$langID])) {
+ if (isset($this->options['string_text_col']) &&
+ !empty($this->options['string_text_col'])) {
+ $cols[$langID] = str_replace('%s', $langID, $this->options['string_text_col']);
+ } else {
+ $cols[$langID] = $langID;
+ }
+ }
+ return $cols[$langID];
+ }
+
+ // }}}
+}
+?>
\ No newline at end of file
diff --git a/Translation2/Container/mdb2.php b/Translation2/Container/mdb2.php
new file mode 100644
index 0000000..69303d2
--- /dev/null
+++ b/Translation2/Container/mdb2.php
@@ -0,0 +1,362 @@
+
+ * @copyright 2004-2008 Lorenzo Alberton
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @version CVS: $Id$
+ * @link http://pear.php.net/package/Translation2
+ */
+
+/**
+ * require Translation2_Container class
+ */
+require_once 'Translation2/Container.php';
+
+/**
+ * Storage driver for fetching data from a database
+ *
+ * This storage driver can use all databases which are supported
+ * by the PEAR::MDB2 abstraction layer to fetch data.
+ *
+ * @category Internationalization
+ * @package Translation2
+ * @author Lorenzo Alberton
+ * @copyright 2004-2008 Lorenzo Alberton
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @version CVS: $Id$
+ * @link http://pear.php.net/package/Translation2
+ */
+class Translation2_Container_mdb2 extends Translation2_Container
+{
+ // {{{ class vars
+
+ /**
+ * MDB2 object
+ * @var object
+ */
+ var $db = null;
+
+ /**
+ * query counter
+ * @var integer
+ * @access private
+ */
+ var $_queries = 0;
+
+ // }}}
+ // {{{ init
+
+ /**
+ * Initialize the container
+ *
+ * @param string &$db Connection data or MDB2 object
+ *
+ * @return boolean|PEAR_Error object if something went wrong
+ */
+ function init(&$db)
+ {
+ $this->_setDefaultOptions();
+ if (PEAR::isError($err = $this->_connect($db))) {
+ return $err;
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ _connect()
+
+ /**
+ * Connect to database by using the given DSN string
+ *
+ * @param mixed &$db DSN string | array | MDB2 object
+ *
+ * @return boolean|PEAR_Error on error
+ * @access private
+ */
+ function _connect(&$db)
+ {
+ if (is_object($db) && is_a($db, 'MDB2_Driver_Common')) {
+ $this->db = &$db;
+ } elseif (is_string($db) || is_array($db)) {
+ include_once 'MDB2.php';
+ $this->db =& MDB2::connect($db);
+ } elseif (is_object($db) && MDB2::isError($db)) {
+ return PEAR::raiseError($db->getMessage(), $db->code);
+ } else {
+ return PEAR::raiseError('The given dsn was not valid in file '
+ . __FILE__ . ' at line ' . __LINE__,
+ TRANSLATION2_ERROR_CANNOT_CONNECT,
+ PEAR_ERROR_RETURN);
+ }
+
+ if (PEAR::isError($this->db)) {
+ return $this->db;
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ _setDefaultOptions()
+
+ /**
+ * Set some default options
+ *
+ * @return void
+ * @access private
+ */
+ function _setDefaultOptions()
+ {
+ $this->options['langs_avail_table'] = 'langs';
+ $this->options['lang_id_col'] = 'id';
+ $this->options['lang_name_col'] = 'name';
+ $this->options['lang_meta_col'] = 'meta';
+ $this->options['lang_errmsg_col'] = 'error_text';
+ $this->options['lang_encoding_col'] = 'encoding';
+
+ $this->options['strings_default_table'] = 'i18n';
+ $this->options['strings_tables'] = array(); // 'lang_id' => 'table_name'
+ $this->options['string_id_col'] = 'id';
+ $this->options['string_page_id_col'] = 'page_id';
+ $this->options['string_page_id_col_length'] = 50;
+ $this->options['string_text_col'] = '%s'; // col_name if one table per lang is used,
+ // or a pattern (i.e. "tr_%s" => "tr_EN_US")
+ }
+
+ // }}}
+ // {{{ setCharset()
+
+ /**
+ * Set charset used to read/store the translations
+ *
+ * @param string $charset character set (encoding)
+ *
+ * @return PEAR_Error on error
+ */
+ function setCharset($charset)
+ {
+ return $this->db->setCharset($charset);
+ }
+
+ // }}}
+ // {{{ fetchLangs()
+
+ /**
+ * Fetch the available langs if they're not cached yet.
+ *
+ * @return PEAR_Error on error
+ */
+ function fetchLangs()
+ {
+ $query = sprintf('SELECT %s AS id, %s AS name, %s AS meta, %s AS error_text, %s AS encoding FROM %s',
+ $this->db->quoteIdentifier($this->options['lang_id_col'], true),
+ $this->db->quoteIdentifier($this->options['lang_name_col'], true),
+ $this->db->quoteIdentifier($this->options['lang_meta_col'], true),
+ $this->db->quoteIdentifier($this->options['lang_errmsg_col'], true),
+ $this->db->quoteIdentifier($this->options['lang_encoding_col'], true),
+ $this->db->quoteIdentifier($this->options['langs_avail_table'], true)
+ );
+
+ ++$this->_queries;
+ $res = $this->db->queryAll($query, null, MDB2_FETCHMODE_ASSOC);
+ if (PEAR::isError($res)) {
+ return $res;
+ }
+ foreach ($res as $row) {
+ $row = array_change_key_case($row, CASE_LOWER);
+ $this->langs[$row['id']] = $row;
+ }
+ }
+
+ // }}}
+ // {{{ getPage()
+
+ /**
+ * Returns an array of the strings in the selected page
+ *
+ * @param string $pageID page/group ID
+ * @param string $langID language ID
+ *
+ * @return array
+ */
+ function getPage($pageID = null, $langID = null)
+ {
+ $langID = $this->_getLangID($langID);
+ if (PEAR::isError($langID)) {
+ return $langID;
+ }
+ $lang_col = $this->_getLangCol($langID);
+ $table = $this->_getLangTable($langID);
+
+ $query = sprintf('SELECT %s, %s FROM %s WHERE %s ',
+ $this->db->quoteIdentifier($this->options['string_id_col'], true),
+ $this->db->quoteIdentifier($lang_col, true),
+ $this->db->quoteIdentifier($table, true),
+ $this->db->quoteIdentifier($this->options['string_page_id_col'], true)
+ );
+
+ if (is_null($pageID)) {
+ $query .= 'IS NULL';
+ } else {
+ $query .= ' = ' . $this->db->quote($pageID, 'text');
+ }
+
+ ++$this->_queries;
+ $res = $this->db->query($query);
+ if (PEAR::isError($res)) {
+ return $res;
+ }
+
+ $strings = array();
+ while (list($key, $value) = $res->fetchRow(MDB2_FETCHMODE_ORDERED)) {
+ $strings[$key] = $value;
+ }
+ $res->free();
+ return $strings;
+ }
+
+ // }}}
+ // {{{ getOne()
+
+ /**
+ * Get a single item from the container
+ *
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ * @param string $langID language ID
+ *
+ * @return string
+ */
+ function getOne($stringID, $pageID = null, $langID = null)
+ {
+ $langID = $this->_getLangID($langID);
+ if (PEAR::isError($langID)) {
+ return $langID;
+ }
+ $lang_col = $this->_getLangCol($langID);
+ $table = $this->_getLangTable($langID);
+
+ $query = sprintf('SELECT %s FROM %s WHERE %s = %s AND %s',
+ $this->db->quoteIdentifier($lang_col, true),
+ $this->db->quoteIdentifier($table, true),
+ $this->db->quoteIdentifier($this->options['string_id_col'], true),
+ $this->db->quote($stringID, 'text'),
+ $this->db->quoteIdentifier($this->options['string_page_id_col'], true)
+ );
+
+ if (is_null($pageID)) {
+ $query .= ' IS NULL';
+ } else {
+ $query .= ' = ' . $this->db->quote($pageID, 'text');
+ }
+
+ ++$this->_queries;
+ return $this->db->queryOne($query);
+ }
+
+ // }}}
+ // {{{ getStringID()
+
+ /**
+ * Get the stringID for the given string
+ *
+ * @param string $string string
+ * @param string $pageID page/group ID
+ *
+ * @return string
+ */
+ function getStringID($string, $pageID = null)
+ {
+ $lang_col = $this->_getLangCol($this->currentLang['id']);
+ $table = $this->_getLangTable($this->currentLang['id']);
+ $query = sprintf('SELECT %s FROM %s WHERE %s = %s AND %s',
+ $this->db->quoteIdentifier($this->options['string_id_col'], true),
+ $this->db->quoteIdentifier($table, true),
+ $this->db->quoteIdentifier($lang_col, true),
+ $this->db->quote($string, 'text'),
+ $this->db->quoteIdentifier($this->options['string_page_id_col'], true)
+ );
+ if (is_null($pageID)) {
+ $query .= ' IS NULL';
+ } else {
+ $query .= ' = ' . $this->db->quote($pageID, 'text');
+ }
+ ++$this->_queries;
+ return $this->db->queryOne($query);
+ }
+
+ // }}}
+ // {{{ _getLangTable()
+
+ /**
+ * Get the table a language is stored in
+ *
+ * @param string $langID language ID
+ *
+ * @return string table $langID is stored in
+ * @access private
+ */
+ function _getLangTable($langID)
+ {
+ if (isset($this->options['strings_tables'][$langID])) {
+ return $this->options['strings_tables'][$langID];
+ }
+ return str_replace('%s', $langID, $this->options['strings_default_table']);
+ }
+
+ // }}}
+ // {{{ _getLangCol()
+
+ /**
+ * Get the column a language's string is stored in
+ *
+ * @param string $langID language ID
+ *
+ * @return string column $langID is stored in
+ * @access private
+ */
+ function _getLangCol($langID)
+ {
+ static $cols;
+ if (!isset($cols[$langID])) {
+ if (isset($this->options['string_text_col']) &&
+ !empty($this->options['string_text_col'])) {
+ $cols[$langID] = str_replace('%s', $langID, $this->options['string_text_col']);
+ } else {
+ $cols[$langID] = $langID;
+ }
+ }
+ return $cols[$langID];
+ }
+
+ // }}}
+}
+?>
\ No newline at end of file
diff --git a/Translation2/Container/xml.php b/Translation2/Container/xml.php
new file mode 100644
index 0000000..8fab9cb
--- /dev/null
+++ b/Translation2/Container/xml.php
@@ -0,0 +1,500 @@
+
+ * @author Olivier Guilyardi
+ * @copyright 2004-2008 Lorenzo Alberton, Olivier Guilyardi
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @version CVS: $Id$
+ * @link http://pear.php.net/package/Translation2
+ */
+
+/**
+ * require Translation2_Container class
+ */
+require_once 'Translation2/Container.php';
+/**
+ * require XML_Unserializer class
+ */
+require_once 'XML/Unserializer.php';
+/**
+ * Document Type Definition
+ */
+define('TRANSLATION2_DTD',
+ "\n" .
+ "\n" .
+ "\n" .
+ "\n" .
+ "\n" .
+ "\n" .
+ "\n" .
+ "\n" .
+ "\n" .
+ "\n" .
+ "\n" .
+ "\n" .
+ "\n" .
+ "\n" .
+ "\n"
+);
+
+/**
+ * Storage driver for fetching data from a XML file
+ *
+ * Example file :
+ *
+ *
+ *
+ *
+ *
+ * English
+ * Custom meta data
+ * Non disponible en français
+ * iso-8859-1
+ *
+ *
+ *
+ *
+ *
+ *
+ * Chat
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * @category Internationalization
+ * @package Translation2
+ * @author Lorenzo Alberton
+ * @author Olivier Guilyardi
+ * @copyright 2004-2008 Lorenzo Alberton, Olivier Guilyardi
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @link http://pear.php.net/package/Translation2
+ */
+class Translation2_Container_xml extends Translation2_Container
+{
+ // {{{ class vars
+
+ /**
+ * Unserialized XML data
+ * @var object
+ */
+ var $_data = null;
+
+ /**
+ * XML file name
+ * @var string
+ */
+ var $_filename;
+
+ // }}}
+ // {{{ init
+
+ /**
+ * Initialize the container
+ *
+ * @param array $options - 'filename': Path to the XML file
+ *
+ * @return boolean|PEAR_Error object if something went wrong
+ */
+ function init($options)
+ {
+ $this->_filename = $options['filename'];
+ unset($options['filename']);
+ $this->_setDefaultOptions();
+ $this->_parseOptions($options);
+
+ return $this->_loadFile();
+ }
+
+ // }}}
+ // {{{ _loadFile()
+
+ /**
+ * Load an XML file into memory, and eventually decode the strings from UTF-8
+ *
+ * @return boolean|PEAR_Error
+ * @access private
+ */
+ function _loadFile()
+ {
+ $keyAttr = array (
+ 'lang' => 'id',
+ 'page' => 'key',
+ 'string' => 'key',
+ 'tr' => 'lang'
+ );
+ if (!$fp = @fopen($this->_filename, 'r')) {
+ return new PEAR_Error ("Can\'t read from the XML source: {$this->_filename}");
+ }
+ @flock($fp, LOCK_SH);
+ $unserializer = &new XML_Unserializer (array('keyAttribute' => $keyAttr));
+ if (PEAR::isError($status = $unserializer->unserialize($this->_filename, true))) {
+ fclose($fp);
+ return $status;
+ }
+ fclose($fp);
+
+ // unserialize data
+ $this->_data = $unserializer->getUnserializedData();
+ $this->fixEmptySets($this->_data);
+ $this->_fixDuplicateEntries();
+
+ // Handle default language settings.
+ // This allows, for example, to rapidly write the meta data as:
+ //
+ //
+ //
+
+ $defaults = array(
+ 'name' => '',
+ 'meta' => '',
+ 'error_text' => '',
+ 'encoding' => 'iso-8859-1'
+ );
+
+ foreach ($this->_data['languages'] as $lang_id => $settings) {
+ if (empty($settings)) {
+ $this->_data['languages'][$lang_id] = $defaults;
+ } else {
+ $this->_data['languages'][$lang_id] =
+ array_merge($defaults, $this->_data['languages'][$lang_id]);
+ }
+ }
+
+ // convert lang metadata from UTF-8
+ if (PEAR::isError($e = $this->_convertLangEncodings('from_xml', $this->_data))) {
+ return $e;
+ }
+
+ // convert encodings of the translated strings from xml (somehow heavy)
+ return $this->_convertEncodings('from_xml', $this->_data);
+ }
+
+ // }}}
+ // {{{ _convertEncodings()
+
+ /**
+ * Convert strings to/from XML unique charset (UTF-8)
+ *
+ * @param string $direction ['from_xml' | 'to_xml']
+ * @param array &$data Data buffer to operate on
+ *
+ * @return boolean|PEAR_Error
+ */
+ function _convertEncodings($direction, &$data)
+ {
+ if ($direction == 'from_xml') {
+ $source_encoding = 'UTF-8';
+ } else {
+ $target_encoding = 'UTF-8';
+ }
+
+ foreach ($data['pages'] as $page_id => $page_content) {
+ foreach ($page_content as $str_id => $translations) {
+ foreach ($translations as $lang => $str) {
+ if ($direction == 'from_xml') {
+ $target_encoding =
+ strtoupper($data['languages'][$lang]['encoding']);
+ } else {
+ $source_encoding =
+ strtoupper($data['languages'][$lang]['encoding']);
+ }
+ if ($target_encoding != $source_encoding) {
+ $res = iconv($source_encoding, $target_encoding, $str);
+ if ($res === false) {
+ $msg = 'Encoding conversion error ' .
+ "(source encoding: $source_encoding, ".
+ "target encoding: $target_encoding, ".
+ "processed string: \"$str\"";
+ return $this->raiseError($msg,
+ TRANSLATION2_ERROR_ENCODING_CONVERSION,
+ PEAR_ERROR_RETURN,
+ E_USER_WARNING);
+ }
+ $data['pages'][$page_id][$str_id][$lang] = $res;
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ _convertLangEncodings()
+
+ /**
+ * Convert lang data to/from XML unique charset (UTF-8)
+ *
+ * @param string $direction ['from_xml' | 'to_xml']
+ * @param array &$data Data buffer to operate on
+ *
+ * @return boolean|PEAR_Error
+ */
+ function _convertLangEncodings($direction, &$data)
+ {
+ static $fields = array('name', 'meta', 'error_text');
+
+ if ($direction == 'from_xml') {
+ $source_encoding = 'UTF-8';
+ } else {
+ $target_encoding = 'UTF-8';
+ }
+
+ foreach ($data['languages'] as $lang_id => $lang) {
+ if ($direction == 'from_xml') {
+ $target_encoding = strtoupper($lang['encoding']);
+ } else {
+ $source_encoding = strtoupper($lang['encoding']);
+ }
+ //foreach (array_keys($lang) as $field) {
+ foreach ($fields as $field) {
+ if ($target_encoding != $source_encoding && !empty($lang[$field])) {
+ $res = iconv($source_encoding, $target_encoding, $lang[$field]);
+ if ($res === false) {
+ $msg = 'Encoding conversion error ' .
+ "(source encoding: $source_encoding, ".
+ "target encoding: $target_encoding, ".
+ "processed string: \"$lang[$field]\"";
+ return $this->raiseError($msg,
+ TRANSLATION2_ERROR_ENCODING_CONVERSION,
+ PEAR_ERROR_RETURN,
+ E_USER_WARNING);
+ }
+ $data['languages'][$lang_id][$field] = $res;
+ }
+ }
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ _fixDuplicateEntries()
+
+ /**
+ * Remove duplicate entries from the xml data
+ *
+ * @return void
+ */
+ function _fixDuplicateEntries()
+ {
+ foreach ($this->_data['pages'] as $pagename => $pagedata) {
+ foreach ($pagedata as $stringname => $stringvalues) {
+ if (is_array(array_pop($stringvalues))) {
+ $this->_data['pages'][$pagename][$stringname] =
+ call_user_func_array(array($this, '_merge'), $stringvalues);
+ }
+ }
+ }
+ }
+
+ // }}}
+ // {{{ fixEmptySets()
+
+ /**
+ * Turn empty strings returned by XML_Unserializer into empty arrays
+ *
+ * Note: this method is public because called statically by the t2xmlchk.php
+ * script. It is not meant to be called by user-space code.
+ *
+ * @param array &$data array of languages/pages
+ *
+ * @return void
+ * @access public
+ * @static
+ */
+ function fixEmptySets(&$data)
+ {
+ if (PEAR::isError($this->_data) && ($this->_data->code == XML_UNSERIALIZER_ERROR_NO_UNSERIALIZATION)) {
+ //empty file... create skeleton
+ $this->_data = array(
+ 'languages' => array(),
+ 'pages' => array(),
+ );
+ }
+ if (is_string($data['languages']) and trim($data['languages']) == '') {
+ $data['languages'] = array();
+ }
+ if (is_string($data['pages']) and trim($data['pages']) == '') {
+ $data['pages'] = array();
+ } else {
+ foreach ($data['pages'] as $pageName => $strings) {
+ //if (is_string($strings) and trim($strings) == '') {
+ if (is_string($strings)) {
+ $data['pages'][$pageName] = array();
+ } else {
+ foreach ($strings as $stringName => $translations) {
+ if (is_string($translations) and trim($translations) == '') {
+ $data['pages'][$pageName][$stringName] = array();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // }}}
+ // {{{ _merge()
+
+ /**
+ * Wrapper for array_merge()
+ *
+ * @param array $arr1 reference
+ *
+ * @return array
+ */
+ function _merge()
+ {
+ $return = array();
+ foreach (func_get_args() as $arg) {
+ $return = array_merge($return, $arg);
+ }
+ return $return;
+ }
+
+ // }}}
+ // {{{ _setDefaultOptions()
+
+ /**
+ * Set some default options
+ *
+ * @return void
+ * @access private
+ */
+ function _setDefaultOptions()
+ {
+ //save changes on shutdown or in real time?
+ $this->options['save_on_shutdown'] = true;
+ }
+
+ // }}}
+ // {{{ fetchLangs()
+
+ /**
+ * Fetch the available langs
+ *
+ * @return void
+ */
+ function fetchLangs()
+ {
+ $res = array();
+ foreach ($this->_data['languages'] as $id => $spec) {
+ $spec['id'] = $id;
+ $res[$id] = $spec;
+ }
+ $this->langs = $res;
+ }
+
+ // }}}
+ // {{{ getPage()
+
+ /**
+ * Returns an array of the strings in the selected page
+ *
+ * @param string $pageID page/group ID
+ * @param string $langID language ID
+ *
+ * @return array
+ */
+ function getPage($pageID = null, $langID = null)
+ {
+ $langID = $this->_getLangID($langID);
+ if (PEAR::isError($langID)) {
+ return $langID;
+ }
+ $pageID = (is_null($pageID)) ? '#NULL' : $pageID;
+ $pageID = (empty($pageID) && (0 !== $pageID)) ? '#EMPTY' : $pageID;
+
+ $result = array();
+ foreach ($this->_data['pages'][$pageID] as $str_id => $translations) {
+ $result[$str_id] = isset($translations[$langID])
+ ? $translations[$langID]
+ : null;
+ }
+
+ return $result;
+ }
+
+ // }}}
+ // {{{ getOne()
+
+ /**
+ * Get a single item from the container
+ *
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ * @param string $langID language ID
+ *
+ * @return string
+ */
+ function getOne($stringID, $pageID = null, $langID = null)
+ {
+ $langID = $this->_getLangID($langID);
+ if (PEAR::isError($langID)) {
+ return $langID;
+ }
+ $pageID = (is_null($pageID)) ? '#NULL' : $pageID;
+ return isset($this->_data['pages'][$pageID][$stringID][$langID])
+ ? $this->_data['pages'][$pageID][$stringID][$langID]
+ : null;
+ }
+
+ // }}}
+ // {{{ getStringID()
+
+ /**
+ * Get the stringID for the given string
+ *
+ * @param string $string string
+ * @param string $pageID page/group ID
+ *
+ * @return string
+ */
+ function getStringID($string, $pageID = null)
+ {
+ $pageID = (is_null($pageID)) ? '#NULL' : $pageID;
+
+ foreach ($this->_data['pages'][$pageID] as $stringID => $translations) {
+ if (array_search($string, $translations) !== false) {
+ return $stringID;
+ }
+ }
+
+ return '';
+ }
+
+ // }}}
+}
+?>
\ No newline at end of file
diff --git a/Decorator.php b/Translation2/Decorator.php
similarity index 100%
rename from Decorator.php
rename to Translation2/Decorator.php
diff --git a/Translation2/Decorator/CacheLiteFunction.php b/Translation2/Decorator/CacheLiteFunction.php
new file mode 100644
index 0000000..227d8e4
--- /dev/null
+++ b/Translation2/Decorator/CacheLiteFunction.php
@@ -0,0 +1,452 @@
+
+ * @copyright 2004-2007 Lorenzo Alberton
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @version CVS: $Id$
+ * @link http://pear.php.net/package/Translation2
+ */
+
+/**
+ * Load Translation2 decorator base class
+ * and Cache_Lite_Function class
+ */
+require_once 'Translation2/Decorator.php';
+require_once 'Cache/Lite/Function.php';
+
+/**
+ * Decorator to cache fetched data using the Cache_Lite_Function class.
+ *
+ * @category Internationalization
+ * @package Translation2
+ * @author Lorenzo Alberton
+ * @copyright 2004-2007 Lorenzo Alberton
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @version CVS: $Id$
+ * @link http://pear.php.net/package/Translation2
+ */
+class Translation2_Decorator_CacheLiteFunction extends Translation2_Decorator
+{
+ // {{{ class vars
+
+ /**
+ * Cache_Lite_Function object
+ * @var object
+ */
+ var $cacheLiteFunction = null;
+
+ /**
+ * @var int (default 1)
+ * @access private
+ */
+ var $tempVarNameGenerator = 1;
+
+ /**
+ * @var string
+ * @access private
+ */
+ var $tempVarName = null;
+
+ /**
+ * Cache lifetime (in seconds)
+ * @var int $lifeTime
+ * @access private
+ */
+ var $lifeTime = 3600;
+
+ /**
+ * Directory where to put the cache files
+ * (make sure to add a trailing slash)
+ * @var string $cacheDir
+ * @access private
+ */
+ var $cacheDir = '/tmp/';
+
+ /**
+ * Enable / disable fileLocking. Can avoid cache corruption under bad
+ * circumstances.
+ * @var string $cacheDir
+ * @access private
+ */
+ var $fileLocking = true;
+
+ /**
+ * Enable / disable caching
+ * (can be very useful to debug cached scripts)
+ * @var boolean $caching
+ */
+ var $caching = true;
+
+ /**
+ * Frequency of cache cleaning.
+ * Higher values mean lower cleaning probability.
+ * Set 0 to disable. Set 1 to clean at every request.
+ * @var boolean $caching
+ */
+ var $cleaningFrequency = 0;
+
+ /**
+ * Name of default cache group.
+ * @var string $defaultGroup
+ */
+ var $defaultGroup = 'Translation2';
+
+ // }}}
+ // {{{ _prepare()
+
+ /**
+ * Istanciate a new Cache_Lite_Function object
+ * and get the name for an unused global variable,
+ * needed by Cache_Lite_Function
+ *
+ * @return void
+ * @access private
+ */
+ function _prepare()
+ {
+ if (is_null($this->cacheLiteFunction)) {
+ $cache_options = array(
+ 'caching' => $this->caching,
+ 'cacheDir' => $this->cacheDir,
+ 'lifeTime' => $this->lifeTime,
+ 'fileLocking' => $this->fileLocking,
+ 'defaultGroup' => $this->defaultGroup,
+
+ );
+ $this->cacheLiteFunction = new Cache_Lite_Function($cache_options);
+ }
+
+ $this->_cleanCache();
+ }
+
+ // }}}
+ // {{{ setLang()
+
+ /**
+ * Set default lang
+ *
+ * Set the language that shall be used when retrieving strings.
+ *
+ * @param string $langID language code (for instance, 'en' or 'it')
+ *
+ * @return void
+ */
+ function setLang($langID)
+ {
+ // WITHOUT THIS, IT DOESN'T WORK
+ global $translation2_storage_cachelitefunction_temp;
+ //generate temp variable
+ $translation2_storage_cachelitefunction_temp = $this->translation2->storage;
+
+ $this->_prepare();
+ $res = $this->cacheLiteFunction->call(
+ 'translation2_storage_cachelitefunction_temp->setLang', $langID);
+ if (PEAR::isError($res)) {
+ return $res;
+ }
+ $this->translation2->lang = $res;
+
+ }
+
+ // }}}
+ // {{{ setCacheOption()
+
+ /**
+ * Set a Cache_Lite option
+ *
+ * Passes a Cache_Lite option forward to the Cache_Lite object
+ * See Cache_Lite constructor for available options
+ *
+ * @param string $name name of the option
+ * @param string $value new value of the option
+ *
+ * @return self
+ * @access public
+ * @see Cache_Lite::setOption()
+ */
+ function setCacheOption($name, $value)
+ {
+ $this->_prepare();
+ $this->cacheLiteFunction->setOption($name, $value);
+ return $this;
+ }
+
+ // }}}
+ // {{{ getLang()
+
+ /**
+ * get lang info
+ *
+ * Get some extra information about the language (its full name,
+ * the localized error text, ...)
+ *
+ * @param string $langID language ID
+ * @param string $format ['name', 'meta', 'error_text', 'array']
+ *
+ * @return mixed [string | array], depending on $format
+ */
+ function getLang($langID = null, $format = 'name')
+ {
+ $langs = $this->getLangs('array');
+
+ if (is_null($langID)) {
+ if (!isset($this->lang['id']) || !array_key_exists($this->lang['id'], $langs)) {
+ $msg = 'Translation2::getLang(): unknown language "'.$langID.'".'
+ .' Use Translation2::setLang() to set a default language.';
+ return $this->storage->raiseError($msg, TRANSLATION2_ERROR_UNKNOWN_LANG);
+ }
+ $langID = $this->lang['id'];
+ }
+
+ if ($format == 'array') {
+ return $langs[$langID];
+ } elseif (isset($langs[$langID][$format])) {
+ return $langs[$langID][$format];
+ } elseif (isset($langs[$langID]['name'])) {
+ return $langs[$langID]['name'];
+ }
+ $msg = 'Translation2::getLang(): unknown language "'.$langID.'".'
+ .' Use Translation2::setLang() to set a default language.';
+ return $this->storage->raiseError($msg, TRANSLATION2_ERROR_UNKNOWN_LANG);
+ }
+
+ // }}}
+ // {{{ getLangs()
+
+ /**
+ * get langs
+ *
+ * Get some extra information about the languages (their full names,
+ * the localized error text, their codes, ...)
+ *
+ * @param string $format ['ids', 'names', 'array']
+ *
+ * @return array
+ */
+ function getLangs($format = 'name')
+ {
+ // WITHOUT THIS, IT DOESN'T WORK
+ global $translation2_cachelitefunction_temp;
+ //generate temp variable
+ $translation2_cachelitefunction_temp = $this->translation2;
+
+ $this->_prepare();
+ return $this->cacheLiteFunction->call('translation2_cachelitefunction_temp->getLangs',
+ $format);
+ }
+
+ // }}}
+ // {{{ getRaw()
+
+ /**
+ * Get translated string (as-is)
+ *
+ * First check if the string is cached, if not => fetch the page
+ * from the container and cache it for later use.
+ *
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ * @param string $langID language ID
+ * @param string $defaultText Text to display when the strings in both
+ * the default and the fallback lang are empty
+ *
+ * @return string
+ */
+ function getRaw($stringID, $pageID = TRANSLATION2_DEFAULT_PAGEID, $langID = null, $defaultText = '')
+ {
+ // WITHOUT THIS, IT DOESN'T WORK
+ global $translation2_cachelitefunction_temp;
+ //generate temp variable
+ $translation2_cachelitefunction_temp = $this->translation2;
+
+ if ($pageID == TRANSLATION2_DEFAULT_PAGEID) {
+ $pageID = $this->translation2->currentPageID;
+ }
+ $langID = empty($langID) ? $this->translation2->lang['id'] : $langID;
+
+ $this->_prepare();
+
+ return $this->cacheLiteFunction->call('translation2_cachelitefunction_temp->getRaw',
+ $stringID, $pageID, $langID, $defaultText);
+ }
+
+ // }}}
+ // {{{ get()
+
+ /**
+ * Get translated string
+ *
+ * First check if the string is cached, if not => fetch the page
+ * from the container and cache it for later use.
+ *
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ * @param string $langID language ID
+ * @param string $defaultText Text to display when the strings in both
+ * the default and the fallback lang are empty
+ *
+ * @return string
+ */
+ function get($stringID, $pageID = TRANSLATION2_DEFAULT_PAGEID, $langID = null, $defaultText = '')
+ {
+ // WITHOUT THIS, IT DOESN'T WORK
+ global $translation2_cachelitefunction_temp;
+ //generate temp variable
+ $translation2_cachelitefunction_temp = $this->translation2->storage;
+
+ if ($pageID == TRANSLATION2_DEFAULT_PAGEID) {
+ $pageID = $this->translation2->currentPageID;
+ }
+ $langID = empty($langID) ? $this->translation2->lang['id'] : $langID;
+
+ $this->_prepare();
+
+ $string = $this->cacheLiteFunction->call('translation2_cachelitefunction_temp->getOne',
+ $stringID, $pageID, $langID);
+ if (empty($string)) {
+ return $defaultText;
+ }
+ return $this->translation2->_replaceParams($string);
+ }
+
+ // }}}
+ // {{{ getRawPage()
+
+ /**
+ * Get the array of strings in a page
+ *
+ * First check if the strings are cached, if not => fetch the page
+ * from the container and cache it for later use.
+ *
+ * @param string $pageID page/group ID
+ * @param string $langID language ID
+ *
+ * @return array
+ */
+ function getRawPage($pageID = TRANSLATION2_DEFAULT_PAGEID, $langID = null)
+ {
+ // WITHOUT THIS, IT DOESN'T WORK
+ global $translation2_cachelitefunction_temp;
+ //generate temp variable
+ $translation2_cachelitefunction_temp = $this->translation2;
+
+ if ($pageID == TRANSLATION2_DEFAULT_PAGEID) {
+ $pageID = $this->translation2->currentPageID;
+ }
+ $langID = empty($langID) ? $this->translation2->lang['id'] : $langID;
+
+ $this->_prepare();
+
+ return $this->cacheLiteFunction->call('translation2_cachelitefunction_temp->getRawPage',
+ $pageID, $langID);
+ }
+
+ // }}}
+ // {{{ getPage()
+
+ /**
+ * Same as getRawPage, but resort to fallback language and
+ * replace parameters when needed
+ *
+ * @param string $pageID page/group ID
+ * @param string $langID language ID
+ *
+ * @return array
+ */
+ function getPage($pageID = TRANSLATION2_DEFAULT_PAGEID, $langID = null)
+ {
+ // WITHOUT THIS, IT DOESN'T WORK
+ global $translation2_cachelitefunction_temp;
+ //generate temp variable
+ $translation2_cachelitefunction_temp = $this->translation2;
+
+ if ($pageID == TRANSLATION2_DEFAULT_PAGEID) {
+ $pageID = $this->translation2->currentPageID;
+ }
+ $langID = empty($langID) ? $this->translation2->lang['id'] : $langID;
+
+ $this->_prepare();
+
+ return $this->cacheLiteFunction->call('translation2_cachelitefunction_temp->getPage',
+ $pageID, $langID);
+ }
+
+ // }}}
+ // {{{ getStringID()
+
+ /**
+ * Get translated string
+ *
+ * @param string $string This is NOT the stringID, this is a real string.
+ * The method will search for its matching stringID,
+ * and then it will return the associate string in the
+ * selected language.
+ * @param string $pageID page/group ID
+ *
+ * @return string
+ */
+ function getStringID($string, $pageID=TRANSLATION2_DEFAULT_PAGEID)
+ {
+ // WITHOUT THIS, IT DOESN'T WORK
+ global $translation2_cachelitefunction_temp;
+ //generate temp variable
+ $translation2_cachelitefunction_temp = $this->translation2;
+
+ if ($pageID == TRANSLATION2_DEFAULT_PAGEID) {
+ $pageID = $this->translation2->currentPageID;
+ }
+ $this->_prepare();
+
+ return $this->cacheLiteFunction->call('translation2_cachelitefunction_temp->getStringID',
+ $string, $pageID);
+ }
+
+ // }}}
+ // {{{ _cleanCache()
+
+ /**
+ * Statistically purge the cache
+ *
+ * @return void
+ */
+ function _cleanCache()
+ {
+ if ($this->cleaningFrequency > 0) {
+ if (mt_rand(1, $this->cleaningFrequency) == 1) {
+ $this->cacheLiteFunction->clean($this->defaultGroup);
+ }
+ }
+ }
+
+ // }}}
+}
+?>
\ No newline at end of file
diff --git a/Translation2/Decorator/CacheMemory.php b/Translation2/Decorator/CacheMemory.php
new file mode 100644
index 0000000..e9eaab1
--- /dev/null
+++ b/Translation2/Decorator/CacheMemory.php
@@ -0,0 +1,229 @@
+
+ * @copyright 2004-2007 Lorenzo Alberton
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @version CVS: $Id$
+ * @link http://pear.php.net/package/Translation2
+ */
+
+/**
+ * Load Translation2 decorator base class
+ */
+require_once 'Translation2/Decorator.php';
+
+/**
+ * Allows redefinition of alternate key for empty pageID
+ */
+if (!defined('TRANSLATION2_EMPTY_PAGEID_KEY')) {
+ define('TRANSLATION2_EMPTY_PAGEID_KEY', 'array_key_4_empty_pageID');
+}
+/**
+ * Allows redefinition of alternate key for null pageID
+ */
+if (!defined('TRANSLATION2_NULL_PAGEID_KEY')) {
+ define('TRANSLATION2_NULL_PAGEID_KEY', 'array_key_4_null_pageID');
+}
+
+/**
+ * Decorator to cache fetched data in memory
+ *
+ * @category Internationalization
+ * @package Translation2
+ * @author Lorenzo Alberton
+ * @copyright 2004-2007 Lorenzo Alberton
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @version CVS: $Id$
+ * @link http://pear.php.net/package/Translation2
+ */
+class Translation2_Decorator_CacheMemory extends Translation2_Decorator
+{
+ // {{{ class vars
+
+ /**
+ * Translated strings array
+ * Used for cache purposes.
+ * No parameter substitution or fallback langs here.
+ * @var array
+ * @access protected
+ */
+ var $rawData = array();
+
+ /**
+ * set prefetch on/off
+ * @var boolean
+ * @access protected
+ */
+ var $prefetch = true;
+
+ // }}}
+ // {{{ _getPageIDKey()
+
+ /**
+ * return a valid array key based on pageID value
+ *
+ * @param mixed $pageID (string or null)
+ *
+ * @return string
+ * @access private
+ */
+ function _getPageIDKey($pageID)
+ {
+ if (is_null($pageID)) {
+ return TRANSLATION2_NULL_PAGEID_KEY;
+ }
+ if (empty($pageID)) {
+ return TRANSLATION2_EMPTY_PAGEID_KEY;
+ }
+ if ($pageID == TRANSLATION2_DEFAULT_PAGEID) {
+ return $this->translation2->currentPageID;
+ }
+ return $pageID;
+ }
+
+ // }}}
+ // {{{ getRaw()
+
+ /**
+ * Get translated string (as-is)
+ *
+ * First check if the string is cached, if not => fetch the page
+ * from the container and cache it for later use.
+ *
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ * @param string $langID language ID
+ * @param string $defaultText Text to display when the strings in both
+ * the default and the fallback lang are empty
+ *
+ * @return string
+ */
+ function getRaw($stringID, $pageID = TRANSLATION2_DEFAULT_PAGEID, $langID = null, $defaultText = null)
+ {
+ $pageID_key = $this->_getPageIDKey($pageID);
+ $langID_key = empty($langID) ? $this->translation2->lang['id'] : $langID;
+
+ if (!array_key_exists($langID_key, $this->rawData)) {
+ $this->rawData[$langID_key] = array();
+ }
+
+ if ($this->prefetch) {
+ $this->getRawPage($pageID, $langID);
+ }
+ if (array_key_exists($pageID_key, $this->rawData[$langID_key])) {
+ if (PEAR::isError($this->rawData[$langID_key][$pageID_key])) {
+ return $this->rawData[$langID_key][$pageID_key];
+ }
+ $str = (isset($this->rawData[$langID_key][$pageID_key][$stringID]) ?
+ $this->rawData[$langID_key][$pageID_key][$stringID] : ''); //empty string or null value?
+ } else {
+ $str = $this->translation2->getRaw($stringID, $pageID, $langID, $defaultText);
+ }
+ return $str;
+ }
+
+ // }}}
+ // {{{ get()
+
+ /**
+ * Get translated string
+ *
+ * First check if the string is cached, if not => fetch the page
+ * from the container and cache it for later use.
+ *
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ * @param string $langID language ID
+ * @param string $defaultText Text to display when the strings in both
+ * the default and the fallback lang are empty
+ *
+ * @return string
+ */
+ function get($stringID, $pageID = TRANSLATION2_DEFAULT_PAGEID, $langID = null, $defaultText = null)
+ {
+ $str = $this->getRaw($stringID, $pageID, $langID, $defaultText);
+ return $this->_replaceParams($str);
+ }
+
+ // }}}
+ // {{{ getRawPage()
+
+ /**
+ * Get the array of strings in a page
+ *
+ * First check if the strings are cached, if not => fetch the page
+ * from the container and cache it for later use.
+ *
+ * @param string $pageID page/group ID
+ * @param string $langID language ID
+ *
+ * @return array
+ */
+ function getRawPage($pageID = TRANSLATION2_DEFAULT_PAGEID, $langID = null)
+ {
+ $pageID_key = $this->_getPageIDKey($pageID);
+ $langID_key = empty($langID) ? $this->translation2->lang['id'] : $langID;
+
+ if (!array_key_exists($langID_key, $this->rawData)) {
+ $this->rawData[$langID_key] = array();
+ }
+ if (!array_key_exists($pageID_key, $this->rawData[$langID_key])) {
+ $this->rawData[$langID_key][$pageID_key] =
+ $this->translation2->getRawPage($pageID, $langID);
+ }
+ return $this->rawData[$langID_key][$pageID_key];
+ }
+
+ // }}}
+ // {{{ getPage()
+
+ /**
+ * Same as getRawPage, but resort to fallback language and
+ * replace parameters when needed
+ *
+ * @param string $pageID page/group ID
+ * @param string $langID language ID
+ *
+ * @return array
+ */
+ function getPage($pageID = TRANSLATION2_DEFAULT_PAGEID, $langID = null)
+ {
+ $pageID_key = $this->_getPageIDKey($pageID);
+ $langID_key = empty($langID) ? $this->translation2->lang['id'] : $langID;
+
+ $this->getRawPage($pageID, $langID);
+ return $this->_replaceParams($this->rawData[$langID_key][$pageID_key]);
+ }
+
+ // }}}
+}
+?>
\ No newline at end of file
diff --git a/Translation2/Decorator/DefaultText.php b/Translation2/Decorator/DefaultText.php
new file mode 100644
index 0000000..4ba46a9
--- /dev/null
+++ b/Translation2/Decorator/DefaultText.php
@@ -0,0 +1,196 @@
+
+ * @author Rolf 'Red' Ochsenbein
+ * @copyright 2004-2007 Lorenzo Alberton, Rolf 'Red' Ochsenbein
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @version CVS: $Id$
+ * @link http://pear.php.net/package/Translation2
+ */
+
+/**
+ * Load Translation2 decorator base class
+ */
+require_once 'Translation2/Decorator.php';
+
+/**
+ * Decorator to provide a fallback text for empty strings.
+ *
+ * If the string is empty, return the defaultText parameter.
+ * If the defaultText parameter is empty too, then return
+ * "$emptyPostfix.$outputString.$emptyPrefix", the three variables
+ * being class properties you can set to a custom string.
+ *
+ * @category Internationalization
+ * @package Translation2
+ * @author Lorenzo Alberton
+ * @author Rolf 'Red' Ochsenbein
+ * @copyright 2004-2007 Lorenzo Alberton, Rolf 'Red' Ochsenbein
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @version CVS: $Id$
+ * @link http://pear.php.net/package/Translation2
+ */
+class Translation2_Decorator_DefaultText extends Translation2_Decorator
+{
+ // {{{ class vars
+
+ /**
+ * String appended to the returned string when the string is empty
+ * and it's replaced by its $stringID. It can be used to mark unreplaced
+ * strings.
+ * @var string
+ * @access protected
+ */
+ var $emptyPostfix = '';
+
+ /**
+ * String prepended to the returned string when the string is empty
+ * and it's replaced by its $stringID. It can be used to mark unreplaced
+ * strings.
+ * @var string
+ * @access protected
+ */
+ var $emptyPrefix = '';
+
+ /**
+ * String to output when there was no translation
+ * %stringID% will be replaced with the stringID
+ * %stringID_url% will replaced with a urlencoded stringID
+ * %url% will be replaced with the targeted url
+ * @var string
+ * @access protected
+ */
+ //var $outputString = '%stringID%(T)';
+ var $outputString = '%stringID%';
+
+ /**
+ * Targeted URL of strings without translations
+ * @var string
+ * @access protected
+ */
+ var $url = '#';
+
+ // }}}
+ // {{{ get()
+
+ /**
+ * Get translated string
+ *
+ * If the string is empty, return the $defaultText if not empty,
+ * the $stringID otherwise.
+ *
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ * @param string $langID language ID
+ * @param string $defaultText Text to display when the string is empty
+ *
+ * @return string
+ */
+ function get($stringID, $pageID = TRANSLATION2_DEFAULT_PAGEID, $langID = null, $defaultText = '')
+ {
+ if ($pageID == TRANSLATION2_DEFAULT_PAGEID) {
+ $pageID = $this->translation2->currentPageID;
+ }
+ $str = $this->translation2->get($stringID, $pageID, $langID);
+ if (!empty($str)) {
+ return $str;
+ }
+ if (!empty($defaultText)) {
+ return $this->_replaceParams($defaultText);
+ }
+
+ $search = array(
+ '%stringID%',
+ '%stringID_url%',
+ '%pageID_url%',
+ '%url%'
+ );
+ $replace = array(
+ $stringID,
+ urlencode($stringID),
+ urlencode($pageID),
+ $this->url
+ );
+ return $this->_replaceParams(
+ $this->emptyPrefix
+ .str_replace($search, $replace, $this->outputString)
+ .$this->emptyPostfix
+ );
+ //$str = (empty($defaultText) ? $this->emptyPrefix.$stringID.$this->emptyPostfix : $defaultText);
+ }
+
+ // }}}
+ // {{{ getPage()
+
+ /**
+ * Replace empty strings with their $stringID
+ *
+ * @param string $pageID page/group ID
+ * @param string $langID language ID
+ *
+ * @return array
+ */
+ function getPage($pageID = TRANSLATION2_DEFAULT_PAGEID, $langID = null)
+ {
+ $data = $this->translation2->getPage($pageID, $langID);
+ return $this->replaceEmptyStringsWithKeys($data);
+ }
+
+ // }}}
+ // {{{ getStringID
+
+ /**
+ * Get the stringID for the given string. This method is the reverse of get().
+ * If the requested string is unknown to the system,
+ * the requested string will be returned.
+ *
+ * @param string $string This is NOT the stringID, this is a real string.
+ * The method will search for its matching stringID,
+ * and then it will return the associate string in the
+ * selected language.
+ * @param string $pageID page/group ID
+ *
+ * @return string
+ */
+ function &getStringID($string, $pageID = TRANSLATION2_DEFAULT_PAGEID)
+ {
+ if ($pageID == TRANSLATION2_DEFAULT_PAGEID) {
+ $pageID = $this->translation2->currentPageID;
+ }
+ $stringID = $this->storage->getStringID($string, $pageID);
+ if (empty($stringID)) {
+ $stringID = $string;
+ }
+ return $stringID;
+ }
+}
+?>
\ No newline at end of file
diff --git a/Translation2/Decorator/ErrorText.php b/Translation2/Decorator/ErrorText.php
new file mode 100644
index 0000000..386a183
--- /dev/null
+++ b/Translation2/Decorator/ErrorText.php
@@ -0,0 +1,108 @@
+
+ * @copyright 2004-2007 Lorenzo Alberton
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @version CVS: $Id$
+ * @link http://pear.php.net/package/Translation2
+ */
+
+/**
+ * Load Translation2 decorator base class
+ */
+require_once 'Translation2/Decorator.php';
+
+/**
+ * Decorator to provide a fallback text for empty strings.
+ *
+ * @category Internationalization
+ * @package Translation2
+ * @author Lorenzo Alberton
+ * @copyright 2004-2007 Lorenzo Alberton
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @version CVS: $Id$
+ * @link http://pear.php.net/package/Translation2
+ */
+class Translation2_Decorator_ErrorText extends Translation2_Decorator
+{
+ // {{{ get()
+
+ /**
+ * Get translated string
+ *
+ * If the string is empty, return the error message
+ *
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ * @param string $langID language ID
+ * @param string $defaultText Text to display when the string is empty
+ *
+ * @return string
+ */
+ function get($stringID, $pageID = TRANSLATION2_DEFAULT_PAGEID, $langID = null, $defaultText = '')
+ {
+ $str = $this->translation2->get($stringID, $pageID, $langID, $defaultText);
+ if (empty($str)) {
+ $str = $this->translation2->getLang(null, 'error_text');
+ }
+ return $str;
+ }
+
+ // }}}
+ // {{{ getPage()
+
+ /**
+ * Same as getRawPage, but resort to fallback language and
+ * replace parameters when needed
+ *
+ * @param string $pageID page/group ID
+ * @param string $langID language ID
+ *
+ * @return array
+ */
+ function getPage($pageID = TRANSLATION2_DEFAULT_PAGEID, $langID = null)
+ {
+ $data = $this->translation2->getPage($pageID, $langID);
+ if (PEAR::isError($data)) {
+ return $data;
+ }
+ $error_text = str_replace('"', '\"', $this->translation2->getLang(null, 'error_text'));
+ array_walk(
+ $data,
+ create_function('&$w', 'if (empty($w)) $w = "'.$error_text.'";')
+ );
+ return $data;
+ }
+
+ // }}}
+}
+?>
\ No newline at end of file
diff --git a/Translation2/Decorator/Iconv.php b/Translation2/Decorator/Iconv.php
new file mode 100644
index 0000000..e97da41
--- /dev/null
+++ b/Translation2/Decorator/Iconv.php
@@ -0,0 +1,156 @@
+
+ * @author Sergey Korotkov
+ * @copyright 2004-2007 Lorenzo Alberton, Sergey Korotkov
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @version CVS: $Id$
+ * @link http://pear.php.net/package/Translation2
+ */
+
+/**
+ * Load Translation2 decorator base class
+ */
+require_once 'Translation2/Decorator.php';
+
+/**
+ * Translation2 Iconv Decorator
+ *
+ * Decorator to change the encoding of the stored translation to the
+ * one given in the 'encoding' option.
+ *
+ *
+ * $tr->setOptions(array('encoding' => 'UTF-8'));
+ *
+ *
+ * @category Internationalization
+ * @package Translation2
+ * @author Lorenzo Alberton
+ * @author Sergey Korotkov
+ * @copyright 2004-2007 Lorenzo Alberton, Sergey Korotkov
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @version CVS: $Id$
+ * @link http://pear.php.net/package/Translation2
+ * @see http://www.php.net/htmlentities for a list of available encodings.
+ */
+class Translation2_Decorator_Iconv extends Translation2_Decorator
+{
+ // {{{ class vars
+
+ /**
+ * @var string
+ * @access private
+ */
+ var $encoding = 'ISO-8859-1';
+
+ /**
+ * @var array
+ * @access private
+ */
+ var $lang_encodings;
+
+ // }}}
+ // {{{ _getEncoding()
+
+ /**
+ * Get the encoding for the given langID
+ *
+ * @param string $langID language ID
+ *
+ * @return string encoding
+ * @access private
+ */
+ function _getEncoding($langID = null)
+ {
+ if (!is_array($this->lang_encodings)) {
+ $this->lang_encodings = array();
+ foreach ($this->translation2->getLangs('encodings') as $langID => $encoding) {
+ $this->lang_encodings[$langID] = $encoding;
+ }
+ }
+ if (!is_null($langID) && isset($this->lang_encodings[$langID])) {
+ return $this->lang_encodings[$langID];
+ }
+ return $this->lang['encoding'];
+ }
+
+ // }}}
+ // {{{ get()
+
+ /**
+ * Get the translated string, in the new encoding
+ *
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ * @param string $langID language ID
+ * @param string $defaultText Text to display when the string is empty
+ *
+ * @return string
+ */
+ function get($stringID, $pageID = TRANSLATION2_DEFAULT_PAGEID, $langID = null, $defaultText = null)
+ {
+ $str = $this->translation2->get($stringID, $pageID, $langID, $defaultText);
+ if (PEAR::isError($str) || empty($str)) {
+ return $str;
+ }
+ return iconv($this->_getEncoding($langID), $this->encoding, $str);
+ }
+
+ // }}}
+ // {{{ getPage()
+
+ /**
+ * Same as getRawPage, but apply transformations when needed
+ *
+ * @param string $pageID page/group ID
+ * @param string $langID language ID
+ *
+ * @return array
+ */
+ function getPage($pageID = TRANSLATION2_DEFAULT_PAGEID, $langID = null)
+ {
+ $data = $this->translation2->getPage($pageID, $langID);
+ if (PEAR::isError($data)) {
+ return $data;
+ }
+ $input_encoding = $this->_getEncoding($langID);
+ foreach (array_keys($data) as $k) {
+ if (!empty($data[$k])) {
+ $data[$k] = iconv($input_encoding, $this->encoding, $data[$k]);
+ }
+ }
+ return $data;
+ }
+
+ // }}}
+}
+?>
\ No newline at end of file
diff --git a/Translation2/Decorator/Lang.php b/Translation2/Decorator/Lang.php
new file mode 100644
index 0000000..6cc6710
--- /dev/null
+++ b/Translation2/Decorator/Lang.php
@@ -0,0 +1,149 @@
+
+ * @copyright 2004-2007 Lorenzo Alberton
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @version CVS: $Id$
+ * @link http://pear.php.net/package/Translation2
+ */
+
+/**
+ * Load Translation2 decorator base class
+ */
+require_once 'Translation2/Decorator.php';
+
+/**
+ * Decorator to provide a fallback language for empty strings.
+ *
+ * @category Internationalization
+ * @package Translation2
+ * @author Lorenzo Alberton
+ * @copyright 2004-2007 Lorenzo Alberton
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @version CVS: $Id$
+ * @link http://pear.php.net/package/Translation2
+ */
+class Translation2_Decorator_Lang extends Translation2_Decorator
+{
+ // {{{ class vars
+
+ /**
+ * fallback lang
+ * @var string
+ * @access protected
+ */
+ var $fallbackLang;
+
+ // }}}
+ // {{{ setOption()
+
+ /**
+ * set Decorator option (intercept 'fallbackLang' option).
+ * I don't know why it's needed, but it doesn't work without.
+ *
+ * @param string $option option name
+ * @param mixed $value option value
+ *
+ * @return self
+ */
+ function setOption($option, $value=null)
+ {
+ if ($option == 'fallbackLang') {
+ $this->fallbackLang = $value;
+ return $this;
+ }
+ return parent::setOption($option, $value);
+ }
+
+ // }}}
+ // {{{ get()
+
+ /**
+ * Get translated string
+ *
+ * If the string is empty, check the fallback language
+ *
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ * @param string $langID language ID
+ * @param string $defaultText Text to display when the strings in both
+ * the default and the fallback lang are empty
+ *
+ * @return string
+ */
+ function get($stringID, $pageID = TRANSLATION2_DEFAULT_PAGEID, $langID = null, $defaultText = '')
+ {
+ $str = $this->translation2->get($stringID, $pageID, $langID, $defaultText);
+ if (empty($str)) {
+ $str = $this->translation2->get($stringID, $pageID, $this->fallbackLang);
+ }
+ return $str;
+ }
+
+ // }}}
+ // {{{ getPage()
+
+ /**
+ * Same as getRawPage, but resort to fallback language and
+ * replace parameters when needed
+ *
+ * @param string $pageID page/group ID
+ * @param string $langID language ID
+ *
+ * @return array
+ */
+ function getPage($pageID = TRANSLATION2_DEFAULT_PAGEID, $langID = null)
+ {
+ $data1 = $this->translation2->getPage($pageID, $langID);
+ if (PEAR::isError($data1)) {
+ return $data1;
+ }
+ $data2 = $this->translation2->getPage($pageID, $this->fallbackLang);
+ if (PEAR::isError($data2)) {
+ return $data2;
+ }
+ foreach ($data1 as $key => $val) {
+ if (empty($val)) {
+ $data1[$key] = $data2[$key];
+ }
+ }
+ // append keys when fallback lang contains more than current
+ $diff = array_diff(array_keys($data2), array_keys($data1));
+ foreach ($diff as $key) {
+ $data1[$key] = $data2[$key];
+ }
+ return $data1;
+ }
+
+ // }}}
+}
+?>
\ No newline at end of file
diff --git a/Translation2/Decorator/SpecialChars.php b/Translation2/Decorator/SpecialChars.php
new file mode 100644
index 0000000..d08894f
--- /dev/null
+++ b/Translation2/Decorator/SpecialChars.php
@@ -0,0 +1,125 @@
+
+ * @copyright 2004-2007 Lorenzo Alberton
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @version CVS: $Id$
+ * @link http://pear.php.net/package/Translation2
+ */
+
+/**
+ * Load Translation2 decorator base class
+ */
+require_once 'Translation2/Decorator.php';
+
+/**
+ * Decorator to replace special chars with the matching html entities.
+ *
+ * You can set the charset to use (the default being 'ISO-8859-1'):
+ *
+ * $tr->setOptions(array('charset' => 'UTF-8'));
+ *
+ *
+ * @category Internationalization
+ * @package Translation2
+ * @author Lorenzo Alberton
+ * @copyright 2004-2007 Lorenzo Alberton
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @version CVS: $Id$
+ * @link http://pear.php.net/package/Translation2
+ * @see http://www.php.net/htmlentities for a list of available charsets.
+ */
+class Translation2_Decorator_SpecialChars extends Translation2_Decorator
+{
+ // {{{ class vars
+
+ /**
+ * @var string
+ * @access protected
+ */
+ var $charset = 'ISO-8859-1';
+
+ // }}}
+ // {{{ get()
+
+ /**
+ * Get translated string
+ *
+ * replace special chars with the matching html entities
+ *
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ * @param string $langID language ID
+ * @param string $defaultText Text to display when the string is empty
+ *
+ * @return string
+ */
+ function get($stringID, $pageID=TRANSLATION2_DEFAULT_PAGEID, $langID=null, $defaultText=null)
+ {
+ $str = $this->translation2->get($stringID, $pageID, $langID, $defaultText);
+ if (PEAR::isError($str)) {
+ return $str;
+ }
+ if (!empty($str)) {
+ $str = htmlentities($str, ENT_QUOTES, $this->charset);
+ }
+ return $str;
+ }
+
+ // }}}
+ // {{{ getPage()
+
+ /**
+ * Same as getRawPage, but apply transformations when needed
+ *
+ * @param string $pageID page/group ID
+ * @param string $langID language ID
+ *
+ * @return array
+ */
+ function getPage($pageID=TRANSLATION2_DEFAULT_PAGEID, $langID=null)
+ {
+ $data = $this->translation2->getPage($pageID, $langID);
+ if (PEAR::isError($data)) {
+ return $data;
+ }
+ foreach ($data as $key => $val) {
+ if (!empty($val)) {
+ $data[$key] = htmlentities($val, ENT_QUOTES, $this->charset);
+ }
+ }
+ return $data;
+ }
+
+ // }}}
+}
+?>
\ No newline at end of file
diff --git a/Translation2/Decorator/UTF8.php b/Translation2/Decorator/UTF8.php
new file mode 100644
index 0000000..c834bea
--- /dev/null
+++ b/Translation2/Decorator/UTF8.php
@@ -0,0 +1,114 @@
+
+ * @copyright 2004-2007 Lorenzo Alberton
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @version CVS: $Id$
+ * @link http://pear.php.net/package/Translation2
+ */
+
+/**
+ * Load Translation2 decorator base class
+ */
+require_once 'Translation2/Decorator.php';
+
+/**
+ * Decorator to convert UTF-8 strings to ISO-8859-1
+ *
+ * @category Internationalization
+ * @package Translation2
+ * @author Lorenzo Alberton
+ * @copyright 2004-2007 Lorenzo Alberton
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @version CVS: $Id$
+ * @link http://pear.php.net/package/Translation2
+ */
+class Translation2_Decorator_UTF8 extends Translation2_Decorator
+{
+ // {{{ get()
+
+ /**
+ * Get translated string
+ *
+ * Decode the UTF-8 string to ISO-8859-1
+ *
+ * @param string $stringID string ID
+ * @param string $pageID page/group ID
+ * @param string $langID language ID
+ * @param string $defaultText Text to display when the strings in both
+ * the default and the fallback lang are empty
+ *
+ * @return string
+ */
+ function get($stringID, $pageID = TRANSLATION2_DEFAULT_PAGEID, $langID = null, $defaultText = null)
+ {
+ $str = $this->translation2->get($stringID, $pageID, $langID);
+ if (PEAR::isError($str)) {
+ return $str;
+ }
+ if (!empty($str)) {
+ $str = utf8_decode($str); //decodes an UTF-8 string to ISO-8859-1
+ }
+ return $str;
+ }
+
+ // }}}
+ // {{{ getPage()
+
+ /**
+ * Same as getRawPage, but resort to fallback language and
+ * replace parameters when needed
+ *
+ * Decode each UTF-8 string in the group to ISO-8859-1
+ *
+ * @param string $pageID page/group ID
+ * @param string $langID language ID
+ *
+ * @return array
+ */
+ function getPage($pageID = TRANSLATION2_DEFAULT_PAGEID, $langID = null)
+ {
+ $data = $this->translation2->getPage($pageID, $langID);
+ if (PEAR::isError($data)) {
+ return $data;
+ }
+ foreach ($data as $key => $val) {
+ if (!empty($val)) {
+ $data[$key] = utf8_decode($val);
+ }
+ }
+ return $data;
+ }
+
+ // }}}
+}
+?>
\ No newline at end of file
diff --git a/package.xml b/package.xml
index 313eb22..de173b6 100644
--- a/package.xml
+++ b/package.xml
@@ -57,7 +57,7 @@ Req #16787 default 'latin1_swedish_ci' db created with collation set t
utf8_unicode_ci - quipo
-
+
@@ -124,7 +124,7 @@ utf8_unicode_ci - quipo
-
+