Index: fb.admin.inc
===================================================================
--- fb.admin.inc	(revision 2961)
+++ fb.admin.inc	(working copy)
@@ -264,3 +264,254 @@
   }

 }
+
+/*
+ * Manages mapping of drupal language -> fb locale
+ */
+function fb_admin_languages() {
+  $form = array();
+
+  $form['fb_admin_languages'] = array(
+    '#title' => t('Languages mapping'),
+    '#type' => 'fieldset',
+  );
+
+  $form['fb_admin_languages']['fb_language_override'] = array(
+    '#title'  => t("Use locale learned from facebook, if available."),
+    '#type'  => 'checkbox',
+    '#return_value'  => 'override',
+    '#default_value' => variable_get('fb_language_override', 'override'),
+    '#description' => t('Otherwise, use the mapping below when the local user\'s language is known.'),
+);
+
+  foreach (language_list() as $language) {
+    $form['fb_admin_languages']['fb_language_'. $language->language] = array(
+      '#title'  => t("Assigned Facebook's locale to @language", array('@language' => t($language->name))),
+      '#type'  => 'select',
+      '#options' => fb_admin_i18n_list(),
+      '#default_value' => fb_admin_i18n_map($language->language),
+    );
+  }
+
+  return system_settings_form($form);
+}
+
+/*
+ * Returns a list of valid Facebook language codes.
+ * Helper function
+ * see: http://wiki.developers.facebook.com/index.php/Facebook_Locales
+*/
+function fb_admin_i18n_list() {
+  return array(
+    'af_ZA' => t('Afrikaans'),
+    'sq_AL' => t('Albanian'),
+    'ar_AR' => t('Arabic'),
+    'hy_AM' => t('Armenian'),
+    'ay_BO' => t('Aymara'),
+    'az_AZ' => t('Azeri'),
+    'eu_ES' => t('Basque'),
+    'be_BY' => t('Belarusian'),
+    'bn_IN' => t('Bengali'),
+    'bs_BA' => t('Bosnian'),
+    'bg_BG' => t('Bulgarian'),
+    'ca_ES' => t('Catalan'),
+    'ck_US' => t('Cherokee'),
+    'hr_HR' => t('Croatian'),
+    'cs_CZ' => t('Czech'),
+    'da_DK' => t('Danish'),
+    'nl_NL' => t('Dutch'),
+    'nl_BE' => t('Dutch (België)'),
+    'en_PI' => t('English (Pirate)'),
+    'en_GB' => t('English (UK)'),
+    'en_US' => t('English (US)'),
+    'en_UD' => t('English (Upside Down)'),
+    'eo_EO' => t('Esperanto'),
+    'et_EE' => t('Estonian'),
+    'fo_FO' => t('Faroese'),
+    'tl_PH' => t('Filipino'),
+    'fi_FI' => t('Finnish'),
+    'fb_FI' => t('Finnish (test)'),
+    'fr_FR' => t('French (France)'),
+    'fr_CA' => t('French (Canada)'),
+    'gl_ES' => t('Galician'),
+    'ka_GE' => t('Georgian'),
+    'de_DE' => t('German'),
+    'el_GR' => t('Greek'),
+    'gn_PY' => t('Guaraní'),
+    'gu_IN' => t('Gujarati'),
+    'he_IL' => t('Hebrew'),
+    'hi_IN' => t('Hindi'),
+    'hu_HU' => t('Hungarian'),
+    'is_IS' => t('Icelandic'),
+    'id_ID' => t('Indonesian'),
+    'ga_IE' => t('Irish'),
+    'it_IT' => t('Italian'),
+    'ja_JP' => t('Japanese'),
+    'jv_ID' => t('Javanese'),
+    'kn_IN' => t('Kannada'),
+    'kk_KZ' => t('Kazakh'),
+    'km_KH' => t('Khmer'),
+    'tl_ST' => t('Klingon'),
+    'ko_KR' => t('Korean'),
+    'ku_TR' => t('Kurdish'),
+    'la_VA' => t('Latin'),
+    'lv_LV' => t('Latvian'),
+    'fb_LT' => t('Leet Speak'),
+    'li_NL' => t('Limburgish'),
+    'lt_LT' => t('Lithuanian'),
+    'mk_MK' => t('Macedonian'),
+    'mg_MG' => t('Malagasy'),
+    'ms_MY' => t('Malay'),
+    'ml_IN' => t('Malayalam'),
+    'mt_MT' => t('Maltese'),
+    'mr_IN' => t('Marathi'),
+    'mn_MN' => t('Mongolian'),
+    'ne_NP' => t('Nepali'),
+    'se_NO' => t('Northern Sámi'),
+    'nb_NO' => t('Norwegian (bokmal)'),
+    'nn_NO' => t('Norwegian (nynorsk)'),
+    'ps_AF' => t('Pashto'),
+    'fa_IR' => t('Persian'),
+    'pl_PL' => t('Polish'),
+    'pt_PT' => t('Portuguese (Portugal)'),
+    'pt_BR' => t('Portuguese (Brazil)'),
+    'pa_IN' => t('Punjabi'),
+    'qu_PE' => t('Quechua'),
+    'ro_RO' => t('Romanian'),
+    'rm_CH' => t('Romansh'),
+    'ru_RU' => t('Russian'),
+    'sa_IN' => t('Sanskrit'),
+    'sr_RS' => t('Serbian'),
+    'zh_CN' => t('Simplified Chinese (China)'),
+    'sk_SK' => t('Slovak'),
+    'sl_SI' => t('Slovenian'),
+    'so_SO' => t('Somali'),
+    'es_LA' => t('Spanish'),
+    'es_CL' => t('Spanish (Chile)'),
+    'es_CO' => t('Spanish (Colombia)'),
+    'es_MX' => t('Spanish (Mexico)'),
+    'es_ES' => t('Spanish (Spain)'),
+    'es_VE' => t('Spanish (Venezuela)'),
+    'sw_KE' => t('Swahili'),
+    'sv_SE' => t('Swedish'),
+    'sy_SY' => t('Syriac'),
+    'tg_TJ' => t('Tajik'),
+    'ta_IN' => t('Tamil'),
+    'tt_RU' => t('Tatar'),
+    'te_IN' => t('Telugu'),
+    'th_TH' => t('Thai'),
+    'zh_HK' => t('Traditional Chinese (Hong Kong)'),
+    'zh_TW' => t('Traditional Chinese (Taiwan)'),
+    'tr_TR' => t('Turkish'),
+    'uk_UA' => t('Ukrainian'),
+    'ur_PK' => t('Urdu'),
+    'uz_UZ' => t('Uzbek'),
+    'vi_VN' => t('Vietnamese'),
+    'cy_GB' => t('Welsh'),
+    'xh_ZA' => t('Xhosa'),
+    'yi_DE' => t('Yiddish'),
+    'zu_ZA' => t('Zulu'),
+  );
+}
+
+
+/*
+ * Mapping Drupal language code -> Facebook  locale
+ * Helper function
+ */
+function fb_admin_i18n_map($lang_code) {
+
+  $languages_map = array(
+    'af' => 'af_ZA',
+    'ar' => 'ar_AR',
+    'ay' => 'ay_BO',
+    'az' => 'az_AZ',
+    'be' => 'be_BY',
+    'bg' => 'bg_BG',
+    'bn' => 'bn_IN',
+    'bs' => 'bs_BA',
+    'ca' => 'ca_ES',
+    'cs' => 'cs_CZ',
+    'cy' => 'cy_GB',
+    'da' => 'da_DK',
+    'de' => 'de_DE',
+    'el' => 'el_GR',
+    'en' => 'en_US',
+    'eo' => 'eo_EO',
+    'es' => 'es_ES',
+    'et' => 'et_EE',
+    'eu' => 'eu_ES',
+    'fa' => 'fa_IR',
+    'fi' => 'fi_FI',
+    'fo' => 'fo_FO',
+    'fr' => 'fr_FR',
+    'ga' => 'ga_IE',
+    'gl' => 'gl_ES',
+    'gn' => 'gn_PY',
+    'gu' => 'gu_IN',
+    'he' => 'he_IL',
+    'hi' => 'hi_IN',
+    'hr' => 'hr_HR',
+    'hu' => 'hu_HU',
+    'hy' => 'hy_AM',
+    'id' => 'id_ID',
+    'is' => 'is_IS',
+    'it' => 'it_IT',
+    'ja' => 'ja_JP',
+    'jv' => 'jv_ID',
+    'ka' => 'ka_GE',
+    'kk' => 'kk_KZ',
+    'km' => 'km_KH',
+    'kn' => 'kn_IN',
+    'ko' => 'ko_KR',
+    'ku' => 'ku_TR',
+    'la' => 'la_VA',
+    'lt' => 'lt_LT',
+    'lv' => 'lv_LV',
+    'mg' => 'mg_MG',
+    'mk' => 'mk_MK',
+    'ml' => 'ml_IN',
+    'mn' => 'mn_MN',
+    'mr' => 'mr_IN',
+    'ms' => 'ms_MY',
+    'mt' => 'mt_MT',
+    'nb' => 'nb_NO',
+    'ne' => 'ne_NP',
+    'nl' => 'nl_BE',
+    'nn' => 'nn_NO',
+    'pa' => 'pa_IN',
+    'pl' => 'pl_PL',
+    'ps' => 'ps_AF',
+    'qu' => 'qu_PE',
+    'rm' => 'rm_CH',
+    'ro' => 'ro_RO',
+    'ru' => 'ru_RU',
+    'sa' => 'sa_IN',
+    'se' => 'se_NO',
+    'sk' => 'sk_SK',
+    'sl' => 'sl_SI',
+    'so' => 'so_SO',
+    'sq' => 'sq_AL',
+    'sr' => 'sr_RS',
+    'sv' => 'sv_SE',
+    'sw' => 'sw_KE',
+    'ta' => 'ta_IN',
+    'te' => 'te_IN',
+    'tg' => 'tg_TJ',
+    'th' => 'th_TH',
+    'tl' => 'tl_ST',
+    'tr' => 'tr_TR',
+    'tt' => 'tt_RU',
+    'uk' => 'uk_UA',
+    'ur' => 'ur_PK',
+    'uz' => 'uz_UZ',
+    'vi' => 'vi_VN',
+    'xh' => 'xh_ZA',
+    'yi' => 'yi_DE',
+    'zh-hans' => 'zh_CN',
+    'zh-hant' => 'zh_TW',
+    'zu' => 'zu_ZA',
+  );
+
+  return isset($languages_map[$lang_code]) ? $languages_map[$lang_code] : 'en_US';
+}
Index: fb.module
===================================================================
--- fb.module	(revision 2965)
+++ fb.module	(working copy)
@@ -127,23 +127,20 @@
     $GLOBALS['fb_init_no_settings'] = TRUE;
   }

