Index: modules/field/field.install
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/field.install,v
retrieving revision 1.3
diff -u -r1.3 field.install
--- modules/field/field.install	11 Feb 2009 04:45:57 -0000	1.3
+++ modules/field/field.install	11 Feb 2009 23:08:36 -0000
@@ -53,6 +53,12 @@
         'not null' => TRUE,
           'default' => 0,
       ),
+      'translatable' => array(
+        'type' => 'int',
+        'size' => 'tiny',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
       'active' => array(
         'type' => 'int',
         'size' => 'tiny',
Index: modules/field/field.crud.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/field.crud.inc,v
retrieving revision 1.4
diff -u -r1.4 field.crud.inc
--- modules/field/field.crud.inc	10 Feb 2009 03:16:14 -0000	1.4
+++ modules/field/field.crud.inc	11 Feb 2009 23:08:36 -0000
@@ -48,6 +48,8 @@
  * - 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)
@@ -195,6 +197,7 @@
 
   $field += array(
     'cardinality' => 1,
+    'translatable' => 0,
     'locked' => FALSE,
     'settings' => array(),
   );
Index: modules/field/field.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/field.module,v
retrieving revision 1.4
diff -u -r1.4 field.module
--- modules/field/field.module	10 Feb 2009 03:16:14 -0000	1.4
+++ modules/field/field.module	11 Feb 2009 23:08:36 -0000
@@ -597,4 +597,4 @@
 
 /**
  * @} End of "defgroup field"
- */
\ No newline at end of file
+ */
Index: modules/locale/locale.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/locale/locale.module,v
retrieving revision 1.237
diff -u -r1.237 locale.module
--- modules/locale/locale.module	5 Feb 2009 00:32:46 -0000	1.237
+++ modules/locale/locale.module	11 Feb 2009 23:08:37 -0000
@@ -601,6 +601,56 @@
 }
 
 /**
+ * Implementation of hook_field_attach_load().
+ *
+ * For multilingual fields, populate the value for each
+ * $object->field_name[$delta] with the best match for the current language
+ * or the site default. Since hook_field_attach_load() is not persistently
+ * cached, we can add language specific information here in the knowledge
+ * it will only affect the curent request. Field rendering and formatting is
+ * agnostic to whether a field is multilingual or not, however we leave the
+ * #languages array in situ to allow other modules to access it if necessary.
+ */
+function locale_field_attach_load($obj_type, $object) {
+  global $language;
+
+  // Only operate on translatable fields.
+  $fields = array();
+  list(, , $bundle) = field_attach_extract_ids($obj_type, $object);
+  $instances = field_info_instances($bundle);
+  foreach ($instances as $instance) {
+    $field = field_info_field($instance['field_name']);
+    if (!empty($field['translatable'])) {
+      $fields[] = $instance['field_name'];
+    }
+  }
+  if (empty($fields)) {
+    return;
+  }
+
+  $default = language_default();
+  foreach ($fields as $field_name) {
+    foreach ($object->$field_name as $delta => $item) {
+      $best_fit = array();
+      // Use current language, if available.
+      if (isset($item['#languages'][$language->language])) {
+        $best_fit = $item['#languages'][$language->language];
+      }
+      // Use default language, if available.
+      elseif (isset($item['#languages'][$default->language])) {
+        $best_fit = $item['#languages'][$default->language];
+      }
+      // Use language neutral values, if available.
+      elseif (isset($item['#languages'][''])) {
+        $best_fit = $item['#languages'][''];
+      }
+            
+      $object->{$field_name}[$delta] = array_merge($item, $best_fit);
+    }
+  }
+}
+
+/**
  * Theme locale translation filter selector.
  *
  * @ingroup themeable
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.4
diff -u -r1.4 field_sql_storage.module
--- modules/field/modules/field_sql_storage/field_sql_storage.module	10 Feb 2009 03:16:14 -0000	1.4
+++ modules/field/modules/field_sql_storage/field_sql_storage.module	11 Feb 2009 23:08:37 -0000
@@ -128,9 +128,17 @@
         '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'),
-    // TODO : index on 'bundle'
+    'primary key' => array('etid', 'entity_id', 'deleted', 'delta', 'language'),
+    // TODO : index on 'bundle'.
+    // TODO : consider an integer field for 'language'.
   );
 
   // Add field columns.
@@ -144,7 +152,7 @@
   $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,7 +220,7 @@
       ->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.
@@ -220,8 +228,17 @@
           $item[$column] = $row->{_field_sql_storage_columnname($field_name, $column)};
         }
 
-        // Add the item to the field values for the entity.
-        $additions[$row->entity_id][$field_name][] = $item;
+        // For translatable fields, put the multilingual values for each delta
+        // into a '#languages' property. This information can be safely cached
+        // in the field and object caches.
+        if ($field['translatable']) {
+          $item['language'] = empty($row->language) ? '' : $row->language;
+          $additions[$row->entity_id][$field_name][$row->delta]['#languages'][$item['language']] = $item;
+        }
+        else {
+          // Add the item to the field values for the entity.
+          $additions[$row->entity_id][$field_name][] = $item;
+        }
         $delta_count[$row->entity_id][$field_name]++;
       }
     }
@@ -256,7 +273,7 @@
 
       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 +290,7 @@
             '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;
@@ -282,7 +300,7 @@
             $revision_query->values($record);
           }
 
-          if ($field['cardinality'] != FIELD_CARDINALITY_UNLIMITED && ++$delta_count == $field['cardinality']) {
+          if ($field['cardinality'] != FIELD_CARDINALITY_UNLIMITED && empty($field['translatable']) && ++$delta_count == $field['cardinality']) {
             break;
           }
         }
@@ -385,4 +403,4 @@
       ->condition('bundle', $bundle_old)
       ->execute();
   }
-}
\ No newline at end of file
+}
