diff --git a/fb.module b/fb.module
index d6760de..83dcb5f 100644
--- a/fb.module
+++ b/fb.module
@@ -60,6 +60,7 @@ define('FB_VAR_RELOAD_APPEND_HASH', 'fb_reload_append_hash');
 define('FB_VAR_CURL_NOVERIFY', 'fb_curl_noverify');
 define('FB_VAR_SECURE_URLS', 'fb_secure_urls');
 define('FB_VAR_ALTER_USERNAME', 'fb_format_username');
+define('FB_VAR_ALTER_USERNAME_AND_CACHE', 'fb_cache_username');
 
 // Possible choices for secure urls.
 define('FB_SECURE_URLS_ALWAYS', 1);
@@ -71,6 +72,10 @@ define('FB_ALTER_USERNAME_ALWAYS', 'always');
 define('FB_ALTER_USERNAME_NEVER', 'never');
 define('FB_ALTER_USERNAME_NOT_THEMING', 'when_not_theming');
 
+// Possible choices for caching the formated username
+define('FB_ALTER_USERNAME_AND_CACHE', 'on');
+define('FB_ALTER_USERNAME_DONT_CACHE', 'off');
+
 // node_access realms (belongs here?)
 define('FB_GRANT_REALM_FRIEND', 'fb_friend');
 define('FB_GRANT_REALM_GROUP', 'fb_group');
@@ -1361,6 +1366,25 @@ function fb_menu() {
 }
 
 /**
+ * Implements hook_language_negotiation_info().
+ */
+function fb_language_negotiation_info() {
+  return array(
+    'fb' => array(
+      'types' => array(LANGUAGE_TYPE_INTERFACE),
+      'callbacks' => array(
+        'language' => 'fb_get_lang'
+      ),
+      'weight' => 0,
+      'name' => t('Facebook locale'),
+      'description' => t('Facebook locale set by the user in canvas applications.'),
+      'cache' => 0,
+      'file' => drupal_get_path('module', 'fb') . '/fb.module'
+    )
+  );
+}
+
+/**
  * Implementation of a %wildcard_load(). http://drupal.org/node/224170
  *
  * Handles menu items with %fb in the path.  Seems to get called a lot(!) so we cache.
@@ -1499,6 +1523,25 @@ function fb_invoke($op, $data = NULL, $return = NULL, $hook = FB_HOOK) {
 }
 
 /**
+ * Helper function that gets the language currently used by the user in Facebook.
+ *
+ * @param mixed $languages List of valid and enable languages
+ * @return string Language code or boolean FALSE
+ */
+function fb_get_lang($langs) {
+  if ($_SESSION['fb_lang']) {
+    return $_SESSION['fb_lang'];
+  }
+  else if ($lang = fb_settings(FB_SETTINGS_USER_LANG)) {
+    $_SESSION['fb_lang'] = $lang;
+    return $lang;
+  }
+  else {
+    return FALSE;
+  }
+}
+
+/**
  * This method will clean up URLs. When serving canvas pages, extra
  * information is included in URLs. This will remove the extra
  * information. Useful when linking back to the website from a canvas page or
@@ -1685,18 +1728,41 @@ function fb_username_alter(&$name, $account) {
   }
 
   if (isset($GLOBALS['_fb']) && $fbu = fb_get_fbu($account)) { // @TODO - is fb_get_fbu() a performance hit here?
-    try {
-      // Use fql query instead of graph api, because it will succeed more often.
-      $data = fb_fql_query($GLOBALS['_fb'], "SELECT name FROM user WHERE uid=$fbu", array(
-                             'access_token' => fb_get_token($GLOBALS['_fb']),
-                           ));
-      if (count($data) && isset($data[0]['name'])) {
-        $name = $data[0]['name'];
+// Being an expensive call we do here some trickery to optimize things
+
+    // First we try the static cache
+    $names = &drupal_static(__FUNCTION__, array());
+
+    if (!$names[$fbu]) {
+      // If no static cache is set we try the persistent cache but here we have to check
+      //if it is allowed to store it
+      $use_cache = variable_get(FB_VAR_ALTER_USERNAME_AND_CACHE, FB_ALTER_USERNAME_DONT_CACHE);
+      if ($use_cache == FB_ALTER_USERNAME_AND_CACHE) {
+        // If it is allowed we try to get it from here
+        $cache = cache_get('fb_username_' . $fbu);
+        // and save it in the $user var
+        $names[$fbu] = $cache->data;
       }
     }
-    catch (Exception $e) {
-      fb_log_exception($e, t('Failed to alter username for facebook user %fbu', array(
-                               '%fbu' => $fbu)));
+    
+    if (!$names[$fbu]) {
+      // Nothing from the previous attempts worked so we have to make the request
+      try {
+        // Use fql query instead of graph api, because it will succeed more often.
+        $data = fb_fql_query($GLOBALS['_fb'], "SELECT name FROM user WHERE uid=$fbu", array('access_token' => fb_get_token($GLOBALS['_fb'])));
+        if (count($data) && isset($data[0]['name'])) {
+          $names[$fbu] = $data[0]['name'];
+
+          $use_cache = variable_get(FB_VAR_ALTER_USERNAME_AND_CACHE, FB_ALTER_USERNAME_DONT_CACHE);
+          if ($use_cache == FB_ALTER_USERNAME_AND_CACHE) {
+            // If we are also allowed to save it we do it now
+            cache_set('fb_username_' . $fbu, $names[$fbu]);
+          }
+        }
+      } catch (Exception $e) {
+        fb_log_exception($e, t('Failed to alter username for facebook user %fbu', array(
+              '%fbu' => $fbu)));
+      }
     }
   }
 }
diff --git a/fb_settings.inc b/fb_settings.inc
index f72f179..299ca9b 100644
--- a/fb_settings.inc
+++ b/fb_settings.inc
@@ -27,6 +27,8 @@ define('FB_SETTINGS_FBU', 'fbu');
 define('FB_SETTINGS_TOKEN', 'token');
 define('FB_SETTINGS_TYPE', 'type'); // page type not same as cb type
 define('FB_SETTINGS_COOKIE_DOMAIN', 'cookie_domain');
+define('FB_SETTINGS_USER_LOCALE', 'user_locale');
+define('FB_SETTINGS_USER_LANG', 'user_language');
 
 // Possible values for page type.
 define('FB_SETTINGS_TYPE_CANVAS', 'canvas');
@@ -108,6 +110,12 @@ function _fb_settings_honor_signed_request($sr) {
     // Probably a canvas page.
     fb_settings(FB_SETTINGS_FBU, $sr['user_id']);
   }
+  
+  if (!empty($sr['user']) && !empty($sr['user']['locale'])) {
+    fb_settings(FB_SETTINGS_USER_LOCALE, $sr['user']['locale']); // Full locale.
+    $lang = preg_replace('/_[A-Z]{2}/', '', $sr['user']['locale']);
+    fb_settings(FB_SETTINGS_USER_LANG, $lang); // Just the language, for Drupal API.
+  }
 
   if (isset($sr['oauth_token'])) {
     fb_settings(FB_SETTINGS_TOKEN, $sr['oauth_token']);
