changed i18n schema to use only one db table for translations

changed fieldname 'row_id' to 'foreign_key'
now allows fallbacks for not-existing translations by setting model's locale to array of locales
model used for hasMany associations and for saving/deleting of translation records is configurable by Model::$translateModel property, db field 'i18n.field' is  configurable by property $displayField of this custom model, instance of this model is obtainable by $this->ModelName->translateModel()
Added tests for translate behavior.
Added i18n shell script
Correcting translation function calls in various files

git-svn-id: https://svn.cakephp.org/repo/branches/1.2.x.x@5669 3807eeeb-6ff5-0310-8944-8be069107fe0
This commit is contained in:
phpnut 2007-09-18 04:16:04 +00:00
parent aaf161c073
commit bd3e702d77
13 changed files with 840 additions and 252 deletions

View file

@ -11,20 +11,18 @@
CREATE TABLE i18n (
id int(10) NOT NULL auto_increment,
locale varchar(6) NOT NULL,
i18n_content_id int(10) NOT NULL,
model varchar(255) NOT NULL,
row_id int(10) NOT NULL,
foreign_key int(10) NOT NULL,
field varchar(255) NOT NULL,
content mediumtext,
PRIMARY KEY (id),
KEY locale (locale),
KEY i18n_content_id (i18n_content_id),
KEY row_id (row_id),
KEY model (model),
KEY field (field)
);
CREATE TABLE i18n_content (
id int(10) NOT NULL auto_increment,
content text,
PRIMARY KEY (id)
# UNIQUE INDEX I18N_LOCALE_FIELD(locale, model, foreign_key, field),
# INDEX I18N_LOCALE_ROW(locale, model, foreign_key),
# INDEX I18N_LOCALE_MODEL(locale, model),
# INDEX I18N_FIELD(model, foreign_key, field),
# INDEX I18N_ROW(model, foreign_key),
INDEX locale (locale),
INDEX model (model),
INDEX row_id (foreign_key),
INDEX field (field)
);

151
cake/console/libs/i18n.php Normal file
View file

@ -0,0 +1,151 @@
<?php
/* SVN FILE: $Id$ */
/**
* Short description for file.
*
* Long description for file
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework <http://www.cakephp.org/>
* Copyright 2005-2007, Cake Software Foundation, Inc.
* 1785 E. Sahara Avenue, Suite 490-204
* Las Vegas, Nevada 89104
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @filesource
* @copyright Copyright 2005-2007, Cake Software Foundation, Inc.
* @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.console.libs
* @since CakePHP(tm) v 1.2.0.5669
* @version $Revision$
* @modifiedby $LastChangedBy$
* @lastmodified $Date$
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
*/
/**
* Shell for I18N management.
*
* @package cake
* @subpackage cake.cake.console.libs
*/
class I18nShell extends Shell {
/**
* Contains database source to use
*
* @var string
* @access public
*/
var $dataSource = 'default';
/**
* Contains tasks to load and instantiate
*
* @var array
* @access public
*/
var $tasks = array('DbConfig', 'Extract');
/**
* Override startup of the Shell
*
* @access public
*/
function startup() {
if (isset($this->params['datasource'])) {
$this->dataSource = $this->params['datasource'];
}
if ($this->command && !in_array($this->command, array('help'))) {
if (!config('database')) {
$this->out(__('Your database configuration was not found. Take a moment to create one.', true), true);
return $this->DbConfig->execute();
}
}
}
/**
* Override main() for help message hook
*
* @access public
*/
function main() {
$this->out(__('I18n Shell', true));
$this->hr();
$this->out(__('[E]xtract POT file from sources', true));
$this->out(__('[I]nitialize i18n database table', true));
$this->out(__('[H]elp', true));
$this->out(__('[Q]uit', true));
$choice = strtoupper($this->in(__('What would you like to do?', true), array('E', 'I', 'H', 'Q')));
switch($choice) {
case 'E':
$this->Extract->execute();
break;
case 'I':
$this->initdb();
break;
case 'H':
$this->help();
break;
case 'Q':
exit(0);
break;
default:
$this->out(__('You have made an invalid selection. Please choose a command to execute by entering E, I, H, or Q.', true));
}
$this->hr();
$this->main();
}
/**
* Initialize I18N database.
*
* @access public
*/
function initdb() {
$db =& ConnectionManager::getDataSource($this->dataSource);
$this->out(__('Initializing Database...', true), true);
$this->out(__('Creating i18n table ...', true), true);
$sql = ' CREATE TABLE '.$db->fullTableName('i18n').' (
'.$db->name('id').' '.$db->column($db->columns['primary_key']).',
'.$db->name('locale').' '.$db->column(array('name' => 'varchar', 'limit' => 6)).' NOT NULL,
'.$db->name('model').' '.$db->column($db->columns['string']).' NOT NULL,
'.$db->name('foreign_key').' '.$db->column($db->columns['integer']).' NOT NULL,
'.$db->name('field').' '.$db->column($db->columns['string']).' NOT NULL,
'.$db->name('content').' '.$db->column($db->columns['text']).',
PRIMARY KEY ('.$db->name('id').'),
INDEX locale ('.$db->name('locale').'),
INDEX model ('.$db->name('model').'),
INDEX foreign_key ('.$db->name('foreign_key').'),
INDEX field ('.$db->name('field').')
)';
if ($db->query($sql) === false) {
die('Error: ' . $db->lastError());
}
$this->out(__('Done.', true), true);
}
/**
* Show help screen.
*
* @access public
*/
function help() {
$this->hr();
$this->out(__('I18n Shell:', true));
$this->hr();
$this->out(__('I18n Shell initializes i18n database table for your application', true));
$this->out(__('and generates .pot file(s) with translations.', true));
$this->hr();
$this->out(__('usage:', true));
$this->out(' cake i18n help');
$this->out(' cake i18n initdb [-datasource custom]');
$this->out('');
$this->hr();
$this->Extract->help();
}
}
?>

View file

