diff -rupN language_fallback-7.x-1.x-dev//language_fallback.admin.inc language_fallback-7.x-2.x-dev//language_fallback.admin.inc
--- language_fallback-7.x-1.x-dev//language_fallback.admin.inc	1970-01-01 00:00:00.000000000 +0000
+++ language_fallback-7.x-2.x-dev//language_fallback.admin.inc	2013-08-20 20:46:54.299079074 +0000
@@ -0,0 +1,376 @@
+<?php
+
+/**
+ * Implements hook_form_alter().
+ * 
+ * This is the place where user can set up language fallback for existing or new languages.
+ */
+function language_fallback_form_alter(&$form, &$form_state, $form_id) {
+	//if ($form_id == 'locale_languages_predefined_form' || $form_id == 'locale_languages_custom_form' || $form_id == 'locale_languages_edit_form') {
+
+	// There was a trouble making AJAX to work on "add language" form so it has been disabled.
+	if ($form_id == 'locale_languages_edit_form') {
+
+		$langcode = isset($form['langcode_view']) ? $form['langcode']['#value'] : NULL;
+		// Add select to create/edit form.
+		
+		$key = $form_id == 'locale_languages_predefined_form' ? 'language list' : 'custom language';
+		//$ajax_key = strtr($key, " ", "-");
+		$form['_key_'] = array('#value' => $key);
+		//$form[$key] = array('#type' => 'fieldset', '#title' => t('Language fallback'));
+		//$form[$key]['fallback'] = array('#tree' => true);
+		
+		//drupal_add_js(drupal_get_path('module', 'language_fallback') . '/language_fallback.js');
+		
+		$form[$key]['fallback'] = array(
+				'#type' => 'fieldset',
+				'#title' => t('Language fallback'),
+				'#tree' => true,
+				'#prefix' => '<div id="countries-fieldset-wrapper">',
+				'#suffix' => '</div>',
+		);
+
+		$countries = language_fallback_get_countries();
+
+		if (empty($form_state[$key]['fallback']['countries'])) {
+			$form_state[$key]['fallback']['countries'] = array(LANGUAGE_FALLBACK_ALL_COUNTRIES);
+			
+			if ($langcode) {
+			// Get all countries that we have a fallback for this language
+				$result = db_select('language_fallback', 'f')
+									->fields('f', array('country'))
+									->condition('language', $langcode, '=')
+									->condition('country', LANGUAGE_FALLBACK_ALL_COUNTRIES, '<>')
+									->execute();
+				while($c = $result->fetchField()){
+					$form_state[$key]['fallback']['countries'][] = $c;
+				}
+			}
+			
+		}
+		
+		foreach($form_state[$key]['fallback']['countries'] as $country){
+			language_fallback_get_fallback_form($form, $form_state, $key, $country, $countries[$country]);
+			
+			// Unset countries that are in use
+			unset($countries[$country]);
+		}
+		
+		if (count($countries) > 0) {
+			$form[$key]['fallback']['country'] = array(
+				'#type' => 'select',
+				'#options' => $countries
+			);
+			
+			$form[$key]['fallback']['add_country'] = array(
+				'#type' => 'submit',
+				'#value' => t('Add country specific fallback'),
+				'#submit' => array('language_fallback_country_add_another'),
+				'#ajax' => array(
+						'callback' => 'language_fallback_country_add_remove_ajax_callback',
+						'wrapper' => 'countries-fieldset-wrapper',
+				),
+			);
+		}
+
+		$form['#submit'][] = 'language_fallback_country_add_language_submit';
+		/**/
+	}
+}
+
+function language_fallback_get_fallback_form(&$form, &$form_state, $key, $ccode = LANGUAGE_FALLBACK_ALL_COUNTRIES, $country = false, $scope = 'admin') {
+	$langcode = isset($form['langcode_view']) ? $form['langcode']['#value'] : NULL;
+	
+	$options = &drupal_static(__FUNCTION__);
+	
+	if (!$options) {
+		// Cache the language options as this function may get called multiple times per page load.
+		$default_lang = language_default();
+		$languages = language_list();
+		$options = locale_language_list();
+		
+		// Alter label of default language.
+		if (isset($options[$default_lang->language])) {
+			
+			//$options = array('' => t('Default language (@lang)', array('@lang' => $default_lang->name))) + $options;
+			//unset($options[$default_lang->language]);
+			$options[$default_lang->language] = t('Default language (@lang)', array('@lang' => $default_lang->name));
+			$options = array('' => t('Select language')) + $options;
+		}
+		
+		// Add native name.
+		foreach ($options as $k => $option) {
+			if (!empty($languages[$k]->native)) {
+				$options[$k] .= " ({$languages[$k]->native})";
+			}
+		}
+	}
+	
+	$description = '';
+	if ($ccode == LANGUAGE_FALLBACK_ALL_COUNTRIES) {
+		$description = (!variable_get('language_fallback_country', false)
+					? t('Enable country specific fallback to create different fallbacks for each country.')
+					: t('This is default fallback chain for all countries. It will be used only if no country specific fallback is available.'));
+	}
+	$form[$key]['fallback'][$ccode] = array(
+			'#type' => 'fieldset',
+			'#title' => $country,
+			'#prefix' => '<div id="country-' . $ccode . '-fieldset-wrapper">', // This is unique to each country fallback chain
+			'#suffix' => '</div>',
+			'#description' => $description
+	);
+	
+	$chain = ($langcode ? language_fallback_get_chain($langcode, $ccode, ($scope == 'admin')) : array());
+	
+	if (empty($form_state[$key]['fallback'][$ccode]['chain_length'])) {
+		$form_state[$key]['fallback'][$ccode]['chain_length'] = count($chain) + 1;
+	}
+	
+	for($i = 0; $i < $form_state[$key]['fallback'][$ccode]['chain_length']; $i++) {
+		$form[$key]['fallback'][$ccode][$i] = array(
+				'#type' => 'select',
+				'#options' => $options,
+				'#default_value' => ($i < count($chain) ? $chain[$i] : ''),
+				//'#field_suffix' => '<a href="#" id="language-fallback-remove-' . $ccode . '-' . $i .'">X</a>' 
+		);
+		
+		/*
+		if ($i < count($chain) && $chain[$i] != '') {
+			$form[$key]['fallback'][$ccode]['lang_remove_' . $chain[$i]] = array(
+				//'#type' => 'image_button',
+				//'#src' => '/modules/overlay/images/close.png',
+				//'#attributes' => array('title' => t('Remove @lang from chain', array('@lang' => $options[$chain[$i]]))),
+				'#type' => 'submit',
+				'#attributes' => array('class' => array('float-right')),
+				'#default_value' => t('Remove @lang from chain', array('@lang' => $options[$chain[$i]])),
+				'#submit' => array('language_fallback_country_remove_language_submit'), // This is shared across all countries
+				'#ajax' => array(
+						'callback' => 'language_fallback_country_remove_language_ajax_callback', // This is shared across all countries
+						'wrapper' => 'country-' . $ccode . '-fieldset-wrapper', // This is unique to each country fallback chain
+				),
+			);
+		}
+		*/
+	}
+	
+	/**
+	 * WARNING!
+	 * Due to the nature of AJAX FAPI the submit button value MUST be unique!
+	 * See this: https://drupal.org/node/1342066
+	 * Or try this workaround: http://stackoverflow.com/a/6521617/899092\
+	 * 
+	 * This also applies the the "remove" button below.
+	 */
+	$form[$key]['fallback'][$ccode]['add'] = array(
+			'#type' => 'submit',
+			'#value' => ($country ? t('Add another language for !country', array('!country' => $country)) : t('Add another language')),
+			'#submit' => array('language_fallback_country_add_language_add_another'), // This is shared across all countries
+			'#ajax' => array(
+					'callback' => 'language_fallback_country_add_language_ajax_callback', // This is shared across all countries
+					'wrapper' => 'country-' . $ccode . '-fieldset-wrapper', // This is unique to each country fallback chain
+			),
+	);
+	
+	if ($form_state[$key]['fallback'][$ccode]['chain_length'] > 1) {
+		$form[$key]['fallback'][$ccode]['remove_one'] = array(
+			'#type' => 'submit',
+			'#value' => ($country ? t('Remove last language for !country', array('!country' => $country)) : t('Remove last language')),
+			'#submit' => array('language_fallback_country_remove_last_submit'), // This is shared across all countries
+			'#ajax' => array(
+					'callback' => 'language_fallback_country_add_language_ajax_callback', // This is shared across all countries
+					'wrapper' => 'country-' . $ccode . '-fieldset-wrapper', // This is unique to each country fallback chain
+			),
+		);
+	}
+	
+	if ($ccode != LANGUAGE_FALLBACK_ALL_COUNTRIES) {
+		$form[$key]['fallback'][$ccode]['remove'] = array(
+			'#type' => 'submit',
+			'#value' => t('Remove fallback chain for !country', array('!country' => $country)),
+			'#submit' => array('language_fallback_country_remove_chain'), // This is shared across all countries
+			'#ajax' => array(
+					'callback' => 'language_fallback_country_add_remove_ajax_callback', // This is shared across all countries
+					'wrapper' => 'countries-fieldset-wrapper',
+			),
+		);
+	}
+}
+
+/**
+ * Ajax submit function for "Remove fallback chain for ..." button
+ * @param unknown $form
+ * @param unknown $form_state
+ */
+function language_fallback_country_remove_chain($form, &$form_state) {
+	
+	$key = $form['_key_']['#value'];
+
+	// Country to remove
+	$remove = $form_state['triggering_element']['#parents'][1];
+	if (($index = array_search($remove, $form_state[$key]['fallback']['countries'])) !== false) {
+		// Remove country
+	  unset($form_state[$key]['fallback']['countries'][$index]);
+	}
+	
+	$form_state['rebuild'] = TRUE;
+}
+
+/**
+ * Callback function for ajax calls that add country specific fallback fieldset.
+ * @param unknown $form
+ * @param unknown $form_state
+ */
+function language_fallback_country_add_remove_ajax_callback($form, $form_state) {
+
+	$key = $form['_key_']['#value'];
+	return $form[$key]['fallback'];
+}
+
+/**
+ * Ajax submit function for "Add country specific fallback" button.
+ * @param unknown $form
+ * @param unknown $form_state
+ */
+function language_fallback_country_add_another($form, &$form_state) {
+	
+	$key = $form['_key_']['#value'];
+	$form_state[$key]['fallback']['countries'][] = $form_state['values']['fallback']['country'];
+	
+	$form_state['rebuild'] = TRUE;
+}
+
+/**
+ * Callback function for ajax calls that add language select element to the country fieldset.
+ * @param unknown $form
+ * @param unknown $form_state
+ */
+function language_fallback_country_add_language_ajax_callback($form, $form_state) {
+	//dpm($form_state, 'ajax callback');
+	
+	$key = $form['_key_']['#value'];
+	
+	/**
+	 * See language_fallback_country_add_language_add_another() function
+	 * for explanation of the followin line:
+	 */
+	$id = count($form_state['triggering_element']['#parents']) - 2;
+	$country = $form_state['triggering_element']['#parents'][$id];
+	
+	return $form[$key]['fallback'][$country];
+}
+
+/**
+ * Ajax submit function for "Remove last language from ..." button.
+ * See function language_fallback_country_add_language_add_another() for details.
+ * 
+ * @param unknown $form
+ * @param unknown $form_state
+ */
+function language_fallback_country_remove_last_submit($form, &$form_state) {
+	$key = $form['_key_']['#value'];
+	
+	// See function language_fallback_country_add_language_add_another() for details.
+	$id = count($form_state['triggering_element']['#parents']) - 2;
+	$country = $form_state['triggering_element']['#parents'][$id];
+	$form_state[$key]['fallback'][$country]['chain_length']--;
+	
+	$form_state['rebuild'] = TRUE;
+}
+
+/**
+ * Ajax submit function for "Add another language to ..." button.
+ * @param unknown $form
+ * @param unknown $form_state
+ */
+function language_fallback_country_add_language_add_another($form, &$form_state) {
+	//dpm($form_state, 'ajax submit form_state');
+	//dpm($form, 'ajax submit form');
+	
+	$key = $form['_key_']['#value'];
+	
+	/**
+	 * The element that triggered this callback is "described" here:
+	 * $form_state['triggering_element'].
+	 * We know that this is an array that looks like this:
+	 * array(
+	 *  0 => 'fallback',
+	 *  1 => [COUNTRY],
+	 *  2 => 'add'
+	 * );
+	 * So we need the '1' index to know wchich country needs another select element.
+	 */
+	$id = count($form_state['triggering_element']['#parents']) - 2;
+	$country = $form_state['triggering_element']['#parents'][$id];
+	$form_state[$key]['fallback'][$country]['chain_length']++;
+	
+	$form_state['rebuild'] = TRUE;
+}
+
+function language_fallback_country_add_language_submit($form, &$form_state) {
+	$key = $form['_key_']['#value'];
+	$fallbacks = $form_state['values']['fallback'];
+	$language = $form_state['values']['langcode'];
+	
+//	dpm($form_state);
+	
+	// Delete old entries. New entries will be added only if needed.
+	db_delete('language_fallback')->condition('language', $language, '=')->execute();
+	variable_del('locale_custom_strings_' . $language);
+	$set_variable = false;
+	
+	foreach($fallbacks as $country => $fallback) {
+		// Omit unwanted values.
+		if (!is_array($fallback))
+			continue;
+		
+		// Fix fallback array
+		$fixed = language_fallback_fix_chain($fallback, $language);
+
+		if (count($fixed) > 0) {
+			// This stores our settings
+			db_insert('language_fallback')->fields(array('language' => $language, 'country' => $country, 'chain' => implode('|', $fixed)))->execute();
+				
+			$set_variable = true;
+		}
+	}
+	
+	if ($set_variable) {
+		// This tells drupal to use our fallback mechanism for string translation
+		variable_set('locale_custom_strings_' . $language, new localeWithFallback($language));
+	}
+}
+
+/**
+ * Sanitize fallback chain to remove duplicates and default language.
+ * 
+ * @param array $fallback
+ * @param string $language
+ * @return array clean fallback array. 
+ */
+function language_fallback_fix_chain($fallback, $language = LANGUAGE_NONE) {
+	$valid = &drupal_static(__FUNCTION__);
+	
+	if (empty($valid))
+		$valid = locale_language_list();
+	
+	// Fix fallback array
+	$fixed = array();
+	foreach($fallback as $id => $lang) {
+		// Accept only valid languages.
+		if (!isset($valid[$lang]))
+			continue;
+	
+		// Prevent deadlocks.
+		if ($lang == $language)
+			continue;
+	
+		// Ignore languages that are already added.
+		if (array_search($lang, $fixed) !== false)
+			continue;
+			
+		// Add language to the chain.
+		$fixed[] = $lang;
+	}
+	
+	return $fixed;
+}
\ No newline at end of file
diff -rupN language_fallback-7.x-1.x-dev//language_fallback.block.inc language_fallback-7.x-2.x-dev//language_fallback.block.inc
--- language_fallback-7.x-1.x-dev//language_fallback.block.inc	1970-01-01 00:00:00.000000000 +0000
+++ language_fallback-7.x-2.x-dev//language_fallback.block.inc	2013-08-20 20:46:54.299079074 +0000
@@ -0,0 +1,146 @@
+<?php
+
+/**
+ * Implements hook_block_info().
+ */
+function language_fallback_block_info() {
+	$blocks['country_selection'] = array(
+		'info' => t('Country selection'),
+		'cache' => DRUPAL_NO_CACHE,
+	);
+	
+	$blocks['language_fallback'] = array(
+		'info' => t('Language fallback selection'),
+		'cache' => DRUPAL_NO_CACHE,
+	);
+	return $blocks;
+}
+
+/**
+ * Implements hook_block_view().
+ */
+function language_fallback_block_view($delta = '') {
+	switch ($delta) {
+		case 'country_selection':
+			$block['subject'] = t('Select country');
+			$block['content'] = drupal_get_form('language_fallback_country_select_block_form');
+			return $block;
+		
+		case 'language_fallback':
+			$block['subject'] = t('Select language fallback');
+			$block['content'] = drupal_get_form('language_fallback_fallback_select_block_form');
+			return $block;
+	}
+}
+
+/**
+ * Callback to generate form for user fallback block
+ * @param unknown $form_id
+ * @param unknown $form_state
+ * @return multitype:string NULL
+ */
+function language_fallback_fallback_select_block_form($form_id, &$form_state) {
+	$form = array();
+	$key = '_fb_';
+	$form['_key_'] = array('#value' => $key);
+	$form[$key] = array('#tree' => 'true');
+
+	/* 'key', langcode_view' and 'langcode' are to satisfy form building function.
+	 * See language_fallback_get_fallback_form function for details.
+	 */
+	$form['langcode_view'] = array();
+	$form['langcode'] = array('#value' => LANGUAGE_NONE);
+	
+	// Buld language fallback form with our reusable function.
+	language_fallback_get_fallback_form($form, $form_state, $key, LANGUAGE_FALLBACK_ALL_COUNTRIES, false, 'block');
+
+	// Get information about current fallback chain
+	$description = '';
+	$prop = 'name'; // Change to "native" to use native names;
+	if (!isset($_SESSION['language_fallback']['user_chain'])) {
+		global $language;
+		$global_chain = language_fallback_get_chain($language->language);
+		$all_languages = locale_language_list($prop, true);
+		$display_chain = array();
+		foreach($global_chain as $langcode) {
+			$display_chain[] = $all_languages[$langcode];
+		}
+		
+		if (count($display_chain) > 0)
+			$description = t('Language fallback for content unavailable in %current is %fallback. Set your own fallback here.', array('%current' => $language->{$prop}, '%fallback' => implode(" => ", $display_chain)));
+		else 
+			$description = t('Language fallback for content unavailable in %current is not set. Set your own fallback here.', array('%current' => $language->{$prop}));
+	} else
+		$description = t('Change language fallback for content unavailable in %current.', array('%current' => $language->{$prop}));
+	
+	/* Replace description from chain selection fieldset
+	 * with information about currently defined fallback
+	 */
+	$form[$key]['fallback'][LANGUAGE_FALLBACK_ALL_COUNTRIES]['#description'] = $description;
+	
+	$form['submit'] = array(
+		'#type' => 'submit',
+		'#value' => t('Save')
+	);
+	
+	return $form;
+}
+
+/**
+ * Callback function for user fallback selection form submit
+ * @param unknown $form
+ * @param unknown $form_state
+ */
+function language_fallback_fallback_select_block_form_submit($form, $form_state) {
+	// Read and fix user defined fallback chain.
+	$key = $form['_key_']['#value'];
+	$chain = language_fallback_fix_chain($form_state['values'][$key]['fallback'][LANGUAGE_FALLBACK_ALL_COUNTRIES]);
+	
+	// If user provided fallback chain is OK, then store in a session variable.
+	if (count($chain) > 0)
+		$_SESSION['language_fallback']['user_chain'] = $chain;
+	else if (isset($_SESSION['language_fallback']['user_chain']))
+		unset ($_SESSION['language_fallback']['user_chain']);
+}
+
+/**
+ * Callback to generate form for country selection block.
+ * @param unknown $form_id
+ * @param unknown $form_state
+ * @return multitype:multitype:NULL  multitype:string NULL  multitype:string NULL unknown
+ */
+function language_fallback_country_select_block_form($form_id, $form_state) {
+	
+	/* There is no need for fancy AJAXification.
+	 * The page reload is needed anyway to view
+	 * content in another language.
+	 */
+	$form = array();
+	
+	$countries = language_fallback_get_countries();
+	
+	if (count($countries) < 2) {
+		$form[] = array('#markup' => t('Country selection is disabled'));
+	} else {
+		$form['country'] = array(
+				'#type' => 'select',
+				'#options' => $countries,
+				'#default_value' => language_fallback_get_country()
+		);
+		$form['submit'] = array(
+				'#type' => 'submit',
+				'#value' => t('Save')
+		);
+	}
+	
+	return $form;
+}
+
+/**
+ * Callback function for user country selection form submit
+ * @param unknown $form
+ * @param unknown $form_state
+ */
+function language_fallback_country_select_block_form_submit($form, $form_state) {
+	language_fallback_set_country($form_state['values']['country']);
+}
\ No newline at end of file
diff -rupN language_fallback-7.x-1.x-dev//language_fallback.fallback.inc language_fallback-7.x-2.x-dev//language_fallback.fallback.inc
--- language_fallback-7.x-1.x-dev//language_fallback.fallback.inc	2013-04-20 07:51:39.000000000 +0000
+++ language_fallback-7.x-2.x-dev//language_fallback.fallback.inc	2013-08-20 20:46:54.299079074 +0000
@@ -8,13 +8,12 @@ class localeWithFallback implements Arra
 
   private $langcode;
   private $cache = array();
