CakePhp TranslateBehavior, проверить и сохранить несколько локалей - PullRequest
1 голос
/ 24 января 2012

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

Проблема: Я попробовал дюжину способов заставить это работать, и я сделал. Но в итоге я использовал два пользовательских SQL-запроса, которые на самом деле не похожи на решение cakePhp.

Вопрос: Кто-нибудь знает лучший способ достичь того же результата?

Что я пробовал:

  • Присвоение полям формы имени, подобного 'Model.fieldName.locale', что дает ему правильный формат в имени атрибута элемента ввода, но тогда моя проверка не распознает имя поля. Но сохранение работает.

  • Присвоение полям формы имени типа 'modelLocale' и передача имени attr 'data [Model] [field] [locale]', в этом случае проверка работоспособности выполняется за исключением isUnique, но сохранение в базе данных не не работает.

  • Больше вариантов, но упоминать не стоит.

Я добавлю свой вид и модель ниже: (если вы хотите увидеть больше кода или вам нужна дополнительная информация, просто спросите)

/ App / View / Категория / 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 / модель / 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 / модель / 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'
      ),
    ),
  );
}
?>

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

Спасибо за чтение !!

1 Ответ

1 голос
/ 27 января 2012

Похоже, вы создаете своего рода 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
...