CakePhp TranslateBehavior,验证并保存多个语言环境
上下文: 我想使用 CakePhp 创建一个应该可翻译的 Web 应用程序。我想以一种形式保存同一字段的多个翻译。
问题: 我尝试了十几种方法来让它发挥作用,而且我做到了。但我最终使用了两个自定义 SQL 查询,这真的感觉不像 cakePhp 解决方案。
问题: 有谁知道更好的方法来达到相同的结果?
我尝试过的:
为表单字段指定一个类似“Model.fieldName.locale”的名称,这在输入元素的名称属性中给出了正确的格式,但我的验证却没有识别字段名称。但保存有效。
为表单字段指定“modelLocale”之类的名称,并传入名称 attr“data[Model][field][locale]”,在这种情况下,除了 isUnique 之外,验证有效,但保存到数据库不起作用.
此的更多变体,但不值得一提。
我将在下面添加我的视图和模型:(如果您想查看更多代码或需要更多信息,请询问)
/App/View/Category/add.ctp
<?php echo $this->Form->create(); ?>
<?php echo $this->Form->input('title|dut'); ?>
<?php echo $this->Form->input('title|eng'); ?>
<?php echo $this->Form->input('title|fre'); ?>
<?php echo $this->Form->input('description|dut', array('type'=>'textarea')); ?>
<?php echo $this->Form->input('description|eng', array('type'=>'textarea')); ?>
<?php echo $this->Form->input('description|fre', array('type'=>'textarea')); ?>
<?php echo $this->Form->end('add'); ?>
/App/Model/ AppModel.php
<?php
App::uses('Model', 'Model');
class AppModel extends Model {
/**
* Check Unique
*
* Searches the i18n table to determine wetter a field is unique or not.
* Expects field name to be as following: "fieldname|locale".
*
* @param array $data The data of the field, automatically passed trough by cakePhp.
* @param string $field The name of the field, which should match the one in the view.
* @returns boolean
*/
public function checkUnique($data, $field) {
// Seperate the field key and locale which are seperated by "|".
$a = preg_split('/[|]/', $field, 2);
// If field key and locale are found...
if (is_array($a) || count($a) === 2) {
$q = sprintf("SELECT * FROM i18n WHERE i18n.locale = '%s' AND i18n.model = '%s' AND i18n.field = '%s' AND i18n.content = '%s' LIMIT 1",
Sanitize::escape($a[1]),
Sanitize::escape(strtolower($this->name)),
Sanitize::escape($a[0]),
Sanitize::escape($data[$field])
);
if ($this->query($q)) {
return false;
}
return true;
}
}
/**
* Setup Translation
*
* Loops trough the fields. If a field is translatable
* (which it will know by it's structure [fieldname]|[locale])
* and has the default locale. Then it's value will be stored
* in the array where cake expects it
* (data[Model][fieldname] instead of data[Model][fieldname|defaultLocale])
* so that cake will save it to the database.
*
* In the afterSave method the translations will be saved, for then we know
* the lastInsertId which is also the foreign_key of the i18n table.
*/
public function _setupTranslations() {
foreach($this->data[$this->name] as $key => $value) {
$a = preg_split('/[|]/', $key, 2);
if (is_array($a) && count($a) === 2) {
$languages = Configure::read('Config.languages');
if ($a[1] === $languages[Configure::read('Config.defaultLanguage')]['locale']) {
$this->data[$this->name][$a[0]] = $value;
}
}
}
}
/**
* Save Translations
*
* Saves the translations to the i18n database.
* Expects form fields with translations to have
* following structure: [fieldname]|[locale] (ex. title|eng, title|fre, ...).
*/
public function _saveTranslations() {
foreach($this->data[$this->name] as $key => $value) {
$a = preg_split('/[|]/', $key, 2);
if (is_array($a) && count($a) === 2) {
$q = sprintf("INSERT INTO i18n (locale, model, foreign_key, field, content) VALUES ('%s', '%s', '%s', '%s', '%s')",
Sanitize::escape($a[1]),
Sanitize::escape(strtolower($this->name)),
Sanitize::escape($this->id),
Sanitize::escape($a[0]),
Sanitize::escape($value)
);
$this->query($q);
}
}
}
/**
* Before Save
*/
public function beforeSave() {
$this->_setupTranslations();
return true;
}
/**
* After Save
*/
public function afterSave() {
$this->_saveTranslations();
return true;
}
}
/App/Model/Category.php
<?php
class Category extends AppModel {
public $name = 'Category';
public $hasMany = array(
'Item'=>array(
'className'=>'Item',
'foreignKey'=>'category_id',
'order'=>'Item.title ASC'
)
);
var $actsAs = array(
'Translate'=>array(
'title',
'description'
)
);
public $validate = array(
'title|dut'=>array(
'required'=>array(
'rule'=>'notEmpty',
'message'=>'Veld verplicht'
),
'unique'=>array(
'rule'=>array('checkUnique', 'title|dut'),
'message'=>'Titel reeds in gebruik'
),
),
'title|eng'=>array(
'required'=>array(
'rule'=>'notEmpty',
'message'=>'Veld verplicht'
),
'unique'=>array(
'rule'=>array('checkUnique', 'title|eng'),
'message'=>'Titel reeds in gebruik'
),
),
'title|fre'=>array(
'required'=>array(
'rule'=>'notEmpty',
'message'=>'Veld verplicht'
),
'unique'=>array(
'rule'=>array('checkUnique', 'title|fre'),
'message'=>'Titel reeds in gebruik'
),
),
);
}
?>
注意: 关于这个主题的信息并不多...我有很多有关翻译行为的更多问题,例如获取递归结果也在正确的区域设置中,...任何人都知道好的图解或信息来源(食谱非常有限)
感谢阅读!!
Context:
I Want to create a web application using CakePhp which should be translatable. I want to save multiple translations for the same field in one form.
Problem:
I've tried a dozen ways to get this to work and I did. But I ended up using two custom SQL queries which really doesn't feel like a cakePhp solution.
Question:
Does anybody know a better way to achieve the same result?
What I tried:
Giving the form fields a name like 'Model.fieldName.locale', which gives it the right format in the name attr of the input element but then my validation doesn't recognize the field name. But saving works.
Giving the form fields a name like 'modelLocale' and pass in a name attr 'data[Model][field][locale]', in this case the validation works exept for isUnique but saving to the database doesn't work.
More variations of this but not worth mentioning.
I'll add my view and model below: (if u want to see more code or need more info just ask)
/App/View/Category/add.ctp
<?php echo $this->Form->create(); ?>
<?php echo $this->Form->input('title|dut'); ?>
<?php echo $this->Form->input('title|eng'); ?>
<?php echo $this->Form->input('title|fre'); ?>
<?php echo $this->Form->input('description|dut', array('type'=>'textarea')); ?>
<?php echo $this->Form->input('description|eng', array('type'=>'textarea')); ?>
<?php echo $this->Form->input('description|fre', array('type'=>'textarea')); ?>
<?php echo $this->Form->end('add'); ?>
/App/Model/AppModel.php
<?php
App::uses('Model', 'Model');
class AppModel extends Model {
/**
* Check Unique
*
* Searches the i18n table to determine wetter a field is unique or not.
* Expects field name to be as following: "fieldname|locale".
*
* @param array $data The data of the field, automatically passed trough by cakePhp.
* @param string $field The name of the field, which should match the one in the view.
* @returns boolean
*/
public function checkUnique($data, $field) {
// Seperate the field key and locale which are seperated by "|".
$a = preg_split('/[|]/', $field, 2);
// If field key and locale are found...
if (is_array($a) || count($a) === 2) {
$q = sprintf("SELECT * FROM i18n WHERE i18n.locale = '%s' AND i18n.model = '%s' AND i18n.field = '%s' AND i18n.content = '%s' LIMIT 1",
Sanitize::escape($a[1]),
Sanitize::escape(strtolower($this->name)),
Sanitize::escape($a[0]),
Sanitize::escape($data[$field])
);
if ($this->query($q)) {
return false;
}
return true;
}
}
/**
* Setup Translation
*
* Loops trough the fields. If a field is translatable
* (which it will know by it's structure [fieldname]|[locale])
* and has the default locale. Then it's value will be stored
* in the array where cake expects it
* (data[Model][fieldname] instead of data[Model][fieldname|defaultLocale])
* so that cake will save it to the database.
*
* In the afterSave method the translations will be saved, for then we know
* the lastInsertId which is also the foreign_key of the i18n table.
*/
public function _setupTranslations() {
foreach($this->data[$this->name] as $key => $value) {
$a = preg_split('/[|]/', $key, 2);
if (is_array($a) && count($a) === 2) {
$languages = Configure::read('Config.languages');
if ($a[1] === $languages[Configure::read('Config.defaultLanguage')]['locale']) {
$this->data[$this->name][$a[0]] = $value;
}
}
}
}
/**
* Save Translations
*
* Saves the translations to the i18n database.
* Expects form fields with translations to have
* following structure: [fieldname]|[locale] (ex. title|eng, title|fre, ...).
*/
public function _saveTranslations() {
foreach($this->data[$this->name] as $key => $value) {
$a = preg_split('/[|]/', $key, 2);
if (is_array($a) && count($a) === 2) {
$q = sprintf("INSERT INTO i18n (locale, model, foreign_key, field, content) VALUES ('%s', '%s', '%s', '%s', '%s')",
Sanitize::escape($a[1]),
Sanitize::escape(strtolower($this->name)),
Sanitize::escape($this->id),
Sanitize::escape($a[0]),
Sanitize::escape($value)
);
$this->query($q);
}
}
}
/**
* Before Save
*/
public function beforeSave() {
$this->_setupTranslations();
return true;
}
/**
* After Save
*/
public function afterSave() {
$this->_saveTranslations();
return true;
}
}
/App/Model/Category.php
<?php
class Category extends AppModel {
public $name = 'Category';
public $hasMany = array(
'Item'=>array(
'className'=>'Item',
'foreignKey'=>'category_id',
'order'=>'Item.title ASC'
)
);
var $actsAs = array(
'Translate'=>array(
'title',
'description'
)
);
public $validate = array(
'title|dut'=>array(
'required'=>array(
'rule'=>'notEmpty',
'message'=>'Veld verplicht'
),
'unique'=>array(
'rule'=>array('checkUnique', 'title|dut'),
'message'=>'Titel reeds in gebruik'
),
),
'title|eng'=>array(
'required'=>array(
'rule'=>'notEmpty',
'message'=>'Veld verplicht'
),
'unique'=>array(
'rule'=>array('checkUnique', 'title|eng'),
'message'=>'Titel reeds in gebruik'
),
),
'title|fre'=>array(
'required'=>array(
'rule'=>'notEmpty',
'message'=>'Veld verplicht'
),
'unique'=>array(
'rule'=>array('checkUnique', 'title|fre'),
'message'=>'Titel reeds in gebruik'
),
),
);
}
?>
NOTE: There isn't that much information out there on this subject... I have a lot more questions about the translation behavior like getting the recursive results also in the correct locale, ... Anybody know a good tut or source of info (cookbook is quite limited)
Thanks for reading!!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
看来您可能正在构建某种 CRM,允许用户根据他们设置的语言建立读取到网站中的内容。我会使用内置的 i18n 和 l10n 。它使它变得非常简单,但这可能不是动态内容的解决方案。
话虽如此,我能想到的唯一其他方法是非常乏味的。我将构建一个带有语言标识符下拉菜单的屏幕。因此,我不会尝试将所有语言塞满在同一个屏幕中,并为每种语言设置一个测试框,而是创建一个表单,然后使用该语言的下拉菜单。
您的模型使用列来定义行所属的语言。您创建的表格在一行中表达了所有语言。因此,如果您要查看显示记录的索引页面,您当然会看到:
此外,如果您要添加新语言,则必须修改模型和表单中的验证。
但是,如果您决定这样做,请更改 |到_然后就出发了。但随后您需要将所有数据存储在单个记录中。因此,当您查看记录索引时,您将看到:
我的建议:
1) 使用 .po / .pot 文件使用内置 i18n / l10n。
2)如果内容会经常更改,并且需要存储在数据库中,以便可以轻松地动态更改/频繁更新,则使用下拉菜单。
It appears you may be building a CRM of sorts that allows the users to establish content that is read into the site based on the language they have set. I would use the built in i18n and l10n. It makes it really simple, but this is probably not a solution for dynamic content.
Having said that, the only other way I can think of doing this is very tedious. I would build a single screen with a language identifier drop down. So instead of trying to cram ALL languages in the same screen with a test box for each language, I would create one form and then use a drop down for the language.
Your model is using a column to define with language the row belongs to. The form you have created is expressing all languages in a single row. So if you were to view the Index page showing the records, of course you would see:
Further more, if you were ever to add a new language, you will have to modify the validation in the model and the form.
However, if you are set on doing it this way, change the | to _ and off you go. But then you will need to store all of the data in a single record. So when you look at the Index for the records, you will see:
My Advice:
1) Use the built in i18n / l10n using .po / .pot files.
2) If the content will be changing frequently and required to be stored in the database so it can be easily changed / updated frequently on the fly, then use a drop down.