-  private $fallback;
+  private $fallbacks;
 
-  public function __construct($langcode, $fallback = '') {
+  public function __construct($langcode) {
     $languages = language_list();
     $this->langcode = isset($languages[$langcode]) ? $langcode : language_default('language');
     $this->cache = array();
-    $this->fallback = isset($languages[$fallback]) ? $fallback : language_default('langauge');
   }
 
   public function offsetExists($offset) {
@@ -23,7 +22,26 @@ class localeWithFallback implements Arra
 
   public function &offsetGet($offset) {
     if (!isset($this->cache[$offset])) {
-      $this->cache[$offset] = new localeContextWithFallback($this->langcode, $this->fallback, $offset);
+    	/**
+    	 * Fallback chains are red and cached with the first call to offsetGet() function.
+    	 * This is on purpouse, to prevent chains to be stored in database when
+    	 * variable_set() function is called on language sttings form.
+    	 */
+    	if (!isset($this->fallbacks)) {
+    		/**
+    		 * WARNING!
+    		 * On some rare occasions when drupal is not fully bootstrapped
+    		 * the function language_fallback_get_chain might not be available.
+    		 * This may happen on some administration pages so it should be
+    		 * safe to just skip all defined fallbacks.
+    		 */
+    		if (function_exists('language_fallback_get_chain'))
+    			$this->fallbacks = language_fallback_get_chain($this->langcode);
+    		else
+    			$this->fallbacks = array();
+    	}
+    	
+      $this->cache[$offset] = new localeContextWithFallback($this->langcode, $this->fallbacks, $offset);
     }
     return $this->cache[$offset];
   }
@@ -53,19 +71,19 @@ class localeContextWithFallback implemen
   private $context = '';
   private $fallback;
 
-  public function __construct($langcode, $fallback, $context) {
+  public function __construct($langcode, $fallbacks, $context) {
     $languages = language_list();
     $this->first_language = isset($languages[$langcode]) ? $langcode : language_default('language');
-    $this->second_language = isset($languages[$fallback]) ? $fallback : language_default('language');
+    $this->second_language = $fallbacks ? array_shift($fallbacks) : language_default('language');
     $this->context = $context;
-    $second_lang_obj = $languages[$this->second_language];
+    
     // If the fallback is the default language, we don't need a
     // localeContextWithFallback since the default language does not have a
     // fallback. Otherwise we use a localeContextWithFallback so we gradually
     // fall back to the default language (cascading fallbacks, see
     // http://drupal.org/node/1877880 for more info.
     if ($this->second_language != language_default('language')) {
-      $this->fallback = new localeContextWithFallback($this->second_language, isset($languages[$second_lang_obj->fallback]) ? $second_lang_obj->fallback : language_default('language'), $context);
+      $this->fallback = new localeContextWithFallback($this->second_language, $fallbacks, $context);
     }
   }
 
diff -rupN language_fallback-7.x-1.x-dev//language_fallback.info language_fallback-7.x-2.x-dev//language_fallback.info
--- language_fallback-7.x-1.x-dev//language_fallback.info	2013-04-20 13:11:06.000000000 +0000
+++ language_fallback-7.x-2.x-dev//language_fallback.info	2013-08-20 20:46:54.299079074 +0000
@@ -7,9 +7,4 @@ dependencies[] = locale
 
 files[] = language_fallback.fallback.inc
 
-; Information added by drupal.org packaging script on 2013-04-20
-version = "7.x-1.0+2-dev"
-core = "7.x"
-project = "language_fallback"
-datestamp = "1366463466"
-
+configure = admin/config/regional/language_fallback
\ No newline at end of file
diff -rupN language_fallback-7.x-1.x-dev//language_fallback.install language_fallback-7.x-2.x-dev//language_fallback.install
--- language_fallback-7.x-1.x-dev//language_fallback.install	2013-04-20 07:51:39.000000000 +0000
+++ language_fallback-7.x-2.x-dev//language_fallback.install	2013-08-20 20:46:54.299079074 +0000
@@ -1,49 +1,120 @@
-<?php
-
-/**
- * @file
- * Schema, update and install function for Language Fallback.
- */
-
-/**
- * Implements hook_schema_alter().
- */
-function language_fallback_schema_alter(&$schema) {
-  if (isset($schema['languages'])) {
-    $schema['languages']['fields']['fallback'] = array(
-      'type' => 'varchar',
-      'length' => 12,
-      'not null' => TRUE,
-      'default' => '',
-      'description' => "Language code, e.g. 'de' or 'en-US'.",
-    );
-  }
-}
-
-/**
- * Implements hook_install().
- */
-function language_fallback_install() {
-  // Add the field to the languages table so that the 'fallback' property is set
-  // on every object in the array returned by language_list().
-  db_add_field(
-    'languages',
-    'fallback',
-    array(
-      'type' => 'varchar',
-      'length' => 12,
-      'not null' => TRUE,
-      'default' => '',
-      'description' => "Language code, e.g. 'de' or 'en-US'.",
-    )
-  );
-  drupal_static_reset('language_list');
-}
-
-/**
- * Implements hook_uninstall().
- */
-function language_fallback_uninstall() {
-  db_drop_field('languages', 'fallback');
-  drupal_static_reset('language_list');
-}
+<?php
+
+/**
+ * @file
+ * Schema, update and install function for Language Fallback.
+ */
+
+/**
+ * Implements hook_schema().
+ */
+function language_fallback_schema() {
+  $schema['language_fallback'] = array(
+    'description' => 'Contains fallback chains for languages',
+    'fields' => array(
+      'language' => array(
+        'description' => 'Lang code',
+        'type' => 'varchar',
+        'length' => 8,
+        'not null' => true,
+      ),
+    	'country' => array(
+      	'description' => 'Visitor country code or "all" for all countries',
+    		'type' => 'varchar',
+    		'length' => 3,
+    		'not null' => true,
+    		'default' => 'all'
+      ),
+    	'chain' => array(
+      	'description' => 'Fallback chain ("|" delimiter)',
+    		'type' => 'varchar',
+    		'length' => 255,
+    		'not null' => true,
+      ),
+    ),
+    'primary key' => array('language', 'country'),
+  );
+  return $schema;
+}
+
+/**
+ * Implements hook_disable()
+ *
+ * WARNING!
+ * It is mandatory to remove all "locale_custom_strings_ID" variables!
+ * After this module is disabled the class localeWithFallback is no longer
+ * available but these variables hold reference to it.
+ */
+function language_fallback_disable(){
+	/**
+	 * We do not know all our variable names because they are created dynamically.
+	 * So to remove unwanted variables we first need to know their names.
+	 *
+	 * Our variables are "locale_custom_strings_ID"
+	 * and their values contain "localeWithFallback" stirng.
+	 *
+	 * So the trick is to read all variable names from "variable" table
+	 * that met the criteria above.
+	 */
+	
+	$result = db_update("variable")
+						->expression('name', "CONCAT('_', name)")
+						->condition('name', 'locale_custom_string_%', 'LIKE')
+						->condition('value', '%localeWithFallback%', 'LIKE')
+						->execute();
+}
+
+/**
+ * Implements hook_enable()
+ * 
+ * Revert back "locale_custom_strings_ID" variable names.
+ * See language_fallback_disable() for details.
+ */
+function language_fallback_enable(){
+	$result = db_update("variable")
+						->expression('name', "REPLACE(name, '_locale_custom_strings_', 'locale_custom_strings_')")
+						->condition('name', '_locale_custom_string_%', 'LIKE')
+						->condition('value', '%localeWithFallback%', 'LIKE')
+						->execute();
+}
+
+/**
+ * Implements hook_uninstall()
+ * 
+ * WARNING!
+ * It is mandatory to remove all "locale_custom_strings_ID" variables!
+ * After this module is disabled the class localeWithFallback is no longer
+ * available but these variables hold reference to it.
+ */
+function language_fallback_uninstall(){
+	
+	/**
+	 * We do not know all our variable names because they are created dynamically.
+	 * So to remove unwanted variables we first need to know their names.
+	 * 
+	 * Our variables are "locale_custom_strings_ID" but they were renamed on
+	 * hook_disable to "_locale_custom_strings_ID".
+	 * and their values contain "localeWithFallback" stirng.
+	 * 
+	 * So the trick is to read all variable names from "variable" table
+	 * that met the criteria above.
+	 */
+	$result = db_select("variable", "v")
+						->fields('v', array('name'))
+						->condition('name', '_locale_custom_string_%', 'LIKE')
+						->condition('value', '%localeWithFallback%', 'LIKE')
+						->execute();
+	
+	while($name = $result->fetchField()){
+		variable_del($name);
+	}
+	
+	if (isset($_SESSION['language_fallback']))
+		unset($_SESSION['language_fallback']);
+	
+	/**
+	 * WARNING!
+	 * This is also very important as variables are cached!
+	 */
+	cache_clear_all('variables', 'cache_bootstrap');
+}
\ No newline at end of file
diff -rupN language_fallback-7.x-1.x-dev//language_fallback.module language_fallback-7.x-2.x-dev//language_fallback.module
--- language_fallback-7.x-1.x-dev//language_fallback.module	2013-04-20 07:51:39.000000000 +0000
+++ language_fallback-7.x-2.x-dev//language_fallback.module	2013-08-20 20:46:54.299079074 +0000
@@ -1,65 +1,188 @@
 <?php
 
+define('LANGUAGE_FALLBACK_ALL_COUNTRIES', 'all');
+
+/**
+ * Administration forms are moved the separate file for better readability.
+ * The same goes for blocks.
+ */
+module_load_include('inc', 'language_fallback', 'language_fallback.admin');
+module_load_include('inc', 'language_fallback', 'language_fallback.block');
+
+/**
+ * Implements hook_menu().
+ */
+function language_fallback_menu() {
+	$items = array();
+
+	$items['admin/config/regional/language_fallback'] = array(
+			'title' => 'Language fallback',
+			'description' => 'Configuration for Language fallback module',
+			'page callback' => 'drupal_get_form',
+			'page arguments' => array('language_fallback_settings_form'),
+			'access arguments' => array('access administration pages'),
+			'type' => MENU_NORMAL_ITEM,
+			'file' => 'language_fallback.settings.inc'
+	);
+
+	return $items;
+}
+
 /**
- * @file
- * Hook implementations for the Language Fallback module.
+ * Implements Drupal hook_language_fallback_candidates_alter().
+ * 
+ * This function provides fallback features for entities.
+ * 
+ * @param $fallback_candidates: An array of language codes whose order will determine the language fallback order.
  */
+function language_fallback_language_fallback_candidates_alter(array &$fallback_candidates) {
+	
+	// Check if entity fallback is enabled
+	if (!variable_get('language_fallback_entity', false))
+		return;
+	
+	global $language;
+	
+	if (LANGUAGE_NONE == $language->language)
+		return;  // There is no fallback for "Undeterminated" language.
+	
+	// Get fallback chain for current language
+	$fallbacks = language_fallback_get_chain($language->language);
+	
+	if (count($fallbacks) > 0) {
+		$fallback_candidates = $fallbacks;
+	}
+}
 
 /**
- * Implements hook_form_alter().
+ * Get fallback chain for specified language.
+ * If you want to hook up to language fallback module - this is the place to start.
+ * 
+ * @param string $lang: Language code for which fallback chain is needed.
+ * @param string $country: Country code for which fallback chain is needed.
+ * @param bool $strict: Used only internally.
+ * @return array of languages. An empty array is returned if no fallback is defined.
  */
-function language_fallback_form_alter(&$form, &$form_state, $form_id) {
-  if ($form_id == 'locale_languages_predefined_form' || $form_id == 'locale_languages_custom_form' || $form_id == 'locale_languages_edit_form') {
-    // Store language for edit form or FALSE on create forms.
-    $editmode = isset($form['langcode_view']) ? $form['langcode']['#value'] : FALSE;
-    $default_lang = language_default();
-    $languages = language_list();
-    $options = locale_language_list();
-
-    // Alter label of default language.
-    if (isset($options[$default_lang->language])) {
-      $options = array('' => t('Default language (@lang)', array('@lang' => $default_lang->name))) + $options;
-      unset($options[$default_lang->language]);
-    }
-
-    // Add native name.
-    foreach ($options as $key => $option) {
-      if (!empty($languages[$key]->native)) {
-        $options[$key] .= " ({$languages[$key]->native})";
-      }
-    }
-
-    // Add select to create/edit form.
-    $key = $form_id == 'locale_languages_predefined_form' ? 'language list' : 'custom language';
-    $form[$key]['fallback'] = array(
-      '#type' => 'select',
-      '#title' => t('Language fallback'),
-      '#default_value' => '',
-      '#options' => $options,
-      '#description' => t('This language will be used as a fallback for text that has no translation in the selected language.'),
-    );
-    $form['#submit'][] = 'language_fallback_add_language_submit';
-
-    // Remove current language in edit mode, set default.
-    if ($editmode) {
-      unset($form[$key]['fallback']['#options'][$editmode]);
-      $form[$key]['fallback']['#default_value'] = $languages[$editmode]->fallback;
-    }
-  }
+function language_fallback_get_chain($lang, $country = false, $strict = false){
+	
+	if (!$strict) {
+		/* User defined fallabck always takes precedence before any other fallback
+		 * But we can't use it on administration pages!
+		 */
+		$user_cahin = language_fallback_get_user_chain();
+		
+		if (count($user_cahin) > 0)
+			return  $user_cahin;
+	}
+	
+	$chains = &drupal_static(__FUNCTION__);
+	
+	if (!isset($chains))
+		$chains = array();
+	
+	if ($country === false)
+		$country = language_fallback_get_country();
+	
+	// Check cache - maybe the chain was already loaded
+	if (!isset($chains[$lang][$country])) {
+		
+		$chain = db_select('language_fallback', 'f')
+							->fields('f', array('chain'))
+							->condition('language', $lang, '=')
+							->condition('country', $country, '=')
+							->execute()
+							->fetchField();
+		
+		if ($chain) {
+			// We have a fallback chain for this country
+			$chains[$lang][$country] = explode("|", $chain); // for better readability fallback chains are stored in a string separated with "|" character.
+		}
+		else if ($country == LANGUAGE_FALLBACK_ALL_COUNTRIES) {
+			// We don't have a fallback chain for all countries
+			$chains[$lang][$country] = array();
+		}
+		else if ($strict) {
+			// Do not use default fallback if no fallback was found.
+			return array();
+		}
+		else {
+			// We dno't have fallback so we try default fallback for all countries
+			// WARNING! Recurency!
+			$chains[$lang][$country] = language_fallback_get_chain($lang, LANGUAGE_FALLBACK_ALL_COUNTRIES);
+		} 
+	}
+
+	return $chains[$lang][$country];
 }
 
 /**
- * Form submit callback.
- */
-function language_fallback_add_language_submit($form, &$form_state) {
-  db_update('languages')
-    ->fields(array('fallback' => $form_state['values']['fallback']))
-    ->condition('language', $form_state['values']['langcode'])
-    ->execute();
-  if (!empty($form_state['values']['fallback'])) {
-    variable_set('locale_custom_strings_' . $form_state['values']['langcode'], new localeWithFallback($form_state['values']['langcode'], $form_state['values']['fallback']));
-  }
-  else {
-    variable_del('locale_custom_strings_' . $form_state['values']['langcode']);
-  }
+ * Get user defined fallback chain if it exists.
+ * If user defined chain does not exists an empty array is returned.
+ * 
+ * @return array: user defined fallback chain.
+ */
+function language_fallback_get_user_chain() {
+	if (isset($_SESSION['language_fallback']['user_chain'])
+		&& is_array($_SESSION['language_fallback']['user_chain'])
+		&& count($_SESSION['language_fallback']['user_chain']) > 0)
+			return $_SESSION['language_fallback']['user_chain'];
+	
+	return array();
 }
+
+/**
+ * Get country code for fallback check.
+ * 
+ * @return string Either country code for fallback chain lookup or empty string if no country is specified for current visitor.
+ */
+function language_fallback_get_country(){
+	// Check if country specific fallback is enabled
+	if (variable_get('language_fallback_country', false)){
+		
+		// Check if country is stored in a session variable
+		if (isset($_SESSION['language_fallback']['country'])) {
+			return $_SESSION['language_fallback']['country'];
+		}	else if (function_exists('smart_ip_get_location')) {
+			
+			// Use smart IP to get user location
+			$country = smart_ip_get_location(ip_address());
+			if ($country !== false)
+				$_SESSION['language_fallback']['country'] = strtoupper($country['country_code']);
+			else
+				$_SESSION['language_fallback']['country'] = LANGUAGE_FALLBACK_ALL_COUNTRIES;
+		} else
+			$_SESSION['language_fallback']['country'] = LANGUAGE_FALLBACK_ALL_COUNTRIES;
+		
+		return $_SESSION['language_fallback']['country'];
+	}
+	
+	return LANGUAGE_FALLBACK_ALL_COUNTRIES;
+}
+
+/**
+ * Set country code for current user.
+ * @param string $country
+ */
+function language_fallback_set_country($country = false){
+	if ($country)
+		$country = LANGUAGE_FALLBACK_ALL_COUNTRIES;
+	
+	$_SESSION['language_fallback']['country'] = $country;
+}
+
+/**
+ * Get list of countries for country specific fallback.
+ * @return array with country names keyed by country code.
+ */
+function language_fallback_get_countries(){
+	$ret = array(
+			LANGUAGE_FALLBACK_ALL_COUNTRIES => t('All countries')
+	);
+
+	if (variable_get('language_fallback_country', false)){
+
+		$ret += country_get_list();
+	}
+
+	return $ret;
+}
\ No newline at end of file
diff -rupN language_fallback-7.x-1.x-dev//language_fallback.settings.inc language_fallback-7.x-2.x-dev//language_fallback.settings.inc
--- language_fallback-7.x-1.x-dev//language_fallback.settings.inc	1970-01-01 00:00:00.000000000 +0000
+++ language_fallback-7.x-2.x-dev//language_fallback.settings.inc	2013-08-20 20:46:54.299079074 +0000
@@ -0,0 +1,20 @@
+<?php
+
+function language_fallback_settings_form($form, &$form_state) {
+	
+	$form['language_fallback_entity'] = array(
+		'#type' => 'checkbox',
+		'#title' => t('Enable entity translation fallback.'),
+		'#description' => t('Entity translation module must be enabled for this feature to work.'),
+		'#default_value' => variable_get('language_fallback_entity', false)
+	);
+	
+	$form['language_fallback_country'] = array(
+		'#type' => 'checkbox',
+		'#title' => t('Enable country specific fallback.'),
+		'#description' => t('This feature allows you to create different fallbacks according to the user country. It is recommended to install Smart IP module to automatically map user IP to country. Otherwise users will have to use country selection block provided by this module.'),
+		'#default_value' => variable_get('language_fallback_country', false)
+	);
+	
+	return system_settings_form($form);
+}
\ No newline at end of file
