Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.864
diff -u -p -r1.864 common.inc
--- includes/common.inc	5 Feb 2009 01:21:16 -0000	1.864
+++ includes/common.inc	5 Feb 2009 16:28:07 -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.2
diff -u -p -r1.2 field.crud.inc
--- modules/field/field.crud.inc	5 Feb 2009 03:42:57 -0000	1.2
+++ modules/field/field.crud.inc	5 Feb 2009 16:28:08 -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)
@@ -185,6 +187,7 @@ function field_create_field($field) {
   }
 
   $field += array(
+    'translatable' => 0,
     'cardinality' => 1,
     'locked' => FALSE,
     'settings' => array(),
@@ -555,4 +558,4 @@ function field_delete_instance($field_na
 
 /**
  * @} End of "defgroup field_crud".
- */
\ No newline at end of file
+ */
Index: modules/field/field.default.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/field.default.inc,v
retrieving revision 1.2
diff -u -p -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 16:28:08 -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 -r1.1 field.install
--- modules/field/field.install	3 Feb 2009 17:30:11 -0000	1.1
+++ modules/field/field.install	5 Feb 2009 16:28:08 -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.3
diff -u -p -r1.3 field_sql_storage.module
--- modules/field/modules/field_sql_storage/field_sql_storage.module	5 Feb 2009 03:42:57 -0000	1.3
+++ modules/field/modules/field_sql_storage/field_sql_storage.module	5 Feb 2009 16:28:08 -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,
@@ -212,16 +221,22 @@ function field_sql_storage_field_storage
       ->execute();
 
     foreach ($results as $row) {
-      if ($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED || $delta_count[$row->entity_id][$field_name] < $field['cardinality']) {
+      if ($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED || !empty($field['translatable']) || $delta_count[$row->entity_id][$field_name] < $field['cardinality']) {
         $item = array();
         // For each column declared by the field, populate the item
         // from the prefixed database column.
         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 prevails.
+        $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;
@@ -385,4 +401,4 @@ function field_sql_storage_field_storage
       ->condition('bundle', $bundle_old)
       ->execute();
   }
-}
\ No newline at end of file
+}
Index: modules/field/modules/text/text.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/modules/text/text.module,v
retrieving revision 1.2
diff -u -p -r1.2 text.module
--- modules/field/modules/text/text.module	5 Feb 2009 03:42:57 -0000	1.2
+++ modules/field/modules/text/text.module	5 Feb 2009 16:28:09 -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 -r1.25 common.test
--- modules/simpletest/tests/common.test	31 Jan 2009 19:07:45 -0000	1.25
+++ modules/simpletest/tests/common.test	5 Feb 2009 16:28:09 -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.'));
+  }
 }
 
 