-  // Values needed by fb.js.
-  $js_settings = array(
-    'ajax_event_url' => url(FB_PATH_AJAX_EVENT, array('absolute' => TRUE)),
-    // @TODO - replace en_US with dynamically determined locale.
-    'js_sdk_url' => variable_get('fb_js_sdk', 'http://connect.facebook.net/en_US/all.js'),
-  );

+  // Javascript settings needed by fb.js.
+  fb_js_settings('ajax_event_url', url(FB_PATH_AJAX_EVENT, array('absolute' => TRUE)));
+
   // Figure out which app the current request is for.
   $_fb_app = fb_invoke(FB_OP_CURRENT_APP);

   if ($_fb_app) {
     // An App is configured.

-    // Initialize javascript.
-    $js_settings['apikey'] = $_fb_app->apikey;
-    $js_settings['label'] = $_fb_app->label;
-    $js_settings['page_type'] = fb_settings(FB_SETTINGS_TYPE); // canvas or connect.
+    // Javascript settings needed by fb.js.
+    fb_js_settings('apikey', $_fb_app->apikey);
+    fb_js_settings('label', $_fb_app->label);
+    fb_js_settings('page_type', fb_settings(FB_SETTINGS_TYPE)); // canvas or connect.

     // Initialize the PHP API.
     $_fb = fb_api_init($_fb_app);
@@ -162,13 +159,13 @@
                     'fb' => $_fb,
                     'fbu' => $_fb->getUser(),
                   ));
