我的问题很简单,但我无法弄清楚如何解决它。
我的网站是多语言的。我希望用户能够根据需要添加多种语言的文章,同时需要输入他的语言(取决于他的语言环境)。
问题是,对于CakePHP关于翻译的惯例,所有输入必须以字段的名称结尾,无论使用何种语言。因此,所有字段对同一字段具有相同的规则。我不能说一个名字"需要另一种语言时不需要另一种语言。
例如,默认语言的输入将是:
<input type="text" name="name" required="required" maxlength="45" id="name">
在此之下,同一领域的另一种语言输入:
<input type="text" name="locales[fr_CA][name]" required="required" maxlength="45" id="locales-fr-ca-name">
&#34; required&#34;由于以下规则,属性会自动添加到两者:
$validator
->requirePresence('name', 'create')
->notEmpty('name')
->add('name', [
'length' => [
'rule' => ['minLength', 10],
'message' => 'The title needs to be at least 10 characters long.',
]
]);
注意:当我保存时,我必须将语言环境更改为默认语言(en_US),以便能够以多种语言保存+默认语言(否则默认输入将保存在默认表格和i18n表格中)。
if ($this->request->is('post')) {
I18n::locale('en_US');
// ......
编辑:所以这是我保存时的完整代码(IngredientsController.php)
public function add() {
$ingredient = $this->Ingredients->newEntity();
if ($this->request->is('post')) {
$ingredient = $this->Ingredients->patchEntity($ingredient, $this->request->data);
if(isset($this->request->data['locales'])) {
foreach ($this->request->data['locales'] as $lang => $data) {
$ingredient->translation($lang)->set($data, ['guard' => false]);
}
}
$locale = I18n::locale(); // At this point the locale is fr_CA (not de default)
I18n::locale('en_US'); // Change the locale to the default
if ($this->Ingredients->save($ingredient)) {
$this->Flash->success(__('The ingredient has been saved.'));
I18n::locale($locale); // Put the locale back to the user's locale
return $this->redirect(['action' => 'index']);
} else {
I18n::locale($locale);
$this->Flash->error(__('The ingredient could not be saved. Please, try again.'));
}
}
$this->set(compact('ingredient'));
$this->set('_serialize', ['ingredient']);
}
我设置的默认语言环境是bootstrap.php
/**
* Set the default locale. This controls how dates, number and currency is
* formatted and sets the default language to use for translations.
*/
ini_set('intl.default_locale', 'en_US');
Configure::write('Config.locales', ['fr_CA']);
我在AppController.php中确定了用户的语言环境
public function beforeFilter(Event $event)
{
$locales = Configure::read('Config.locales');
$boom = explode(',', str_replace('-', '_', $_SERVER['HTTP_ACCEPT_LANGUAGE']));
$user_lang = substr($boom[0], 0, 2);
// This piece of code is only to change the locale to fr_CA even if the user's language is just fr or fr_FR
if(in_array($user_lang, Configure::read('Config.langs'))) {
if(in_array($boom[0], $locales)) {
I18n::locale($boom[0]);
} else {
foreach ($locales as $locale) {
if(substr($locale, 0, 2) == $user_lang) {
I18n::locale($locale);
}
}
}
}
$this->set('locales', $locales);
$this->set('locale', I18n::locale());
}
因此,如果我在与默认语言环境不同的语言环境中保存,则相同的默认输入将保存在成分表和fr_CA中的i18n表中
答案 0 :(得分:1)
如果默认语言环境已被更改,默认语言的输入存储在转换表中这一事实似乎是预期的行为,就像读取数据一样,它将检索与数据相关的数据。当前区域设置,保存数据时同样适用。
<强> Cookbook > Database Access & ORM > Behaviours > Translate > Saving in Another Language 强>
将语言环境更改为默认语言是一种解决方法,但它可能有点过于干扰,因为它会干扰使用该值检查当前语言环境的任何代码。最好直接在表格上设置所需的区域设置
$Ingredients->locale(I18n::defaultLocale());
或者,主要实体上的侵入性最小的选项,而不是
$ingredient->_locale = I18n::defaultLocale();
前者是链接的文档序列描述的内容,但实际上没有显示,需要修复。
虽然我可以看到为什么表单助手(分别是实体上下文)为“错误”字段选择验证规则,即xyz.name
字段为name
字段选择那些,我可以'告诉我这是否是如何工作的。
由于它不会发现嵌套错误,我猜这是预期的行为,但我不确定,所以我建议create an issue over at GitHub进行澄清。在任何情况下,有多种方法可以解决此问题,例如重命名字段,或将required
选项设置为false
。
echo $this->Form->input('locales.fr_CA.name', [
// ...
'required' => false
]);
在您的示例中,这几乎只是一个前端问题,因为字段不会在服务器端实际验证。
另一种选择是使用自定义翻译表类,具有特定于翻译的验证,实际应用于使用的字段,但是这可能不是 可取的,除非您实际想要应用任何完全验证。
为了完成,我们也将介绍验证/应用规则。
为了实际应用验证和/或应用程序规则,并在表单中识别它们,您将使用包含规则的自定义转换表类,并且您必须使用转换行为使用的实际属性名称对于hasMany
关联的翻译表,即_i18n
。
这是一个例子。
<强>的src /型号/表/ IngredientsI18nTable.php 强>
namespace App\Model\Table;
use Cake\Datasource\EntityInterface;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;
class IngredientsI18nTable extends Table
{
public function initialize(array $config) {
$this->entityClass('Ingredient');
$this->table('i18n');
$this->displayField('id');
$this->primaryKey('id');
}
public function validationDefault(Validator $validator) {
$validator
->allowEmpty('name')
->add('name', 'valid', [
'rule' => function ($value, $context) {
return false;
}
]);
return $validator;
}
public function buildRules(RulesChecker $rules)
{
$rules->add(
function (EntityInterface $entity, $options) {
return false;
},
'i18nName',
[
'errorField' => 'name'
]
);
return $rules;
}
}
<强> IngredientsTable 强>
public function initialize(array $config) {
// ...
$this->addBehavior('Translate', [
// ...
'translationTable' => 'IngredientsI18n'
]);
}
查看模板
echo $this->Form->hidden('_i18n.0.locale', ['value' => 'fr_FR']);
echo $this->Form->input('_i18n.0.name');
echo $this->Form->hidden('_i18n.1.locale', ['value' => 'da_DK']);
echo $this->Form->input('_i18n.1.name');
// ...
现在字段将选择正确的验证器,因此不会被标记为必需。在创建/修补实体时也会应用验证,最后也会应用应用程序规则。但是我无法保证这没有任何副作用,因为内部的翻译行为似乎没有考虑到_i18n
属性已在外部设置的情况!
此外,您仍然需要使用translations()
在实体上设置翻译,以便正确保存翻译!
foreach ($this->request->data['_i18n'] as $translation) {
$ingredient->translation($translation['locale'])->set('name', $translation['name']);
}