![]() |
|
Snippets |
|
By default, I18n content in database does not support fallback in default culture. This snippet allow you to enable I18n content fallback in order to always have a default value for your texts.
This snippet is sponsored by Dorigo consultants.
To enable the fallback, edit your table object class in lib/model/TableClassName.php and add the following code. Then search and replace TableClassName by your table object class name.
/** * Fetch the i18n object for this object culture. * * @return mixed A i18n object * @throws PropelException Any exceptions caught during processing will be * rethrown wrapped into a PropelException. */ public function getCurrentTableClassNameI18n() { if (!isset($this->current_i18n[$this->culture])) { $obj = TableClassNameI18nPeer::retrieveByPK($this->getId(), $this->culture); if ($obj) // Test if there is a translation for current culture { $this->setTableClassNameI18nForCulture($obj, $this->culture); } else // Create a translation for this culture { $new_i18n = new TableClassNameI18n(); $default_culture = sfConfig::get('sf_i18n_default_culture'); // We try to fetch the default culture translation to initialise the new culture. if (!isset($this->current_i18n[$default_culture])) { $obj = TableClassNameI18nPeer::retrieveByPK($this->getId(), $default_culture); if ($obj) // Test if there is a translation for current culture { $this->setTableClassNameI18nForCulture($obj, $default_culture); } } else { $obj = $this->current_i18n[$default_culture]; } if ($obj) { $obj->copyInto($new_i18n); } $new_i18n->setId($this->getId()); $new_i18n->setCulture($this->culture); $this->setTableClassNameI18nForCulture($new_i18n, $this->culture); } } return $this->current_i18n[$this->culture]; }
You now need to add default translation when you create a new object. We do that with this doSave function.
To use this function, edit your table object class in lib/model/TableClassName.php and add the following code. Then search and replace TableClassName by your table object class name.
/** * Stores the object in the database while setting default culture if necessary. * * If the object is new, it inserts it; otherwise an update is performed. * All related objects are also updated in this method. * * @param Connection $con The database connection * @return int The number of rows affected by this insert/update and any referring fk objects' save() operations. * @throws PropelException Any exceptions caught during processing will be * rethrown wrapped into a PropelException. * @see save() */ protected function doSave($con) { $default_culture = sfConfig::get('sf_i18n_default_culture'); // We try to fetch the default culture translation to initialise the new culture. if (!isset($this->current_i18n[$default_culture])) { $obj = TableClassNameI18nPeer::retrieveByPK($this->getId(), $default_culture, $con); if ($obj) // Test if there is a translation for current culture { $this->setTableClassNameI18nForCulture($obj, $default_culture); } } else { $obj = $this->current_i18n[$default_culture]; } if(!$obj && isset($this->current_i18n[$this->culture])) { $new_i18n = new TableClassNameI18n(); $this->current_i18n[$this->culture]->copyInto($new_i18n); $new_i18n->setId($this->getId()); $new_i18n->setCulture($default_culture); $this->setTableClassNameI18nForCulture($new_i18n, $default_culture); } return parent::doSave($con); }
To complete this snippet, here is a fallback version of doSelectWithI18n.
To enable the fallback, edit your table object peer class in lib/model/TableClassNamePeer.php and add the following code. Then search and replace TableClassName by your table object class name.
/** * Selects a collection of TableClassName objects pre-filled with their i18n objects. * * @param Criteria $criteria * @param string $culture The selected culture. * @param Connection $con An optional database connection * @return array Array of TableClassName objects. * @throws PropelException Any exceptions caught during processing will be * rethrown wrapped into a PropelException. */ public static function doSelectWithI18n(Criteria $c, $culture = null, $con = null) { if ($culture === null) { $culture = sfContext::getInstance()->getUser()->getCulture(); } $default_culture = sfConfig::get('sf_i18n_default_culture'); // Set the correct dbName if it has not been overridden if ($c->getDbName() == Propel::getDefaultDB()) { $c->setDbName(self::DATABASE_NAME); } TableClassNamePeer::addSelectColumns($c); $startcol = (TableClassNamePeer::NUM_COLUMNS - TableClassNamePeer::NUM_LAZY_LOAD_COLUMNS) + 1; TableClassNameI18nPeer::addSelectColumns($c); $c->addJoin(TableClassNamePeer::ID, TableClassNameI18nPeer::ID); $criterion = $c->getNewCriterion(TableClassNameI18nPeer::CULTURE, $culture); $criterion->addOr($c->getNewCriterion(TableClassNameI18nPeer::CULTURE, $default_culture)); $c->add($criterion); $rs = BasePeer::doSelect($c, $con); $results = array(); $uncultured_results = array(); while($rs->next()) { $omClass = TableClassNamePeer::getOMClass(); $cls = Propel::import($omClass); $obj1 = new $cls(); $obj1->hydrate($rs); $obj1->setCulture($culture); if(isset($results[$obj1->getId()])) { $obj1 = $results[$obj1->getId()]; } $omClass = TableClassNameI18nPeer::getOMClass($rs, $startcol); $cls = Propel::import($omClass); $obj2 = new $cls(); $obj2->hydrate($rs, $startcol); $obj1->setTableClassNameI18nForCulture($obj2, $obj2->getCulture()); $obj2->setTableClassName($obj1); if(!isset($uncultured_results[$obj1->getId()])) { $uncultured_results[$obj1->getId()] = $obj1; } if($obj2->getCulture() == $culture) { $uncultured_results[$obj1->getId()] = false; } if(!isset($results[$obj1->getId()])) { $results[$obj1->getId()] = $obj1; } elseif($obj2->getCulture() == $culture) { // Move result to the end of results array to fit eventual sort // criteria (ugly fix). unset($results[$obj1->getId()]); $results[$obj1->getId()] = $obj1; } } foreach($uncultured_results as $obj1) { if($obj1) { $obj1->setCulture($default_culture); $default_culture_object = $obj1->getCurrentTableClassNameI18n(); if($default_culture_object) { $obj2 = new TableClassNameI18n(); $default_culture_object->copyInto($obj2); $obj2->setCulture($culture); $obj2->setTableClassName($obj1); $obj1->setTableClassNameI18nForCulture($obj2, $obj2->getCulture()); } $obj1->setCulture($culture); } } return array_values($results); }
If you want to use a pager with a filter on translations, you will need this count method. Once this method present, the magic is done by :
$criteria->setDistinct(); $pager->setCriteria($criteria); $pager->setPeerMethod('doSelectWithI18n'); $pager->setPeerCountMethod('doCountWithI18n');
Note : The setDistinct is very important for this snippet to work. It should not falsify your results, and without it, the doCountWithI18n method could return bad results.
To enable the fallback, edit your table object peer class in lib/model/TableClassNamePeer.php and add the following code. Then search and replace TableClassName by your table object class name.
/** * Returns the number of rows matching criteria with I18N criteria. * * @param Criteria $criteria * @param boolean $distinct Whether to select only distinct columns (You can also set DISTINCT modifier in Criteria). * @param Connection $con An optional database connection * @param string $culture The selected culture. * @return int Number of matching rows. */ public static function doCountWithI18n(Criteria $criteria, $distinct = false, $con = null, $culture = null) { // we're going to modify criteria, so copy it first $criteria = clone $criteria; $default_culture = sfConfig::get('sf_i18n_default_culture'); if ($culture === null) { // We use current user culture. $culture = sfContext::getInstance()->getUser()->getCulture(); } // clear out anything that might confuse the ORDER BY clause $criteria->clearSelectColumns()->clearOrderByColumns(); $criteria->addSelectColumn(TableClassNamePeer::COUNT_DISTINCT); // just in case we're grouping: add those columns to the select statement foreach($criteria->getGroupByColumns() as $column) { $criteria->addSelectColumn($column); } $criteria->addJoin(TableClassNamePeer::ID, TableClassNameI18nPeer::ID); $criterion = $criteria->getNewCriterion(TableClassNameI18nPeer::CULTURE, $culture); $criterion->addOr($criteria->getNewCriterion(TableClassNameI18nPeer::CULTURE, $default_culture)); $criteria->add($criterion); $rs = TableClassNamePeer::doSelectRS($criteria, $con); if ($rs->next()) { return $rs->getInt(1); } else { // no rows returned; we infer that means 0 matches. return 0; } }
Comments on this snippet
What version of Symfony are you using ? Because in my current website I use i18n and I have no problem when no translation exists for __('My string'), the it just displays the string as-is. It even works when the translation is empty in messages.XX.xml (the item exists, but the tag is empty).
I'm using Symfony 1.0.7. This snippet is not for fixed messages machinery. It is for database stored data. I agree that when you use __() helper, the fallback is present by default. But it is not the case when you use i18N tables in your database. This snippet is to implement fallback for database contents.
Hi from Russia!
This snippet proved to be very useful for me.
If do not setCulture, then when you invoke a method $Table->save(); caught in SQL table 2 record. The first record culture=''(empty string, not null), in the second recording culture='en' (default value). Why did it happen?
Sorry for my English primitive!
p.s. Bears with us in the streets no longer go! Extinct! :)
well, it happen because this snippet believe that '' is a valid culture, not the default one.
You can change this by adding
in doSave() method after the line :