-        $js_settings['fbu'] = $_fb->getUser();
+        fb_js_settings('fbu', $_fb->getUser());
       }
       else {
         // Add perms to settings, for calling FB.login().
         $perms = array();
         drupal_alter('fb_required_perms', $perms);
-        $js_settings['perms'] = implode(',', $perms);
+        fb_js_settings('perms', implode(',', $perms));
       }
     }
     else
@@ -187,23 +184,70 @@
       $destination = fb_scrub_urls($destination); // Needed?
     }

-    $js_settings['reload_url'] = url($destination, array('absolute' => TRUE, 'fb_canvas' => fb_is_canvas()));
+    fb_js_settings('reload_url', url($destination, array('absolute' => TRUE, 'fb_canvas' => fb_is_canvas())));
   }
   // Allow third-parties to act, even if we did not initialize $_fb.
   fb_invoke(FB_OP_POST_INIT, array('fb_app' => $_fb_app,
                                    'fb' => $_fb));

-  $js_settings['controls'] = implode(',', fb_controls());
+  fb_js_settings('controls', implode(',', fb_controls()));

+  if (!fb_js_settings('js_sdk_url')) {
+    if (isset($_SESSION['fb_locale']) && variable_get('fb_language_override', 'override')) {
+      $fb_lang = $_SESSION['fb_locale'];
+    }
+    else {
+      $user_language = user_preferred_language($GLOBALS['user']);
+      $fb_lang = variable_get('fb_language_' . $user_language->language, 'en_US');
+    }
+    fb_js_settings('js_sdk_url', variable_get('fb_js_sdk', 'http://connect.facebook.net/' . $fb_lang . '/all.js'));
+  }
+
   // Add javascript to all pages.
   drupal_add_js(drupal_get_path('module', 'fb') . '/fb.js');
