CakePhp TranslateBehavior, проверка и сохранение нескольких локалей

Контекст: я хочу создать веб-приложение с помощью CakePhp, которое должно быть переводимым. Я хочу сохранить несколько переводов для одного и того же поля в одной форме.

Проблема. Я испробовал дюжину способов заставить это работать, и мне это удалось. Но в итоге я использовал два пользовательских 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'
      ),
    ),
  );
}
?>

ПРИМЕЧАНИЕ. На эту тему не так много информации... У меня есть еще много вопросов о поведении перевода, таких как получение рекурсивных результатов также в правильном языковом стандарте... Кто-нибудь знает хороший учебник или источник информации (кулинарная книга весьма ограничена)

Спасибо, что прочитали!!


person issem danny    schedule 24.01.2012    source источник


Ответы (1)


Похоже, вы создаете своего рода CRM, которая позволяет пользователям устанавливать контент, который считывается на сайте, на основе установленного ими языка. Я бы использовал встроенные i18n и l10n. . Это делает его действительно простым, но, вероятно, это не решение для динамического контента.

Сказав это, единственный другой способ, который я могу придумать, очень утомителен. Я бы построил один экран с выпадающим идентификатором языка. Поэтому вместо того, чтобы пытаться втиснуть ВСЕ языки на один и тот же экран с тестовым полем для каждого языка, я бы создал одну форму, а затем использовал раскрывающийся список для языка.

Ваша модель использует столбец для определения языка, которому принадлежит строка. Созданная вами форма выражает все языки в одной строке. Итак, если бы вы просмотрели страницу индекса, показывающую записи, вы, конечно же, увидели бы:

title 1 eng
title 1 dut
title 1 fre
title 2 eng
title 2 dut
title 2 fre
...

Более того, если вы когда-нибудь добавите новый язык, вам придется изменить проверку в модели и форме.

Однако, если вы настроены на это, измените | к _ и вперед. Но тогда вам нужно будет хранить все данные в одной записи. Итак, когда вы посмотрите на индекс записей, вы увидите:

title 1 end dut fre
title 2 end dut fre
...

Мой совет:

1) Использовать встроенный i18n/l10n с использованием файлов .po/.pot.

2) Если контент будет часто меняться и его необходимо хранить в базе данных, чтобы его можно было легко изменять/обновлять на лету, используйте раскрывающийся список.

Language: dropdown
Title: text_field
person Chuck Burgess    schedule 27.01.2012
comment
Спасибо, я уже использую файлы .po для нединамического контента. И да, использование ввода выбора для языка является обычным способом сделать это, но поскольку есть только два поля, требующие перевода, и они всегда нуждаются в переводе, я настроен сделать это в одной форме. Я предполагаю, что использование двух пользовательских запросов не такая уж большая жертва... Просто интересно, есть ли лучшее решение. Я ожидал большего от поведения Translate. - person issem danny; 18.02.2012