@ -126,7 +126,7 @@ class SchemaShell extends Shell {
}
$File = new File($this->Schema->path . DS . $write, true);
if($File->write($contents)) {
$this->out(__('SQL dump file created in '. $File->pwd(), true));
$this->out(sprintf(__('SQL dump file created in %s', true), $File->pwd()));
exit();
} else {
$this->err(__('SQL dump could not be created', true));

View file

@ -398,7 +398,7 @@ class Shell extends Object {
*/
function createFile ($path, $contents) {
$path = str_replace(DS . DS, DS, $path);
$this->out("\n".__(sprintf("Creating file %s", $path), true));
$this->out("\n" . sprintf(__("Creating file %s", true), $path));
if (is_file($path) && $this->interactive === true) {
$key = $this->in(__("File exists, overwrite?", true). " {$path}", array('y', 'n', 'q'), 'n');
if (low($key) == 'q') {

View file

@ -147,7 +147,7 @@ class ProjectTask extends Shell {
if ($Folder->copy($path)) {
$path = $Folder->slashTerm($path);
$this->hr();
$this->out(__(sprintf("Created: %s in %s", $app, $path), true));
$this->out(sprintf(__("Created: %s in %s", true), $app, $path));
$this->hr();
if ($this->createHome($path, $app)) {

View file

@ -61,7 +61,7 @@ class AclComponent extends Object {
}
$name .= 'Component';
} else {
trigger_error(__(sprintf('Could not find %s.', $name), true), E_USER_WARNING);
trigger_error(sprintf(__('Could not find %s.', true), $name), E_USER_WARNING);
}
}
$this->_instance =& new $name();

View file

@ -418,7 +418,7 @@ class AuthComponent extends Object {
case 'crud':
$this->mapActions();
if (!isset($this->actionMap[$this->params['action']])) {
trigger_error(__(sprintf('Auth::startup() - Attempted access of un-mapped action "%s" in controller "%s"', $this->params['action'], $this->params['controller']), true), E_USER_WARNING);
trigger_error(sprintf(__('Auth::startup() - Attempted access of un-mapped action "%s" in controller "%s"', true), $this->params['action'], $this->params['controller']), E_USER_WARNING);
} else {
$valid = $this->Acl->check($user, $this->action(':controller'), $this->actionMap[$this->params['action']]);
}
@ -434,13 +434,13 @@ class AuthComponent extends Object {
$action = $this->action(':action');
}
if (empty($object)) {
trigger_error(__(sprintf('Could not find %s. Set AuthComponent::$object in beforeFilter() or pass a valid object', get_class($object)), true), E_USER_WARNING);
trigger_error(sprintf(__('Could not find %s. Set AuthComponent::$object in beforeFilter() or pass a valid object', true), get_class($object)), E_USER_WARNING);
return;
}
if (method_exists($object, 'isAuthorized')) {
$valid = $object->isAuthorized($user, $this->action(':controller'), $action);
} elseif ($object){
trigger_error(__(sprintf('%s::isAuthorized() is not defined.', get_class($object)), true), E_USER_WARNING);
trigger_error(sprintf(__('%s::isAuthorized() is not defined.', true), get_class($object)), E_USER_WARNING);
}
break;
case null:
@ -669,7 +669,7 @@ class AuthComponent extends Object {
}
if (!ClassRegistry::isKeySet($name)) {
if (!loadModel(Inflector::underscore($name))) {
trigger_error(__(sprintf('Auth::getModel() - %s is not set or could not be found', $name), true), E_USER_WARNING);
trigger_error(sprintf(__('Auth::getModel() - %s is not set or could not be found', true), $name), E_USER_WARNING);
return $model;
} else {
$model = new $name();
@ -685,7 +685,7 @@ class AuthComponent extends Object {
}
if (empty($model)) {
trigger_error(__(sprintf('Auth::getModel() - %s is not set or could not be found', $name), true) . $name, E_USER_WARNING);
trigger_error(sprintf(__('Auth::getModel() - %s is not set or could not be found', true), $name), E_USER_WARNING);
return null;
}

View file

@ -27,8 +27,8 @@
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
*/
/**
* Included libraries.
*/
* Included libraries.
*/
uses('l10n');
/**
* Short description for file.
@ -43,16 +43,9 @@ class I18n extends Object {
* Instance of the I10n class for localization
*
* @var object
* @access private
*/
var $__l10n = null;
/**
* The locale for current translation
*
* @var string
* @access public
*/
var $locale = null;
var $l10n = null;
/**
* Translation strings for a specific domain read from the .mo or .po files
*
@ -86,7 +79,13 @@ class I18n extends Object {
static $instance = array();
if (!$instance) {
$instance[0] =& new I18n();
$instance[0]->__l10n =& new L10n();
$instance[0]->l10n =& new L10n();
$language = Configure::read('Config.language');
if ($language === null && !empty($_SESSION['Config']['language'])) {
$language = $_SESSION['Config']['language'];
}
$instance[0]->l10n->get($language);
}
return $instance[0];
}
@ -107,16 +106,6 @@ class I18n extends Object {
$_this =& I18n::getInstance();
$_this->category = $_this->__categories[$category];
if ($_this->__l10n->found === false) {
$language = Configure::read('Config.language');
if ($language === null && !empty($_SESSION['Config']['language'])) {
$language = $_SESSION['Config']['language'];
}
$_this->__l10n->get($language);
$_this->locale = $_this->__l10n->locale;
}
if (is_null($domain)) {
if (preg_match('/views{0,1}\\'.DS.'([^\/]*)/', $directory, $regs)) {
$domain = $regs[1];
@ -168,7 +157,7 @@ class I18n extends Object {
return($plural);
}
return($singular);
}
}
/**
* Attempts to find the plural form of a string.
*
@ -215,7 +204,7 @@ class I18n extends Object {
switch ($type) {
case -1:
return (0);
return (0);
case 1:
if ($n != 1) {
return (1);
@ -227,7 +216,7 @@ class I18n extends Object {
}
return (0);
case 7:
return ($n);
return ($n);
case 21:
if (($n % 10 == 1) && ($n % 100 != 11)) {
return (0);
@ -316,7 +305,7 @@ class I18n extends Object {
}
foreach ($searchPath as $directory) {
foreach ($_this->__l10n->languagePath as $lang) {
foreach ($_this->l10n->languagePath as $lang) {
$file = $directory . DS . $lang . DS . $_this->category . DS . $domain;
$default = APP . 'locale'. DS . $lang . DS . $_this->category . DS . 'default';
$core = CAKE_CORE_INCLUDE_PATH . DS . 'cake' . DS . 'locale'. DS . $lang . DS . $_this->category . DS . 'core';

View file

@ -118,8 +118,8 @@ class L10n extends Object {
/* French (Standard) */ 'fre' => 'fr',
/* Gaelic (Scots) */ 'gla' => 'gd',
/* Galician */ 'glg' => 'gl',
/* German (Standard) */ 'deu' => 'de',
/* German (Standard) */ 'ger' => 'de',
/* German (Standard) */ 'deu' => 'de',
/* German (Standard) */ 'ger' => 'de',
/* Greek */ 'ell' => 'el',
/* Greek */ 'gre' => 'el',
/* Hebrew */ 'heb' => 'he',
@ -135,7 +135,7 @@ class L10n extends Object {
/* Latvian */ 'lav' => 'lv',
/* Lithuanian */ 'lit' => 'lt',
/* Macedonian */ 'mac' => 'mk',
/* Macedonian */ 'mkd' => 'mk',
/* Macedonian */ 'mkd' => 'mk',
/* Malaysian */ 'may' => 'ms',
/* Malaysian */ 'msa' => 'ms',
/* Maltese */ 'mlt' => 'mt',
@ -158,8 +158,8 @@ class L10n extends Object {
/* Sorbian */ 'wen' => 'sb',
/* Spanish (Spain - Traditional) */ 'spa' => 'es',
/* Swedish */ 'swe' => 'sv',
/* Thai */ 'tha' => 'th',
/* Tsonga */ 'tso' => 'ts',
/* Thai */ 'tha' => 'th',
/* Tsonga */ 'tso' => 'ts',
/* Tswana */ 'tsn' => 'tn',
/* Turkish */ 'tur' => 'tr',
/* Ukrainian */ 'ukr' => 'uk',
@ -168,7 +168,7 @@ class L10n extends Object {
/* Vietnamese */ 'vie' => 'vi',
/* Xhosa */ 'xho' => 'xh',
/* Yiddish */ 'yid' => 'yi',
/* Zulu */ 'zul' => 'zu');
/* Zulu */ 'zul' => 'zu');
/**
* HTTP_ACCEPT_LANGUAGE catalog
*
@ -200,12 +200,12 @@ class L10n extends Object {
'ca' => array('language' => 'Catalan', 'locale' => 'cat', 'localeFallback' => 'cat', 'charset' => 'utf-8'),
'cs' => array('language' => 'Czech', 'locale' => 'cze', 'localeFallback' => 'cze', 'charset' => 'utf-8'),
'da' => array('language' => 'Danish', 'locale' => 'dan', 'localeFallback' => 'dan', 'charset' => 'utf-8'),
'de' => array('language' => 'German (Standard)', 'locale' => 'deu', 'localeFallback' => 'deu', 'charset' => 'utf-8'),
'de-at' => array('language' => 'German (Austria)', 'locale' => 'de_at', 'localeFallback' => 'deu', 'charset' => 'utf-8'),
'de-ch' => array('language' => 'German (Swiss)', 'locale' => 'de_ch', 'localeFallback' => 'deu', 'charset' => 'utf-8'),
'de-de' => array('language' => 'German (Germany)', 'locale' => 'de_de', 'localeFallback' => 'deu', 'charset' => 'utf-8'),
'de-li' => array('language' => 'German (Liechtenstein)', 'locale' => 'de_li', 'localeFallback' => 'deu', 'charset' => 'utf-8'),
'de-lu' => array('language' => 'German (Luxembourg)', 'locale' => 'de_lu', 'localeFallback' => 'deu', 'charset' => 'utf-8'),
'de' => array('language' => 'German (Standard)', 'locale' => 'deu', 'localeFallback' => 'deu', 'charset' => 'utf-8'),
'de-at' => array('language' => 'German (Austria)', 'locale' => 'de_at', 'localeFallback' => 'deu', 'charset' => 'utf-8'),
'de-ch' => array('language' => 'German (Swiss)', 'locale' => 'de_ch', 'localeFallback' => 'deu', 'charset' => 'utf-8'),
'de-de' => array('language' => 'German (Germany)', 'locale' => 'de_de', 'localeFallback' => 'deu', 'charset' => 'utf-8'),
'de-li' => array('language' => 'German (Liechtenstein)', 'locale' => 'de_li', 'localeFallback' => 'deu', 'charset' => 'utf-8'),
'de-lu' => array('language' => 'German (Luxembourg)', 'locale' => 'de_lu', 'localeFallback' => 'deu', 'charset' => 'utf-8'),
'e' => array('language' => 'Greek', 'locale' => 'gre', 'localeFallback' => 'gre', 'charset' => 'utf-8'),
'el' => array('language' => 'Greek', 'locale' => 'gre', 'localeFallback' => 'gre', 'charset' => 'utf-8'),
'en' => array('language' => 'English', 'locale' => 'eng', 'localeFallback' => 'eng', 'charset' => 'utf-8'),
@ -409,5 +409,62 @@ class L10n extends Object {
}
return false;
}
/**
* Attempts to find locale for language, or language for locale
*
* @param mixed $mixed 2/3 char string (language/locale), array of those strings, or null
* @return mixed string language/locale, array of those values, whole map as an array, or false when language/locale doesn't exist
* @access public
*/
function map($mixed = null) {
if (is_array($mixed)) {
$result = array();
foreach ($mixed as $_mixed) {
if ($_result = $this->map($_mixed)) {
$result[$_mixed] = $_result;
}
}
return $result;
}
if (is_string($mixed)) {
if (2 == strlen($mixed)) {
if (in_array($mixed, $this->__l10nMap)) {
return array_search($mixed, $this->__l10nMap);
}
} else {
if (isset($this->__l10nMap[$mixed])) {
return $this->__l10nMap[$mixed];
}
}
return false;
}
return $this->__l10nMap;
}
/**
* Attempts to find catalog record for requested language
*
* @param mixed $language string requested language, array of requested languages, or null for whole catalog
* @return mixed array catalog record for requested language, array of catalog records, whole catalog, or false when language doesn't exist
*/
function catalog($language = null) {
if (is_array($language)) {
$result = array();
foreach ($language as $_language) {
if ($_result = $this->catalog($_language)) {
$result[$_language] = $_result;
}
}
return $result;
}
if (is_string($language)) {
if (isset($this->__l10nCatalog[$language])) {
return $this->__l10nCatalog[$language];
}
return false;
}
return $this->__l10nCatalog;
}
}
?>

View file

@ -39,19 +39,6 @@ class TranslateBehavior extends ModelBehavior {
* Used for runtime configuration of model
*/
var $runtime = array();
/**
* Instance of I18nModel class, used internally
*/
var $_model = null;
/**
* Constructor
*/
function __construct() {
parent::__construct();
$this->_model =& new I18nModel();
ClassRegistry::addObject('I18nModel', $this->_model);
}
/**
* Callback
*
@ -66,44 +53,45 @@ class TranslateBehavior extends ModelBehavior {
* bindTranslation() method
*/
function setup(&$model, $config = array()) {
$this->settings[$model->name] = array();
$this->runtime[$model->name] = array('fields' => array());
$db =& ConnectionManager::getDataSource($model->useDbConfig);
if (!$db->connected) {
trigger_error('Datasource '.$model->useDbConfig.' for I18nBehavior of model '.$model->name.' is not connected', E_USER_ERROR);
trigger_error('Datasource '.$model->useDbConfig.' for TranslateBehavior of model '.$model->name.' is not connected', E_USER_ERROR);
return false;
}
$this->runtime[$model->name]['tablePrefix'] = $db->config['prefix'];
return $this->bindTranslation($model, $config, false);
$this->settings[$model->name] = array();
$this->runtime[$model->name] = array('fields' => array());
$this->translateModel($model);
return $this->bindTranslation($model, null, false);
}
/**
* Callback
*/
function beforeFind(&$model, $query) {
$locale = $this->_getLocale($model);
$db =& ConnectionManager::getDataSource($model->useDbConfig);
$tablePrefix = $db->config['prefix'];
$RuntimeModel =& $this->translateModel($model);
if (is_string($query['fields']) && 'COUNT(*) AS count' == $query['fields']) {
if (is_string($query['fields']) && 'COUNT(*) AS '.$db->name('count') == $query['fields']) {
$this->runtime[$model->name]['count'] = true;
$db =& ConnectionManager::getDataSource($model->useDbConfig);
$tablePrefix = $this->runtime[$model->name]['tablePrefix'];
if (empty($locale)) {
return $query;
}
$query['fields'] = 'COUNT(DISTINCT('.$db->name($model->name).'.'.$db->name($model->primaryKey).')) ' . $db->alias . 'count';
$query['joins'][] = array(
'type' => 'INNER',
'alias' => 'I18nModel',
'table' => $tablePrefix . 'i18n',
'conditions' => array(
$model->name.'.id' => '{$__cakeIdentifier[I18nModel.row_id]__$}',
'I18nModel.model' => $model->name,
'I18nModel.locale' => $locale
)
);
'type' => 'INNER',
'alias' => $RuntimeModel->name,
'table' => $db->name($tablePrefix . 'i18n'),
'conditions' => array(
$model->name.'.id' => '{$__cakeIdentifier['.$RuntimeModel->name.'.foreign_key]__$}',
$RuntimeModel->name.'.model' => $model->name,
$RuntimeModel->name.'.locale' => $locale));
return $query;
}
if (empty($locale) || is_array($locale)) {
if (empty($locale)) {
return $query;
}
$autoFields = false;
@ -125,42 +113,53 @@ class TranslateBehavior extends ModelBehavior {
}
$autoFields = true;
}
$fields = am($this->settings[$model->name], $this->runtime[$model->name]['fields']);
$tablePrefix = $this->runtime[$model->name]['tablePrefix'];
$addFields = array();
if (is_array($query['fields'])) {
if (in_array($model->name.'.*', $query['fields'])) {
foreach ($fields as $key => $value) {
$addFields[] = ife(is_numeric($key), $value, $key);
}
} else {
foreach ($fields as $key => $value) {
$field = ife(is_numeric($key), $value, $key);
if ($autoFields || in_array($model->name.'.'.$field, $query['fields']) || in_array($field, $query['fields'])) {
$addFields[] = $field;
}
foreach ($fields as $key => $value) {
$field = ife(is_numeric($key), $value, $key);
if (in_array($model->name.'.*', $query['fields']) || $autoFields || in_array($model->name.'.'.$field, $query['fields']) || in_array($field, $query['fields'])) {
$addFields[] = $field;
}
}
}
if ($addFields) {
$db =& ConnectionManager::getDataSource($model->useDbConfig);
foreach ($addFields as $field) {
$key = array_search($model->name.'.'.$field, $query['fields']);
if (false !== $key) {
unset($query['fields'][$key]);
foreach (array($field, $model->name.'.'.$field) as $_field) {
$key = array_search($_field, $query['fields']);
if ($key !== false) {
unset($query['fields'][$key]);
}
}
$query['fields'][] = 'I18n__'.$field.'.content';
$query['joins'][] = 'LEFT JOIN '.$db->name($tablePrefix.'i18n').' AS '.$db->name('I18n__'.$field.'Model').' ON '.$db->name($model->name.'.id').' = '.$db->name('I18n__'.$field.'Model.row_id');
$query['joins'][] = 'LEFT JOIN '.$db->name($tablePrefix.'i18n_content').' AS '.$db->name('I18n__'.$field).' ON '.$db->name('I18n__'.$field.'Model.i18n_content_id').' = '.$db->name('I18n__'.$field.'.id');
$query['conditions'][$db->name('I18n__'.$field.'Model.model')] = $model->name;
$query['conditions'][$db->name('I18n__'.$field.'Model.field')] = $field;
$query['conditions'][$db->name('I18n__'.$field.'Model`.`locale')] = $locale;
if (is_array($locale)) {
foreach ($locale as $_locale) {
$query['fields'][] = 'I18n__'.$field.'__'.$_locale.'.content';
$query['joins'][] = array(
'type' => 'LEFT',
'alias' => 'I18n__'.$field.'__'.$_locale,
'table' => $db->name($tablePrefix . 'i18n'),
'conditions' => array(
$model->name.'.id' => '{$__cakeIdentifier[I18n__'.$field.'__'.$_locale.'.foreign_key]__$}',
'I18n__'.$field.'__'.$_locale.'.model' => $model->name,
'I18n__'.$field.'__'.$_locale.'.'.$RuntimeModel->displayField => $field,
'I18n__'.$field.'__'.$_locale.'.locale' => $_locale));
}
} else {
$query['fields'][] = 'I18n__'.$field.'.content';
$query['joins'][] = array(
'type' => 'LEFT',
'alias' => 'I18n__'.$field,
'table' => $db->name($tablePrefix . 'i18n'),
'conditions' => array(
$model->name.'.id' => '{$__cakeIdentifier[I18n__'.$field.'.foreign_key]__$}',
'I18n__'.$field.'.model' => $model->name,
'I18n__'.$field.'.'.$RuntimeModel->displayField => $field));
$query['conditions'][$db->name('I18n__'.$field.'.locale')] = $locale;
}
}
}
$query['fields'] = am($query['fields']);
@ -178,31 +177,27 @@ class TranslateBehavior extends ModelBehavior {
$this->runtime[$model->name]['fields'] = array();
$locale = $this->_getLocale($model);
if (empty($locale) || empty($results)) {
if (empty($locale) || empty($results) || empty($this->runtime[$model->name]['beforeFind'])) {
return $results;
}
$beforeFind = $this->runtime[$model->name]['beforeFind'];
if (is_array($locale)) {
$fields = am($this->settings[$model->name], $this->runtime[$model->name]['fields']);
$emptyFields = array('locale' => '');
foreach ($results as $key => $row) {
$results[$key][$model->name]['locale'] = ife(is_array($locale), @$locale[0], $locale);
foreach ($fields as $key => $value) {
$field = ife(is_numeric($key), $value, $key);
$emptyFields[$field] = '';
}
unset($fields);
foreach ($beforeFind as $field) {
if (is_array($locale)) {
foreach ($locale as $_locale) {
if (!isset($results[$key][$model->name][$field]) && !empty($results[$key]['I18n__'.$field.'__'.$_locale]['content'])) {
$results[$key][$model->name][$field] = $results[$key]['I18n__'.$field.'__'.$_locale]['content'];
}
unset($results[$key]['I18n__'.$field.'__'.$_locale]);
}
foreach ($results as $key => $row) {
$results[$key][$model->name] = am($results[$key][$model->name], $emptyFields);
}
unset($emptyFields);
} elseif (!empty($this->runtime[$model->name]['beforeFind'])) {
$beforeFind = $this->runtime[$model->name]['beforeFind'];
foreach ($results as $key => $row) {
$results[$key][$model->name]['locale'] = $locale;
foreach ($beforeFind as $field) {
if (!isset($results[$key][$model->name][$field])) {
$results[$key][$model->name][$field] = '';
}
} else {
$value = ife(empty($results[$key]['I18n__'.$field]['content']), '', $results[$key]['I18n__'.$field]['content']);
$results[$key][$model->name][$field] = $value;
unset($results[$key]['I18n__'.$field]);
@ -210,7 +205,7 @@ class TranslateBehavior extends ModelBehavior {
}
}
return $results;
}
}
/**
* Callback
*/
@ -232,89 +227,47 @@ class TranslateBehavior extends ModelBehavior {
}
}
$this->runtime[$model->name]['beforeSave'] = $tempData;
$this->runtime[$model->name]['ignoreUserAbort'] = ignore_user_abort();
@ignore_user_abort(true);
return true;
}
/**
* Callback
*/
function afterSave(&$model, $created) {
$locale = $this->_getLocale($model);
if (empty($locale) || is_array($locale) || empty($this->runtime[$model->name]['beforeSave'])) {
if (!isset($this->runtime[$model->name]['beforeSave'])) {
return true;
}
$locale = $this->_getLocale($model);
$tempData = $this->runtime[$model->name]['beforeSave'];
unset($this->runtime[$model->name]['beforeSave']);
$conditions = array('locale' => $locale, 'model' => $model->name, 'foreign_key' => $model->id);
$RuntimeModel =& $this->translateModel($model);
$conditions = array('locale' => $locale,
'model' => $model->name,
'row_id' => $model->id);
if (empty($created)) {
$translations = $RuntimeModel->generateList(am($conditions, array($RuntimeModel->displayField => array_keys($tempData))));
if ($created) {
foreach ($tempData as $field => $value) {
$this->_model->Content->create();
$this->_model->Content->save(array('I18nContent' => array('content' => $value)));
$this->_model->create();
$this->_model->save(array('I18nModel' => am($conditions, array(
'i18n_content_id' => $this->_model->Content->getInsertID(),
'field' => $field))));
}
} else {
$this->_model->recursive = -1;
$translations = $this->_model->findAll($conditions, array('field', 'i18n_content_id'));
$fields = Set::extract($translations, '{n}.I18nModel.field');
$ids = Set::extract($translations, '{n}.I18nModel.i18n_content_id');
foreach ($fields as $key => $field) {
if (array_key_exists($field, $tempData)) {
$this->_model->Content->create();
$this->_model->Content->save(array('I18nContent' => array(
'id' => $ids[$key],
'content' => $tempData[$field])));
if ($translations) {
foreach ($translations as $id => $field) {
$RuntimeModel->create();
$RuntimeModel->save(array($RuntimeModel->name => array('id' => $id, 'content' => $tempData[$field])));
unset($tempData[$field]);
}
}
}
@ignore_user_abort((bool) $this->runtime[$model->name]['ignoreUserAbort']);
unset($this->runtime[$model->name]['ignoreUserAbort']);
}
/**
* Callback
*/
function beforeDelete(&$model) {
$this->runtime[$model->name]['ignoreUserAbort'] = ignore_user_abort();
@ignore_user_abort(true);
return true;
if (!empty($tempData)) {
foreach ($tempData as $field => $value) {
$RuntimeModel->create(am($conditions, array($RuntimeModel->displayField => $field, 'content' => $value)));
$RuntimeModel->save();
}
}
}
/**
* Callback
*/
function afterDelete(&$model) {
$this->_model->recursive = -1;
$conditions = array('model' => $model->name, 'row_id' => $model->id);
$translations = $this->_model->findAll($conditions, array('i18n_content_id'));
$ids = Set::extract($translations, '{n}.I18nModel.i18n_content_id');
$db =& ConnectionManager::getDataSource($model->useDbConfig);
$db->delete($this->_model->Content, array('id' => $ids));
$db->delete($this->_model, $conditions);
@ignore_user_abort((bool) $this->runtime[$model->name]['ignoreUserAbort']);
unset($this->runtime[$model->name]['ignoreUserAbort']);
}
/**
* Autodetects locale for application
*
* @todo
* @return string
*/
function _autoDetectLocale() {
// just fast hack to obtain selected locale
__d('core', 'Notice', true);
$I18n =& I18n::getInstance();
return $I18n->locale;
$RuntimeModel =& $this->translateModel($model);
$conditions = array('model' => $model->name, 'foreign_key' => $model->id);
$RuntimeModel->deleteAll($conditions);
}
/**
* Get selected locale for model
@ -323,36 +276,67 @@ class TranslateBehavior extends ModelBehavior {
*/
function _getLocale(&$model) {
if (!isset($model->locale) || is_null($model->locale)) {
$model->locale = $this->_autoDetectLocale();
$I18n =& I18n::getInstance();
$model->locale = $I18n->l10n->locale;
}
return $model->locale;
}
/**
* Get instance of model for translations
*
* @return object
*/
function &translateModel(&$model) {
if (!isset($this->runtime[$model->name]['model'])) {
if (!isset($model->translateModel) || empty($model->translateModel)) {
$className = 'I18nModel';
} else {
$className = $model->translateModel;
if (!class_exists($className) && !loadModel($className)) {
return $this->cakeError('missingModel', array(array('className' => $className)));
}
}
if (ClassRegistry::isKeySet($className)) {
if (PHP5) {
$this->runtime[$model->name]['model'] = ClassRegistry::getObject($className);
} else {
$this->runtime[$model->name]['model'] =& ClassRegistry::getObject($className);
}
} else {
if (PHP5) {
$this->runtime[$model->name]['model'] = new $className();
} else {
$this->runtime[$model->name]['model'] =& new $className();
}
ClassRegistry::addObject($className, $this->runtime[$model->name]['model']);
ClassRegistry::map($className, $className);
}
}
return $this->runtime[$model->name]['model'];
}
/**
* Bind translation for fields, optionally with hasMany association for
* fake field
*
* @param object instance of model
* @param mixed string with field or array(field1, field2=>AssocName, field3)
* @param mixed string with field, or array(field1, field2=>AssocName, field3), or null for bind all original translations
* @param boolead $reset
* @return boolean
*/
function bindTranslation(&$model, $fields, $reset = true) {
function bindTranslation(&$model, $fields = null, $reset = true) {
if (empty($fields)) {
return true;
return $this->bindTranslation($model, $model->actsAs['Translate'], $reset);
}
if (is_string($fields)) {
$fields = array($fields);
}
$settings =& $this->settings[$model->name];
$runtime =& $this->runtime[$model->name]['fields'];
$associations = array();
$default = array('className' => 'I18nModel', 'foreignKey' => 'row_id');
$RuntimeModel =& $this->translateModel($model);
$default = array('className' => $RuntimeModel->name, 'foreignKey' => 'foreign_key');
foreach ($fields as $key => $value) {
if (is_numeric($key)) {
$field = $value;
$association = null;
@ -361,29 +345,32 @@ class TranslateBehavior extends ModelBehavior {
$association = $value;
}
if (in_array($field, $settings)) {
$this->settings[$model->name] = array_diff_assoc($settings, array($field));
} elseif (array_key_exists($field, $settings)) {
unset($settings[$field]);
if (array_key_exists($field, $this->settings[$model->name])) {
unset($this->settings[$model->name][$field]);
} elseif (in_array($field, $this->settings[$model->name])) {
$this->settings[$model->name] = am(array_diff_assoc($this->settings[$model->name], array($field)));
}
if (in_array($field, $runtime)) {
$this->runtime[$model->name]['fields'] = array_diff_assoc($runtime, array($field));
} elseif (array_key_exists($field, $runtime)) {
unset($runtime[$field]);
if (array_key_exists($field, $this->runtime[$model->name]['fields'])) {
unset($this->runtime[$model->name]['fields'][$field]);
} elseif (in_array($field, $this->runtime[$model->name]['fields'])) {
$this->runtime[$model->name]['fields'] = am(array_diff_assoc($this->runtime[$model->name]['fields'], array($field)));
}
if (is_null($association)) {
if ($reset) {
$runtime[] = $field;
$this->runtime[$model->name]['fields'][] = $field;
} else {
$settings[] = $field;
$this->settings[$model->name][] = $field;
}
} else {
if ($reset) {
$runtime[$field] = $association;
$this->runtime[$model->name]['fields'][$field] = $association;
} else {
$settings[$field] = $association;
$this->settings[$model->name][$field] = $association;
}
foreach (array('hasOne', 'hasMany', 'belongsTo', 'hasAndBelongsToMany') as $type) {
@ -393,8 +380,8 @@ class TranslateBehavior extends ModelBehavior {
}
}
$associations[$association] = am($default, array('conditions' => array(
'model' => $model->name,
'field' => $field)));
'model' => $model->name,
$RuntimeModel->displayField => $field)));
}
}
@ -408,21 +395,19 @@ class TranslateBehavior extends ModelBehavior {
* fake field
*
* @param object instance of model
* @param mixed string with field or array(field1, field2=>AssocName, field3)
* @param mixed string with field, or array(field1, field2=>AssocName, field3), or null for unbind all original translations
* @return boolean
*/
function unbindTranslation(&$model, $fields) {
function unbindTranslation(&$model, $fields = null) {
if (empty($fields)) {
return true;
return $this->unbindTranslation($model, $model->actsAs['Translate']);
}
if (is_string($fields)) {
$fields = array($fields);
}
$settings =& $this->settings[$model->name];
$runtime =& $this->runtime[$model->name]['fields'];
$default = array('className' => 'I18nModel', 'foreignKey' => 'row_id');
$RuntimeModel =& $this->translateModel($model);
$default = array('className' => $RuntimeModel->name, 'foreignKey' => 'foreign_key');
$associations = array();
foreach ($fields as $key => $value) {
@ -434,16 +419,18 @@ class TranslateBehavior extends ModelBehavior {
$association = $value;
}
if (in_array($field, $settings)) {
$this->settings[$model->name] = array_diff_assoc($settings, array($field));
} elseif (array_key_exists($field, $settings)) {
unset($settings[$field]);
if (array_key_exists($field, $this->settings[$model->name])) {
unset($this->settings[$model->name][$field]);
} elseif (in_array($field, $this->settings[$model->name])) {
$this->settings[$model->name] = am(array_diff_assoc($this->settings[$model->name], array($field)));
}
if (in_array($field, $runtime)) {
$this->runtime[$model->name]['fields'] = array_diff_assoc($runtime, array($field));
} elseif (array_key_exists($field, $runtime)) {
unset($runtime[$field]);
if (array_key_exists($field, $this->runtime[$model->name]['fields'])) {
unset($this->runtime[$model->name]['fields'][$field]);
} elseif (in_array($field, $this->runtime[$model->name]['fields'])) {
$this->runtime[$model->name]['fields'] = am(array_diff_assoc($this->runtime[$model->name]['fields'], array($field)));
}
if (!is_null($association) && (isset($model->hasMany[$association]) || isset($model->__backAssociation['hasMany'][$association]))) {
@ -457,21 +444,15 @@ class TranslateBehavior extends ModelBehavior {
return true;
}
}
if (!defined('CAKEPHP_UNIT_TEST_EXECUTION')) {
/**
* @package cake
* @subpackage cake.cake.libs.model.behaviors
*/
class I18nContent extends AppModel {
var $name = 'I18nContent';
var $useTable = 'i18n_content';
}
/**
* @package cake
* @subpackage cake.cake.libs.model.behaviors
*/
class I18nModel extends AppModel {
var $name = 'I18nModel';
var $useTable = 'i18n';
var $belongsTo = array('Content' => array('className' => 'I18nContent', 'foreignKey' => 'i18n_content_id'));
class I18nModel extends AppModel {
var $name = 'I18nModel';
var $useTable = 'i18n';
var $displayField = 'field';
}
}
?>

View file

@ -0,0 +1,302 @@
<?php
/* SVN FILE: $Id$ */
/**
* Short description for file.
*
* Long description for file
*
* PHP versions 4 and 5
*
* CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite>
* Copyright 2005-2007, Cake Software Foundation, Inc.
* 1785 E. Sahara Avenue, Suite 490-204
* Las Vegas, Nevada 89104
*
* Licensed under The Open Group Test Suite License
* Redistributions of files must retain the above copyright notice.
*
* @filesource
* @copyright Copyright 2005-2007, Cake Software Foundation, Inc.
* @link https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests
* @package cake.tests
* @subpackage cake.tests.cases.libs.model.behaviors
* @since CakePHP(tm) v 1.2.0.5669
* @version $Revision$
* @modifiedby $LastChangedBy$
* @lastmodified $Date$
* @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
*/
if (!defined('CAKEPHP_UNIT_TEST_EXECUTION')) {
define('CAKEPHP_UNIT_TEST_EXECUTION', 1);
}
/**
* Short description for class.
*
* @package cake.tests
* @subpackage cake.tests.cases.libs.model.behaviors
*/
class TranslateTestModel extends CakeTestModel {
var $name = 'TranslateTestModel';
var $useTable = 'i18n';
var $displayField = 'field';
}
/**
* Short description for class.
*
* @package cake.tests
* @subpackage cake.tests.cases.libs.model.behaviors
*/
class TranslatedItem extends CakeTestModel {
var $name = 'TranslatedItem';
var $cacheQueries = false;
var $actsAs = array('Translate' => array('content', 'title'));
var $translateModel = 'TranslateTestModel';
}
/**
* Short description for class.
*
* @package cake.tests
* @subpackage cake.tests.cases.libs.model.behaviors
*/
class TranslateTest extends CakeTestCase {
var $fixtures = array('core.translated_item', 'core.translate');
var $Model = null;
function startCase() {
$this->db->fullDebug = true;
$this->Model =& new TranslatedItem();
$this->I18nModel =& ClassRegistry::getObject('TranslateTestModel');
}
function testLocaleFalsePlain() {
$this->Model->locale = false;
$result = $this->Model->read(null, 1);
$expected = array('TranslatedItem' => array('id' => 1, 'slug' => 'first_translated'));
$this->assertEqual($result, $expected);
$result = $this->Model->findAll(null, array('slug'));
$expected = array(
array('TranslatedItem' => array('slug' => 'first_translated')),
array('TranslatedItem' => array('slug' => 'second_translated')),
array('TranslatedItem' => array('slug' => 'third_translated')));
$this->assertEqual($result, $expected);
}
function testLocaleFalseAssociations() {
$this->Model->locale = false;
$this->Model->unbindTranslation();
$translations = array('title' => 'Title', 'content' => 'Content');
$this->Model->bindTranslation($translations, false);
$result = $this->Model->read(null, 1);
$expected = array(
'TranslatedItem' => array('id' => 1, 'slug' => 'first_translated'),
'Title' => array(
array('id' => 1, 'locale' => 'eng', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'title', 'content' => 'Title #1'),
array('id' => 3, 'locale' => 'deu', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'title', 'content' => 'Titel #1'),
array('id' => 5, 'locale' => 'cze', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'title', 'content' => 'Titulek #1')),
'Content' => array(
array('id' => 2, 'locale' => 'eng', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'content', 'content' => 'Content #1'),
array('id' => 4, 'locale' => 'deu', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'content', 'content' => 'Inhalt #1'),
array('id' => 6, 'locale' => 'cze', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'content', 'content' => 'Obsah #1')));
$this->assertEqual($result, $expected);
$this->Model->hasMany['Title']['fields'] = $this->Model->hasMany['Content']['fields'] = array('content');
$this->Model->hasMany['Title']['conditions']['locale'] = $this->Model->hasMany['Content']['conditions']['locale'] = 'eng';
$result = $this->Model->findAll(null, array('TranslatedItem.slug'));
$expected = array(
array('TranslatedItem' => array('id' => 1, 'slug' => 'first_translated'),
'Title' => array(array('foreign_key' => 1, 'content' => 'Title #1')),
'Content' => array(array('foreign_key' => 1, 'content' => 'Content #1'))),
array('TranslatedItem' => array('id' => 2, 'slug' => 'second_translated'),
'Title' => array(array('foreign_key' => 2, 'content' => 'Title #2')),
'Content' => array(array('foreign_key' => 2, 'content' => 'Content #2'))),
array('TranslatedItem' => array('id' => 3, 'slug' => 'third_translated'),
'Title' => array(array('foreign_key' => 3, 'content' => 'Title #3')),
'Content' => array(array('foreign_key' => 3, 'content' => 'Content #3'))));
$this->assertEqual($result, $expected);
$this->Model->hasMany['Title']['fields'] = $this->Model->hasMany['Content']['fields'] = '';
unset($this->Model->hasMany['Title']['conditions']['locale']);
unset($this->Model->hasMany['Content']['conditions']['locale']);
$this->Model->unbindTranslation($translations);
$this->Model->bindTranslation(null, false);
}
function testLocaleSingle() {
$this->Model->locale = 'eng';
$result = $this->Model->read(null, 1);
$expected = array('TranslatedItem' => array(
'id' => 1,
'slug' => 'first_translated',
'locale' => 'eng',
'title' => 'Title #1',
'content' => 'Content #1'));
$this->assertEqual($result, $expected);
$result = $this->Model->findAll();
$expected = array(
array('TranslatedItem' => array(
'id' => 1,
'slug' => 'first_translated',
'locale' => 'eng',
'title' => 'Title #1',
'content' => 'Content #1')),
array('TranslatedItem' => array(
'id' => 2,
'slug' => 'second_translated',
'locale' => 'eng',
'title' => 'Title #2',
'content' => 'Content #2')),
array('TranslatedItem' => array(
'id' => 3,
'slug' => 'third_translated',
'locale' => 'eng',
'title' => 'Title #3',
'content' => 'Content #3')));
$this->assertEqual($result, $expected);
}
function testLocaleSingleAssociations() {
$this->Model->locale = 'eng';
$this->Model->unbindTranslation();
$translations = array('title' => 'Title', 'content' => 'Content');
$this->Model->bindTranslation($translations, false);
$result = $this->Model->read(null, 1);
$expected = array(
'TranslatedItem' => array(
'id' => 1,
'slug' => 'first_translated',
'locale' => 'eng',
'title' => 'Title #1',
'content' => 'Content #1'),
'Title' => array(
array('id' => 1, 'locale' => 'eng', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'title', 'content' => 'Title #1'),
array('id' => 3, 'locale' => 'deu', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'title', 'content' => 'Titel #1'),
array('id' => 5, 'locale' => 'cze', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'title', 'content' => 'Titulek #1')),
'Content' => array(
array('id' => 2, 'locale' => 'eng', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'content', 'content' => 'Content #1'),
array('id' => 4, 'locale' => 'deu', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'content', 'content' => 'Inhalt #1'),
array('id' => 6, 'locale' => 'cze', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'content', 'content' => 'Obsah #1')));
$this->assertEqual($result, $expected);
$this->Model->hasMany['Title']['fields'] = $this->Model->hasMany['Content']['fields'] = array('content');
$this->Model->hasMany['Title']['conditions']['locale'] = $this->Model->hasMany['Content']['conditions']['locale'] = 'eng';
$result = $this->Model->findAll(null, array('TranslatedItem.title'));
$expected = array(
array('TranslatedItem' => array('id' => 1, 'locale' => 'eng', 'title' => 'Title #1'),
'Title' => array(array('foreign_key' => 1, 'content' => 'Title #1')),
'Content' => array(array('foreign_key' => 1, 'content' => 'Content #1'))),
array('TranslatedItem' => array('id' => 2, 'locale' => 'eng', 'title' => 'Title #2'),
'Title' => array(array('foreign_key' => 2, 'content' => 'Title #2')),
'Content' => array(array('foreign_key' => 2, 'content' => 'Content #2'))),
array('TranslatedItem' => array('id' => 3, 'locale' => 'eng', 'title' => 'Title #3'),
'Title' => array(array('foreign_key' => 3, 'content' => 'Title #3')),
'Content' => array(array('foreign_key' => 3, 'content' => 'Content #3'))));
$this->assertEqual($result, $expected);
$this->Model->hasMany['Title']['fields'] = $this->Model->hasMany['Content']['fields'] = '';
unset($this->Model->hasMany['Title']['conditions']['locale']);
unset($this->Model->hasMany['Content']['conditions']['locale']);
$this->Model->unbindTranslation($translations);
$this->Model->bindTranslation(array('title', 'content'), false);
}
function testLocaleMultiple() {
$this->Model->locale = array('deu', 'eng', 'cze');
$delete = array(
array('locale' => 'deu'),
array('foreign_key' => 1, 'field' => 'title', 'locale' => 'eng'),
array('foreign_key' => 1, 'field' => 'content', 'locale' => 'cze'),
array('foreign_key' => 2, 'field' => 'title', 'locale' => 'cze'),
array('foreign_key' => 2, 'field' => 'content', 'locale' => 'eng'),
array('foreign_key' => 3, 'field' => 'title'));
$this->I18nModel->deleteAll(array('or' => $delete));
$result = $this->Model->read(null, 1);
$expected = array(
'TranslatedItem' => array(
'id' => 1,
'slug' => 'first_translated',
'locale' => 'deu',
'title' => 'Titulek #1',
'content' => 'Content #1'));
$this->assertEqual($result, $expected);
$result = $this->Model->findAll(null, array('slug', 'title', 'content'));
$expected = array(
array('TranslatedItem' => array(
'slug' => 'first_translated',
'locale' => 'deu',
'title' => 'Titulek #1',
'content' => 'Content #1')),
array('TranslatedItem' => array(
'slug' => 'second_translated',
'locale' => 'deu',
'title' => 'Title #2',
'content' => 'Obsah #2')),
array('TranslatedItem' => array(
'slug' => 'third_translated',
'locale' => 'deu',
'title' => '',
'content' => 'Content #3')));
$this->assertEqual($result, $expected);
}
function testReadSelectedFields() {
$this->Model->locale = 'eng';
$result = $this->Model->findAll(null, array('slug', 'TranslatedItem.content'));
$expected = array(
array('TranslatedItem' => array('slug' => 'first_translated', 'locale' => 'eng', 'content' => 'Content #1')),
array('TranslatedItem' => array('slug' => 'second_translated', 'locale' => 'eng', 'content' => 'Content #2')),
array('TranslatedItem' => array('slug' => 'third_translated', 'locale' => 'eng', 'content' => 'Content #3')));
$this->assertEqual($result, $expected);
$result = $this->Model->findAll(null, array('TranslatedItem.slug', 'content'));
$this->assertEqual($result, $expected);
$this->Model->locale = array('eng', 'deu', 'cze');
$delete = array(array('locale' => 'deu'), array('field' => 'content', 'locale' => 'eng'));
$this->I18nModel->deleteAll(array('or' => $delete));
$result = $this->Model->findAll(null, array('title', 'content'));
$expected = array(
array('TranslatedItem' => array('locale' => 'eng', 'title' => 'Title #1', 'content' => 'Obsah #1')),
array('TranslatedItem' => array('locale' => 'eng', 'title' => 'Title #2', 'content' => 'Obsah #2')),
array('TranslatedItem' => array('locale' => 'eng', 'title' => 'Title #3', 'content' => 'Obsah #3')));
$this->assertEqual($result, $expected);
}
function testSaveCreate() {
$this->Model->locale = 'spa';
$data = array('slug' => 'fourth_translated', 'title' => 'Leyenda #4', 'content' => 'Contenido #4');
$this->Model->create($data);
$this->Model->save();
$result = $this->Model->read();
$expected = array('TranslatedItem' => am($data, array('id' => $this->Model->id, 'locale' => 'spa')));
$this->assertEqual($result, $expected);
}
function testSaveUpdate() {
$this->Model->locale = 'spa';
$oldData = array('slug' => 'fourth_translated', 'title' => 'Leyenda #4');
$this->Model->create($oldData);
$this->Model->save();
$id = $this->Model->id;
$newData = array('id' => $id, 'content' => 'Contenido #4');
$this->Model->create($newData);
$this->Model->save();
$result = $this->Model->read(null, $id);
$expected = array('TranslatedItem' => am($oldData, $newData, array('locale' => 'spa')));
$this->assertEqual($result, $expected);
}
}
?>

View file

@ -0,0 +1,65 @@
<?php
/* SVN FILE: $Id$ */
/**
* Short description for file.
*
* Long description for file
*
* PHP versions 4 and 5
*
* CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite>
* Copyright 2005-2007, Cake Software Foundation, Inc.
* 1785 E. Sahara Avenue, Suite 490-204
* Las Vegas, Nevada 89104
*
* Licensed under The Open Group Test Suite License
* Redistributions of files must retain the above copyright notice.
*
* @filesource
* @copyright Copyright 2005-2007, Cake Software Foundation, Inc.
* @link https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests
* @package cake.tests
* @subpackage cake.tests.fixtures
* @since CakePHP(tm) v 1.2.0.5669
* @version $Revision$
* @modifiedby $LastChangedBy$
* @lastmodified $Date$
* @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
*/
/**
* Short description for class.
*
* @package cake.tests
* @subpackage cake.tests.fixtures
*/
class TranslateFixture extends CakeTestFixture {
var $name = 'Translate';
var $table = 'i18n';
var $fields = array(
'id' => array('type' => 'integer', 'key' => 'primary', 'extra'=> 'auto_increment'),
'locale' => array('type' => 'string', 'length' => 6, 'null' => false),
'model' => array('type' => 'string', 'null' => false),
'foreign_key' => array('type' => 'integer', 'null' => false),
'field' => array('type' => 'string', 'null' => false),
'content' => array('type' => 'text'));
var $records = array(
array('id' => 1, 'locale' => 'eng', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'title', 'content' => 'Title #1'),
array('id' => 2, 'locale' => 'eng', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'content', 'content' => 'Content #1'),
array('id' => 3, 'locale' => 'deu', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'title', 'content' => 'Titel #1'),
array('id' => 4, 'locale' => 'deu', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'content', 'content' => 'Inhalt #1'),
array('id' => 5, 'locale' => 'cze', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'title', 'content' => 'Titulek #1'),
array('id' => 6, 'locale' => 'cze', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'content', 'content' => 'Obsah #1'),
array('id' => 7, 'locale' => 'eng', 'model' => 'TranslatedItem', 'foreign_key' => 2, 'field' => 'title', 'content' => 'Title #2'),
array('id' => 8, 'locale' => 'eng', 'model' => 'TranslatedItem', 'foreign_key' => 2, 'field' => 'content', 'content' => 'Content #2'),
array('id' => 9, 'locale' => 'deu', 'model' => 'TranslatedItem', 'foreign_key' => 2, 'field' => 'title', 'content' => 'Titel #2'),
array('id' => 10, 'locale' => 'deu', 'model' => 'TranslatedItem', 'foreign_key' => 2, 'field' => 'content', 'content' => 'Inhalt #2'),
array('id' => 11, 'locale' => 'cze', 'model' => 'TranslatedItem', 'foreign_key' => 2, 'field' => 'title', 'content' => 'Titulek #2'),
array('id' => 12, 'locale' => 'cze', 'model' => 'TranslatedItem', 'foreign_key' => 2, 'field' => 'content', 'content' => 'Obsah #2'),
array('id' => 13, 'locale' => 'eng', 'model' => 'TranslatedItem', 'foreign_key' => 3, 'field' => 'title', 'content' => 'Title #3'),
array('id' => 14, 'locale' => 'eng', 'model' => 'TranslatedItem', 'foreign_key' => 3, 'field' => 'content', 'content' => 'Content #3'),
array('id' => 15, 'locale' => 'deu', 'model' => 'TranslatedItem', 'foreign_key' => 3, 'field' => 'title', 'content' => 'Titel #3'),
array('id' => 16, 'locale' => 'deu', 'model' => 'TranslatedItem', 'foreign_key' => 3, 'field' => 'content', 'content' => 'Inhalt #3'),
array('id' => 17, 'locale' => 'cze', 'model' => 'TranslatedItem', 'foreign_key' => 3, 'field' => 'title', 'content' => 'Titulek #3'),
array('id' => 18, 'locale' => 'cze', 'model' => 'TranslatedItem', 'foreign_key' => 3, 'field' => 'content', 'content' => 'Obsah #3'));
}
?>

View file

@ -0,0 +1,45 @@
<?php
/* SVN FILE: $Id$ */
/**
* Short description for file.
*
* Long description for file
*
* PHP versions 4 and 5
*
* CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite>
* Copyright 2005-2007, Cake Software Foundation, Inc.
* 1785 E. Sahara Avenue, Suite 490-204
* Las Vegas, Nevada 89104
*
* Licensed under The Open Group Test Suite License
* Redistributions of files must retain the above copyright notice.
*
* @filesource
* @copyright Copyright 2005-2007, Cake Software Foundation, Inc.
* @link https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests
* @package cake.tests
* @subpackage cake.tests.fixtures
* @since CakePHP(tm) v 1.2.0.5669
* @version $Revision$
* @modifiedby $LastChangedBy$
* @lastmodified $Date$
* @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
*/
/**
* Short description for class.
*
* @package cake.tests
* @subpackage cake.tests.fixtures
*/
class TranslatedItemFixture extends CakeTestFixture {
var $name = 'TranslatedItem';
var $fields = array(
'id' => array('type' => 'integer', 'key' => 'primary', 'extra'=> 'auto_increment'),
'slug' => array('type' => 'string', 'null' => false));
var $records = array(
array('id' => 1, 'slug' => 'first_translated'),
array('id' => 2, 'slug' => 'second_translated'),
array('id' => 3, 'slug' => 'third_translated'));
}
?>