diff --git a/file_entity.file.inc b/file_entity.file.inc
index d5cfadc..ab99e18 100644
--- a/file_entity.file.inc
+++ b/file_entity.file.inc
@@ -182,30 +182,83 @@ function file_entity_file_mimetype_mapping_alter(&$mapping) {
  * Implements hook_file_load().
  */
 function file_entity_file_load($files) {
-  $alt = variable_get('file_entity_alt', '[file:field_file_image_alt_text]');
-  $title = variable_get('file_entity_title', '[file:field_file_image_title_text]');
+  foreach ($files as $file) {
+    $file->metadata = array();
+  }
+  file_entity_replace_title($files);
+  file_entiy_replace_alt($files);
+
+  // Load and unserialize metadata.
+  $results = db_query("SELECT * FROM {file_metadata} WHERE fid IN (:fids)", array(':fids' => array_keys($files)));
+  foreach ($results as $result) {
+    $files[$result->fid]->metadata[$result->name] = unserialize($result->value);
+  }
+}
 
-  $replace_options = array(
+/**
+ * Replace file entity title text.
+ *
+ * @param $files
+ *   The array of file entities.
+ * @param $replace_options
+ *   (Optional) Options to pass to token_replace().
+ */
+function file_entity_replace_title($files, $replace_options = array()) {
+  $replace_options += array(
     'clear' => TRUE,
     'sanitize' => FALSE,
   );
 
-  foreach ($files as $file) {
-    $file->metadata = array();
-
-    // Load alt and title text from fields.
-    if (!empty($alt)) {
-      $file->alt = token_replace($alt, array('file' => $file), $replace_options);
+  $title_default = '[file:field_file_image_title_text]';
+  $title = variable_get('file_entity_title', $title_default);
+  // If the defaults are not changed then inlining replacement is much faster
+  // than dealing with the token system.
+  if ($title === $title_default) {
+    foreach ($files as $file) {
+      $title_items = field_get_items('file', $file, 'field_file_image_title_text');
+      $file->title = $title_items ? $title_items[0]['value'] : '';
     }
-    if (!empty($title)) {
-      $file->title = token_replace($title, array('file' => $file), $replace_options);
+  }
+  else {
+    foreach ($files as $file) {
+      if (!empty($title)) {
+        $file->title = token_replace($title, array('file' => $file), $replace_options);
+      }
     }
   }
+}
 
-  // Load and unserialize metadata.
-  $results = db_query("SELECT * FROM {file_metadata} WHERE fid IN (:fids)", array(':fids' => array_keys($files)));
-  foreach ($results as $result) {
-    $files[$result->fid]->metadata[$result->name] = unserialize($result->value);
+/**
+ * Replace file entity alt.
+ *
+ * @param $files
+ *   The array of file entities.
+ * @param $replace_options
+ *   (Optional) Options to pass to token_replace().
+ */
+function file_entity_replace_alt($files, $replace_options = array()) {
+  $replace_options += array(
+    'clear' => TRUE,
+    'sanitize' => FALSE,
+  );
+
+  $alt_default = '[file:field_file_image_alt_text]';
+  $alt = variable_get('file_entity_alt', $alt_default);
+  // If the defaults are not changed then inlining replacement is much faster
+  // than dealing with the token system.
+  if ($alt === $alt_default) {
+    foreach ($files as $file) {
+      $alt_items = field_get_items('file', $file, 'field_file_image_alt_text');
+      $file->alt = $alt_items ? $alt_items[0]['value'] : '';
+    }
+  }
+  else {
+    foreach ($files as $file) {
+      // Load alt and title text from fields.
+      if (!empty($alt)) {
+        $file->alt = token_replace($alt, array('file' => $file), $replace_options);
+      }
+    }
   }
 }
 
diff --git a/file_entity.file_api.inc b/file_entity.file_api.inc
index 9baa1a4..115fe72 100644
--- a/file_entity.file_api.inc
+++ b/file_entity.file_api.inc
@@ -231,6 +231,23 @@ function file_view_file($file, $displays = 'full', $langcode = NULL) {
   drupal_alter('file_displays', $displays, $file, $view_mode);
   _file_sort_array_by_weight($displays);
 
+  // Since $file->alt and $file->title were set in file_entity_file_load()
+  // (which is a language-agnostic hook) they will not be in the correct
+  // language if the file is being displayed in a language other than the
+  // default one. Set them again here, using the correct language. This must
+  // run after hook_file_displays_alter() since the Media module sets
+  // $file->alt and $file->title again during that hook.
+  if ($langcode != $GLOBALS['language_content']->language) {
+    $languages = language_list();
+    if (isset($languages[$langcode])) {
+      $replace_options = array(
+        'language' => $languages[$langcode],
+      );
+      file_entity_replace_title(array($file->fid => $file), $replace_options);
+      file_entity_replace_alt(array($file->fid => $file), $replace_options);
+    }
+  }
+
   // Attempt to display the file with each of the possible displays. Stop after
   // the first successful one. See file_displays() for details.
   $element = NULL;
