Index: includes/common.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/common.inc,v retrieving revision 1.864 diff -u -p -b -r1.864 common.inc --- includes/common.inc 5 Feb 2009 01:21:16 -0000 1.864 +++ includes/common.inc 5 Feb 2009 02:51:52 -0000 @@ -3391,6 +3391,31 @@ function drupal_render_children(&$elemen } /** + * Replace the contents of an element with translations in the current language. + * + * This can be used as #pre_render callback when your $elements array contains + * a 'translations' key, and allows for only the translation for the current + * language to be rendered. + */ +function drupal_filter_translation($elements) { + global $language; + $translation_key = isset($elements['#translations'][$language->language]) ? + $language->language : (isset($elements['#translations'][0]) ? 0 : FALSE); + + if ($translation_key !== FALSE) { + foreach ($elements['#translations'][$translation_key] as $delta => $item) { + $elements['items'][$delta]['#item'] = $item; + $elements['items'][$delta]['#item']['#delta'] = $delta; + // TODO : translations might not be properly sanitized! + $elements['items'][$delta]['#item']['safe'] = $item['value']; + } + return $elements; + } + + return array(); +} + +/** * Function used by uasort to sort structured arrays by weight. */ function element_sort($a, $b) { Index: modules/field/field.crud.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/field/field.crud.inc,v retrieving revision 1.1 diff -u -p -b -r1.1 field.crud.inc --- modules/field/field.crud.inc 3 Feb 2009 17:30:11 -0000 1.1 +++ modules/field/field.crud.inc 4 Feb 2009 23:14:40 -0000 @@ -39,6 +39,8 @@ class FieldException extends Exception { * - cardinality (integer) * The number of values the field can hold. Legal values are any * positive integer or FIELD_CARDINALITY_UNLIMITED. + * - translatable (integer) + Whether the field is translatable. * - locked (integer) * TODO: undefined. * - module (string, read-only) @@ -183,8 +185,14 @@ function field_create_field($field) { if (!empty($prior_field)) { throw new FieldException(t('Attempt to create field name %name which already exists.', array('%name' => $field['field_name']))); } + // Translatable fields are set to FIELD_CARDINALITY_UNLIMITED to account for + // data being stored in any variable number of languages. + if (!empty($field['translatable'])) { + $field['cardinality'] = FIELD_CARDINALITY_UNLIMITED; + } $field += array( + 'translatable' => 0, 'cardinality' => 1, 'locked' => FALSE, 'settings' => array(), Index: modules/field/field.default.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/field/field.default.inc,v retrieving revision 1.2 diff -u -p -b -r1.2 field.default.inc --- modules/field/field.default.inc 5 Feb 2009 01:21:16 -0000 1.2 +++ modules/field/field.default.inc 5 Feb 2009 02:53:24 -0000 @@ -158,10 +158,18 @@ function field_default_view($obj_type, $ 'items' => array(), ); + // Pass ahead translation settings. + if (isset($items['#pre_render'])) { + $element['#pre_render'] = $items['#pre_render']; + } + if (isset($items['#translations'])) { + $element['#translations'] = $items['#translations']; + } + // Fill-in items. - foreach ($items as $delta => $item) { + foreach (element_children($items) as $delta) { $element['items'][$delta] = array( - '#item' => $item, + '#item' => $items[$delta], '#weight' => $delta, ); } @@ -175,7 +183,7 @@ function field_default_view($obj_type, $ ); if ($single) { - foreach ($items as $delta => $item) { + foreach (element_children($items) as $delta) { $element['items'][$delta] += $format_info; $element['items'][$delta]['#item']['#delta'] = $delta; } Index: modules/field/field.install =================================================================== RCS file: /cvs/drupal/drupal/modules/field/field.install,v retrieving revision 1.1 diff -u -p -b -r1.1 field.install --- modules/field/field.install 3 Feb 2009 17:30:11 -0000 1.1 +++ modules/field/field.install 4 Feb 2009 23:41:38 -0000 @@ -97,6 +97,12 @@ function field_schema() { 'not null' => TRUE, 'default' => 0, ), + 'translatable' => array( + 'type' => 'int', + 'size' => 'tiny', + 'not null' => TRUE, + 'default' => 0, + ), ), 'primary key' => array('field_name'), 'indexes' => array( Index: modules/field/modules/field_sql_storage/field_sql_storage.module =================================================================== RCS file: /cvs/drupal/drupal/modules/field/modules/field_sql_storage/field_sql_storage.module,v retrieving revision 1.2 diff -u -p -b -r1.2 field_sql_storage.module --- modules/field/modules/field_sql_storage/field_sql_storage.module 4 Feb 2009 20:27:58 -0000 1.2 +++ modules/field/modules/field_sql_storage/field_sql_storage.module 5 Feb 2009 02:42:11 -0000 @@ -128,9 +128,18 @@ function _field_sql_storage_schema($fiel 'not null' => TRUE, 'description' => 'The sequence number for this data item, used for multi-value fields', ), + 'language' => array( + 'type' => 'varchar', + 'length' => 32, + 'not null' => TRUE, + 'default' => '', + 'description' => 'The language for this data item.', ), - 'primary key' => array('etid', 'entity_id', 'deleted', 'delta'), + ), + + 'primary key' => array('etid', 'entity_id', 'deleted', 'delta', 'language'), // TODO : index on 'bundle' + // TODO : consider using an integer value for 'language' ); // Add field columns. @@ -144,7 +153,7 @@ function _field_sql_storage_schema($fiel $revision = $current; $revision['description'] = 'Revision archive storage for field ' . $field['field_name']; $revision['revision_id']['description'] = 'The entity revision id this data is attached to'; - $revision['primary key'] = array('etid', 'revision_id', 'deleted', 'delta'); + $revision['primary key'] = array('etid', 'revision_id', 'deleted', 'delta', 'language'); return array( _field_sql_storage_tablename($field['field_name']) => $current, @@ -219,9 +228,15 @@ function field_sql_storage_field_storage foreach ($field['columns'] as $column => $attributes) { $item[$column] = $row->{_field_sql_storage_columnname($field_name, $column)}; } - + if (!empty($field['translatable'])) { + // Add the item to the translations for the field, keyed by language. + $item['language'] = empty($row->language) ? 0 : $row->language; + $additions[$row->entity_id][$field_name]['#pre_render'] = array('drupal_filter_translation'); + $additions[$row->entity_id][$field_name]['#translations'][$item['language']][] = $item; + } // Add the item to the field values for the entity. - $additions[$row->entity_id][$field_name][] = $item; + // TODO : translated fields share $row->delta, find out a way to decide which language prevale + $additions[$row->entity_id][$field_name][$row->delta] = $item; $delta_count[$row->entity_id][$field_name]++; } } @@ -256,7 +271,7 @@ function field_sql_storage_field_storage if ($object->$field_name) { // Prepare the multi-insert query. - $columns = array('etid', 'entity_id', 'revision_id', 'bundle', 'delta'); + $columns = array('etid', 'entity_id', 'revision_id', 'bundle', 'delta', 'language'); foreach ($field['columns'] as $column => $attributes) { $columns[] = _field_sql_storage_columnname($field_name, $column); } @@ -273,6 +288,7 @@ function field_sql_storage_field_storage 'revision_id' => $vid, 'bundle' => $bundle, 'delta' => $delta, + 'language' => !empty($item->language) ? $item->language : '', ); foreach ($field['columns'] as $column => $attributes) { $record[_field_sql_storage_columnname($field_name, $column)] = isset($item[$column]) ? $item[$column] : NULL; Index: modules/field/modules/text/text.module =================================================================== RCS file: /cvs/drupal/drupal/modules/field/modules/text/text.module,v retrieving revision 1.1 diff -u -p -b -r1.1 text.module --- modules/field/modules/text/text.module 3 Feb 2009 17:30:12 -0000 1.1 +++ modules/field/modules/text/text.module 5 Feb 2009 01:34:21 -0000 @@ -103,7 +103,8 @@ function text_field_validate($obj_type, function text_field_sanitize($obj_type, $object, $field, $instance, &$items) { global $language; - foreach ($items as $delta => $item) { + foreach (element_children($items) as $delta) { + $item = $items[$delta]; // TODO D7 : this code is really node-related. if (!empty($instance['settings']['text_processing'])) { $check = is_null($object) || (isset($object->build_mode) && $object->build_mode == NODE_BUILD_PREVIEW); Index: modules/simpletest/tests/common.test =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/tests/common.test,v retrieving revision 1.25 diff -u -p -b -r1.25 common.test --- modules/simpletest/tests/common.test 31 Jan 2009 19:07:45 -0000 1.25 +++ modules/simpletest/tests/common.test 4 Feb 2009 23:14:40 -0000 @@ -538,6 +538,41 @@ class DrupalRenderUnitTestCase extends D // The lowest weight element should appear last in $output. $this->assertTrue(strpos($output, $second) > strpos($output, $first), t('Elements were sorted correctly by weight')); } + + /** + * Test rendering with drupal_filter_translation() as a #pre_render callback. + */ + function testPreRenderCallback() { + + // Render an element with multiple values per language. + $elements = array( + 'field_name' => array( + array('#markup' => 'English 1'), + array('#markup' => 'English 2'), + array('#markup' => 'French 1'), + array('#markup' => 'French 2'), + '#pre_render' => array('drupal_filter_translation'), + 'translations' => array( + 'en' => array( + array('#markup' => 'English 1'), + array('#markup' => 'English 2'), + ), + 'fr' => array( + array('#markup' => 'French 1'), + array('#markup' => 'French 2'), + ), + ), + ), + ); + + // English is the default language, so assume this for the purposes of testing. + $english_content = drupal_render($elements); + + $this->assertTrue(strpos($english_content, 'English 1') !== FALSE, t('English field was rendered.')); + $this->assertTrue(strpos($english_content, 'English 2') !== FALSE, t('English field was rendered.')); + $this->assertFalse(strpos($english_content, 'French 1') !== FALSE, t('French content was not rendered.')); + $this->assertFalse(strpos($english_content, 'French 2') !== FALSE, t('French content was not rendered.')); + } }