+}

-  // @TODO - move this to fb_preprocess_page().  See og_preprocess_page for example.
+/**
+ * Implements hook_preprocess_page().
+ *
+ * We add our javascript settings here, as late as possible in the page
+ * request, so that third-party modules have a chance to customize by calling
+ * fb_js_settings().
+ */
+function fb_preprocess_page(&$variables) {
+  $js_settings = fb_js_settings();
   drupal_add_js(array('fb' => $js_settings), 'setting');
+
+  $variables['scripts'] = drupal_get_js();
 }

+/**
+ *
+ * Adds the javascript setting with the supplied key/value.  This function merely keeps track
+ * of the settings and writes them as late as possible (in the fb_preprocess_page function).
+ *
+ * @param $key
+ *   The javascript setting name.  If the key is null then nothing is modified and the settings are returned.
+ * @param $value
+ *   The value of the javascript setting.  If the key is not null by the value is the setting is removed
+ * @return
+ *    The associative array containing the current fb javascript settings
+ */
+function fb_js_settings($key = null, $value = null) {
+  static $fb_js_settings = array();

+  if (isset($key) && isset($value)) {
+    $fb_js_settings[$key] = $value;
+  }
+  elseif (isset($key)) {
+    return isset($fb_js_settings[$key]) ? $fb_js_settings[$key] : NULL;
+  }
+  else {
+    return $fb_js_settings;
+  }
+}

+
 /**
  * Include and initialize Facebook's PHP SDK.
  */
@@ -429,22 +473,15 @@
 function fb_api_check_session($fb) {
   $success = FALSE;
   try {
-    $session = $fb->getSession();
-    // Newer API uses access_token
-    if (isset($session['access_token'])) {
-      $is_user = $fb->api(array(
-                            'method' => 'users.isAppUser',
-                          ));
-      // Does not matter what is returned, as long as exception is not thrown.
-      $success = TRUE;
+    $me = $fb->api('me');
+
+    // Store the locale if set.
+    if (isset($me['locale'])) {
+      $_SESSION['fb_locale'] = $me['locale'];
     }
-    // Older API used session_key.  Still needed?
-    elseif (isset($session['session_key'])) {
-      $is_user = $fb->api(array(
-                            'method' => 'users.isAppUser',
-                          ));
-      $success = TRUE;
-    }
+
+    // Does not matter what is returned, as long as exception is not thrown.
+    $success = TRUE;
   }
   catch (Exception $e) {
     if (fb_verbose()) {
@@ -458,6 +495,7 @@
   return $success;
 }

+
 /**
  * Returns the facebook user id currently visiting a canvas page, or if
  * set_user has been called.  Unlike fb_get_fbu(), works only on canvas and
@@ -743,6 +781,16 @@
     'type' => MENU_DEFAULT_LOCAL_TASK,
   );

+  //Languages (fb locales) mapping
+  $items[FB_PATH_ADMIN . '/languages'] = array(
+    'title' => 'Languages',
+    'access arguments' => array(FB_PERM_ADMINISTER),
+    'weight' => 0,
+    'type' => MENU_LOCAL_TASK,
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('fb_admin_languages'),
+    'file' => 'fb.admin.inc',
+  );
   // Admin pages for each app.
   $items[FB_PATH_ADMIN_APPS . '/%fb'] = array(
     'title' => 'Application Detail',
@@ -1141,4 +1189,4 @@
   }
   drupal_access_denied();
   exit();
-}
\ No newline at end of file
+}
