2
votes

I'm slowly learning how to apply CakePHP's Translate Behavior and I think I've grasped the basics, but I'm working on an existing project with existing data. It's great that Cake will insert new records for me, but I need to manually (I assume) insert a few translations for some existing data. My first question:

Do I have to have a record in my i18n table for en_us if that's my default language and the language that's used in my core table? For example, if categories.name = 'Entertainment', do I really need to create a "duplicate" record in i18n to indicate to specify the English translation of "Entertainment"? Empirical evidence seems to say yes, but that seems like a lot of extra work so I'm hoping that someone can tell me that I'm missing something.

Second, is there an "easy" way to get translation records into the database? As languages are added over the course of a project, it's going to be really painful to go back and create i18n records for legacy data.

Thanks.

4

4 Answers

3
votes

I know this is old, but since it's unanswered and I got into some trouble figuring this out, here are my two cents:

scope, Cakephp 1.3.

first, you need an i18n record for every field -> record -> model in your app, of course only the models with the Translate behavior and only for the specified fields, so yes, you'll end up with duplicated content both in the actual model fields and the i18n table.

Also, you need existing translations for any language you specify for the find methods to return anything. This one got me confused, because I also added the translate behavior to an already developed project, with tons of data. Basically what I did was create a sync method in order to created every translation for every field for every model with the translate behavior. Should I add a new language in the future, I'll have to run it again.

here is the code for that:

$locales = array( 'spa', 'eng', 'por' );
$models = array( 'Event', 'News', 'Promotion', 'Shop', 'ShopCategory' );

foreach ( $models as $model )
{
    $this->loadModel( $model );
    $records = $this->{$model}->find( 'all', array(
                                                  'fields' => array( "*" ),
                                                  'contain' => FALSE
                                             ) );

    $fields = $this->{$model}->actsAs['Translate'];
    foreach ( $records as $record )
    {
        foreach ( $fields as $field )
        {
            foreach ( $locales as $locale )
            {
                $content = mysql_real_escape_string( $record[$model][$field] );
                $this->{$model}->query( "INSERT INTO `i18n` (`locale`, `model`, `foreign_key`, `field`, `content`) VALUES('{$locale}', '{$model}', '{$record[$model]['id']}', '{$field}', '{$content}');" );
            }
        }
    }
}

Tip: create an unique index in the i18n table with locale, model, foreign_key and field keys.

So, now that I have my original data replicated in all the languages, I need to be able to create new records with a give language. The problem is, if I create a new record in English, I should be able to update it in Spanish, but since there's no i18n record for Spanish, I cannot see the record in my CMS and I cannot edit it. The way I solved this is to create i18n records for every language when creating new records, but only edit with the current language when editing records.

I used this modification for the Translate behavior: http://bin.cakephp.org/view/1939852942

But most examples with this modification said that you have to create form inputs in the form: echo $this->Form->input('name.eng'); echo $this->Form->input('name.spa'); echo $this->Form->input('description.eng'); echo $this->Form->input('description.spa');

I find this annoying, so I basically made a helper function in app_controller to convert translatable fields to this data structure:

function _expandLocalizations( $data )
{
    foreach ( $data as $modelName => $modelData )
    {
        if ( !isset( $modelData[$this->{$modelName}->primaryKey] ) )
        {
            foreach ( $this->{$modelName}->actsAs['Translate'] as $field )
            {
                $translations = array();
                foreach ( $this->languages as $language )
                {
                    $translations[$language] = $this->data[$modelName][$field];
                }
                $data[$modelName][$field] = $translations;
            }
        }
    }
    return $data;
}

and in every add method:

if ( !empty( $this->data ) )
    {
        $this->News->set( $this->data );
        if ( $this->News->validates() )
        {
            $this->News->create();
            $data = $this->_expandLocalizations( $this->data );
            $saved = $this->News->save( $data );
            if ( $saved )
            {

This way I can use traditional forms, and I don't need to change the view if I add new languages. I need to modify the tradicional baked add method because I need the submitted data for validation, but the modified data for saving.

Finally, I followed this steps for changing the language within the site/admin: http://nuts-and-bolts-of-cakephp.com/2008/11/28/cakephp-url-based-language-switching-for-i18n-and-l10n-internationalization-and-localization/

So basically that's it. 1. You create translations for every data you already have in the DB, each copied to every language. 2. Create every translation for new records on create, all with the same value of course. 3. Edit every record in the selected translation.

0
votes

I don't have an answer for the second question, but the answer to the first seems to be "yes". After digging through the source code, it looks like the where clause of generated queries restricts the results to those where the field name being retrieved has a locale value for the current locale. That's pretty limiting.

There's an existing ticket for this (or something very similar to it).

0
votes

As for the first question the answer is no. There is no need for duplicate data even though I thought so at first as well.

I've written a blog post about it to help you lost souls out there. http://kristofferdarj.se/2012/05/cakephp-how-to-actually-use-i18n/

0
votes

Using the translate behaviour with many columns slows down your site dramatically. The simplest way I could find to speed it up was adding this index. This index is commented out in cakephp/app/Config/Schema/i18n.sql and I have no idea why.

ALTER TABLE rabotvins_i18n
ADD UNIQUE INDEX I18N_LOCALE_FIELD(locale, model, foreign_key, field);