Index: wp-admin/includes/class-wp-upgrader.php
===================================================================
--- wp-admin/includes/class-wp-upgrader.php	(revision 23265)
+++ wp-admin/includes/class-wp-upgrader.php	(working copy)
@@ -165,7 +165,7 @@
 	function install_package($args = array()) {
 		global $wp_filesystem;
 		$defaults = array( 'source' => '', 'destination' => '', //Please always pass these
-						'clear_destination' => false, 'clear_working' => false,
+						'clear_destination' => false, 'clear_working' => false, 'abort_if_destination_exists' => true,
 						'hook_extra' => array());
 
 		$args = wp_parse_args($args, $defaults);
@@ -224,7 +224,7 @@
 				return $removed;
 			else if ( ! $removed )
 				return new WP_Error('remove_old_failed', $this->strings['remove_old_failed']);
-		} elseif ( $wp_filesystem->exists($remote_destination) ) {
+		} elseif ( $abort_if_destination_exists && $wp_filesystem->exists($remote_destination) ) {
 			//If we're not clearing the destination folder and something exists there already, Bail.
 			//But first check to see if there are actually any files in the folder.
 			$_files = $wp_filesystem->dirlist($remote_destination);
@@ -272,6 +272,7 @@
 		$defaults = array( 	'package' => '', //Please always pass this.
 							'destination' => '', //And this
 							'clear_destination' => false,
+							'abort_if_destination_exists' => true, // Abort if the Destination directory exists, Pass clear_destination as false please
 							'clear_working' => true,
 							'is_multi' => false,
 							'hook_extra' => array() //Pass any extra $hook_extra args here, this will be passed to any hooked filters.
@@ -318,6 +319,7 @@
 											'source' => $working_dir,
 											'destination' => $destination,
 											'clear_destination' => $clear_destination,
+											'abort_if_destination_exists' => $abort_if_destination_exists,
 											'clear_working' => $clear_working,
 											'hook_extra' => $hook_extra
 										) );
@@ -412,6 +414,7 @@
 		// Force refresh of plugin update information
 		delete_site_transient('update_plugins');
 		wp_cache_delete( 'plugins', 'plugins' );
+		do_action( 'upgrader_process_complete', $this, 'install-plugin', $package );
 
 		return true;
 	}
@@ -457,6 +460,7 @@
 		// Force refresh of plugin update information
 		delete_site_transient('update_plugins');
 		wp_cache_delete( 'plugins', 'plugins' );
+		do_action( 'upgrader_process_complete', $this, 'update-plugin', $plugin );
 	}
 
 	function bulk_upgrade($plugins) {
@@ -539,6 +543,7 @@
 		// Force refresh of plugin update information
 		delete_site_transient('update_plugins');
 		wp_cache_delete( 'plugins', 'plugins' );
+		do_action( 'upgrader_process_complete', $this, 'bulk-upgrade-plugins', $plugins );
 
 		return $results;
 	}
@@ -764,6 +769,7 @@
 
 		// Force refresh of theme update information
 		wp_clean_themes_cache();
+		do_action( 'upgrader_process_complete', $this, 'install-theme', $package );
 
 		return true;
 	}
@@ -810,6 +816,7 @@
 
 		// Force refresh of theme update information
 		wp_clean_themes_cache();
+		do_action( 'upgrader_process_complete', $this, 'upgrade-theme', $theme );
 
 		return true;
 	}
@@ -897,6 +904,7 @@
 
 		// Force refresh of theme update information
 		wp_clean_themes_cache();
+		do_action( 'upgrader_process_complete', $this, 'bulk-upgrade-themes', $themes );
 
 		return $results;
 	}
@@ -1067,11 +1075,140 @@
 		if ( ! function_exists( 'update_core' ) )
 			return new WP_Error( 'copy_failed_space', $this->strings['copy_failed_space'] );
 
-		return update_core($working_dir, $wp_dir);
+		$result = update_core($working_dir, $wp_dir);
+		do_action( 'upgrader_process_complete', $this, 'upgrade-core', $result );
+		return $result;
 	}
 
 }
 
+add_action( 'upgrader_process_complete', array( 'Language_Pack_Upgrader', 'async_upgrade' ), 20, 3);
+class Language_Pack_Upgrader extends WP_Upgrader {
+	
+	var $result;
+	var $bulk = true;
+
+	static function async_upgrade( $upgrader, $context, $package ) {
+		// Don't do Language updates on language updates on language updates..
+		if ( $upgrader instanceof Language_Pack_Upgrader )
+			return;
+
+		echo '<!-- start languages --><div style="padding-left: 3em; border-left: 5px solid grey">';
+
+		$lp_upgrader = new Language_Pack_Upgrader( new Headerless_Skin() );
+
+		$lp_upgrader->upgrade();
+
+		echo '</div><!-- end of languages -->';
+	}
+	function upgrade_strings() {
+		$this->strings['starting_upgrade'] = __( 'Some of your language files need updating, Sit tight for a few more seconds while we update them as well.' );
+		$this->strings['up_to_date'] = __('The language is up to date.'); // We need to silently skip this case
+		$this->strings['no_package'] = __('Update package not available.');
+		$this->strings['downloading_package'] = __('Downloading language update from <span class="code">%s</span>&#8230;');
+		$this->strings['unpack_package'] = __('Unpacking the update&#8230;');
+		$this->strings['remove_old'] = __('Removing the old version of the language&#8230;');
+		$this->strings['remove_old_failed'] = __('Could not remove the old language.');
+		$this->strings['process_failed'] = __('Language update failed.');
+		$this->strings['process_success'] = __('Language updated successfully.');
+	}
+
+	function upgrade() {
+		return $this->bulk_upgrade();
+	}
+
+	function bulk_upgrade() {
+
+		$this->init();
+		$this->upgrade_strings();
+
+		$language_updates = get_language_updates();
+
+		if ( empty($language_updates) )
+			return true;
+		
+		$this->skin->feedback( 'starting_upgrade' );
+
+		add_filter('upgrader_source_selection', array(&$this, 'check_package'), 10, 3 );
+
+		$this->skin->header();
+
+		// Connect to the Filesystem first.
+		$res = $this->fs_connect( array(WP_CONTENT_DIR, WP_LANG_DIR) );
+		if ( ! $res ) {
+			$this->skin->footer();
+			return false;
+		}
+
+		$results = array();
+
+		$this->update_count = count($language_updates);
+		$this->update_current = 0;
+		foreach ( $language_updates as $language_update ) {
+
+			$destination = WP_LANG_DIR;
+			if ( 'plugin' == $language_update->type ) {
+				$destination .= '/plugins';
+			} elseif ( 'theme' == $language_update->type ) {
+				$destination .= '/themes';
+			}
+
+			$this->update_current++;
+
+			$options = array(
+				'package' => $language_update->package,
+				'destination' => $destination,
+				'clear_destination' => false,
+				'abort_if_destination_exists' => false, // We expect the destination to exist.
+				'clear_working' => true,
+				'is_multi' => true,
+				'hook_extra' => array(
+					'language_update_type' => $language_update->type,
+					'language_update' => $language_update,
+				)
+			);
+
+			$result = $this->run($options);
+
+			$results[] = $this->result;
+
+			// Prevent credentials auth screen from displaying multiple times
+			if ( false === $result )
+				break;
+		} //end foreach $language_updates
+
+		// Cleanup our hooks, in case something else does a upgrade on this connection.
+		remove_filter('upgrader_source_selection', array(&$this, 'check_package'), 10, 2 );
+
+		return $results;
+	}
+
+	function check_package( $source, $remote_source ) {
+		global $wp_filesystem;
+
+		if ( is_wp_error($source) )
+			return $source;
+
+		// Check the folder contains a valid language
+		$files = $wp_filesystem->dirlist( $remote_source );
+
+		// Check to see if a .po and .mo exist in the folder..
+		$po = $mo = false;
+		foreach ( (array)$files as $file => $filedata ) {
+			if ( '.po' == substr( $file, -3) )
+				$po = true;
+			elseif ( '.mo' == substr($file, -3) )
+				$mo = true;
+		}
+
+		if ( ! $mo || ! $po )
+			return new WP_Error( 'incompatible_archive', $this->strings['incompatible_archive'], __( 'The language pack is missing either the <code>.po</code> or <code>.mo</code> files.' ) );
+
+		return $source;
+	}
+
+}
+
 /**
  * Generic Skin for the WordPress Upgrader classes. This skin is designed to be extended for specific purposes.
  *
@@ -1160,6 +1297,20 @@
 }
 
 /**
+ * A basic Upgrader skin which doesn't have any Header/Footers
+ *
+ * @package WordPress
+ * @subpackage Upgrader
+ * @since 3.4.0
+ */
+class Headerless_Skin extends WP_Upgrader_Skin {
+	function before() {}
+	function after() {}
+	function header() {}
+	function footer() {}
+}
+
+/**
  * Plugin Upgrader Skin for WordPress Plugin Upgrades.
  *
  * @TODO More Detailed docs, for methods as well.
Index: wp-admin/includes/update.php
===================================================================
--- wp-admin/includes/update.php	(revision 23265)
+++ wp-admin/includes/update.php	(working copy)
@@ -308,3 +308,54 @@
 	echo "<div class='update-nag'>$msg</div>";
 }
 add_action( 'admin_notices', 'maintenance_nag' );
+
+// Retrieves a list of all language updates available.
+function get_language_updates() {
+
+	$updates = array();
+	foreach ( array( 'update_core' => 'core', 'update_plugins' => 'plugin', 'update_themes' => 'theme' ) as $transient => $type ) {
+
+		$transient = get_site_transient( $transient );
+		if ( empty( $transient->language_updates ) )
+			continue;
+
+		foreach ( (array)$transient->language_updates as $update ) {
+			if ( empty( $update->type ) )
+				$update->type = $type;
+
+			$updates[] = $update;
+		}
+	}
+
+	return $updates;
+
+/*	// Expected content:
+	return array(
+			(object)array(
+						'type' => 'core',
+						'lang' => 'de_DE', // The language it is.. This isn't currently used anywhere
+						'version' => '2012-02-18 08:48:04', // New Version - Nothing looks at this either.
+						'package' => 'http://core-downloads/core-de_DE.zip'
+						),
+			(object)array(
+						'type' => 'plugin', // The type of the upgrade, 'plugin' or 'theme' will throw files into that folder, any other name (ie. 'core') is thrown into WP_LANG_DIR.. 
+						'slug' => 'akismet', // not used.
+						'for' => 'akismet/akismet.php', // again, not used yet.
+						'lang' => 'de_DE',
+						'version' => '2012-02-18 08:48:04',
+						'package' => 'http://core-downloads/akismet-de_DE.zip'
+						),
+			(object)array(
+						'type' => 'theme',
+						'slug' => 'twentytwelve',
+						'lang' => 'de_DE',
+						'version' => '2012-02-18 08:48:04',
+						'package' => 'http://core-downloads/twentytwelve-de_DE.zip' // And no, this isn't a translation of 2012, I believe it's a out of date Core set.
+						),
+		);
+Zip file expected format:
+./akismet-de_DE.mo
+./akismet-de_DE.po
+(ie. Language files bundled together in root, other files such as .php and .css would be kept with it)
+*/
+}
\ No newline at end of file
Index: wp-includes/l10n.php
===================================================================
--- wp-includes/l10n.php	(revision 23265)
+++ wp-includes/l10n.php	(working copy)
@@ -550,4 +550,62 @@
 	}
 
 	return $languages;
-}
\ No newline at end of file
+}
+
+// Returns a list of translation files and their details.
+function get_installed_language_files( $type = false ) {
+        $language_files = array();
+
+        if ( ! is_dir( WP_LANG_DIR ) )
+                return $language_files;
+
+        $dirs_to_check = array( WP_LANG_DIR );
+        foreach ( (array)glob( WP_LANG_DIR . '/*', GLOB_ONLYDIR) as $dir )
+                $dirs_to_check[] = $dir;
+
+        foreach ( $dirs_to_check as $dir ) {
+                foreach ( (array)glob( $dir . '/*.po') as $lang_file ) {
+                        $lang_file = str_replace( trailingslashit(WP_LANG_DIR), '', $lang_file );
+
+                        $file_data = get_translation_file_data( $lang_file );
+                        $file_data['file'] = basename( $lang_file );
+                        $file_data['type'] = dirname( $lang_file );
+                        if ( '.' == $file_data['type'] )
+                                $file_data['type'] = 'core';                            
+
+                        if ( $type && $type != $file_data['type'] )
+                                continue;
+
+                        if ( preg_match( '!(.*?)-?([a-z]{2}_[a-z]{2})?\.po!i', $file_data['file'], $lang_match ) ) {
+                                $file_data['slug'] = $lang_match[1];
+                                if ( isset( $lang_match[2] ) )
+                                        $file_data['language'] = $lang_match[2];
+                        }
+
+                        $language_files[] = $file_data;
+
+                }
+        }
+
+        return $language_files;
+}
+
+// retrieves the date/generator/project-id headers from a po file relative to WP_LANG_DIR
+function get_translation_file_data( $file ) {
+        // Just incase someone calls with the machine file instead of the textual version
+        $file = preg_replace( '!\.mo$!i', '.po', $file );
+
+        // Lets just assume that it's within the WP_LANG_DIR for now
+        // if we keep track of the loaded text domains later, we can probably include a lookup for the location of the files instead.
+        $data = get_file_data(  WP_LANG_DIR . '/' . $file, array(
+                'date' => '"PO-Revision-Date',
+                'generator' => '"X-Generator',
+                'project-id' => '"Project-Id-Version'
+        ) );
+
+        // Strip the .po field endings off the values, which may, or may not, include a textual \n
+        foreach ( (array)$data as $key => $value )
+                $data[ $key ] = preg_replace( '!(\\\n)?"$!', '', $value);
+
+        return $data;
+ }
\ No newline at end of file
Index: wp-includes/update.php
===================================================================
--- wp-includes/update.php	(revision 23265)
+++ wp-includes/update.php	(working copy)
@@ -64,6 +64,8 @@
 		$wp_install = home_url( '/' );
 	}
 
+	$installed_languages = get_installed_language_files( 'core' );
+
 	$query = array(
 		'version'           => $wp_version,
 		'php'               => $php_version,
@@ -80,13 +82,14 @@
 	$options = array(
 		'timeout' => ( ( defined('DOING_CRON') && DOING_CRON ) ? 30 : 3 ),
 		'user-agent' => 'WordPress/' . $wp_version . '; ' . home_url( '/' ),
+		'body' => array( 'installed_languages' => serialize( $installed_languages ) ),
 		'headers' => array(
 			'wp_install' => $wp_install,
 			'wp_blog' => home_url( '/' )
 		)
 	);
 
-	$response = wp_remote_get($url, $options);
+	$response = wp_remote_post($url, $options);
 
 	if ( is_wp_error( $response ) || 200 != wp_remote_retrieve_response_code( $response ) )
 		return false;
@@ -117,6 +120,8 @@
 	$updates->updates = $offers;
 	$updates->last_checked = time();
 	$updates->version_checked = $wp_version;
+	if ( isset( $body['language_updates'] ) )
+		$updates->language_updates = $body['language_updates'];
 	set_site_transient( 'update_core',  $updates);
 }
 
@@ -154,6 +159,9 @@
 
 	// Check for update on a different schedule, depending on the page.
 	switch ( current_filter() ) {
+		case 'upgrader_process_complete':
+			$timeout = 0;
+			break;
 		case 'load-update-core.php' :
 			$timeout = MINUTE_IN_SECONDS;
 			break;
@@ -194,11 +202,13 @@
 	$current->last_checked = time();
 	set_site_transient( 'update_plugins', $current );
 
+	$installed_languages = get_installed_language_files( 'plugins' );
+
 	$to_send = (object) compact('plugins', 'active');
 
 	$options = array(
 		'timeout' => ( ( defined('DOING_CRON') && DOING_CRON ) ? 30 : 3),
-		'body' => array( 'plugins' => serialize( $to_send ) ),
+		'body' => array( 'plugins' => serialize( $to_send ), 'installed_languages' => serialize( $installed_languages ) ),
 		'user-agent' => 'WordPress/' . $wp_version . '; ' . get_bloginfo( 'url' )
 	);
 
@@ -209,10 +219,15 @@
 
 	$response = maybe_unserialize( wp_remote_retrieve_body( $raw_response ) );
 
-	if ( is_array( $response ) )
+	if ( is_array( $response ) ) {
+		if ( isset( $response['language_updates'] ) ) {
+			$new_option->language_updates = $response['language_updates'];
+			unset( $response['language_updates'] );
+		}
 		$new_option->response = $response;
-	else
+	} else {
 		$new_option->response = array();
+	}
 
 	set_site_transient( 'update_plugins', $new_option );
 }
@@ -263,6 +278,9 @@
 
 	// Check for update on a different schedule, depending on the page.
 	switch ( current_filter() ) {
+		case 'upgrader_process_complete':
+			$timeout = 0;
+			break;
 		case 'load-update-core.php' :
 			$timeout = MINUTE_IN_SECONDS;
 			break;
@@ -301,9 +319,11 @@
 	$last_update->last_checked = time();
 	set_site_transient( 'update_themes', $last_update );
 
+	$installed_languages = get_installed_language_files( 'themes' );
+
 	$options = array(
 		'timeout' => ( ( defined('DOING_CRON') && DOING_CRON ) ? 30 : 3),
-		'body'			=> array( 'themes' => serialize( $themes ) ),
+		'body'			=> array( 'themes' => serialize( $themes ), 'installed_languages' => serialize( $installed_languages ) ),
 		'user-agent'	=> 'WordPress/' . $wp_version . '; ' . get_bloginfo( 'url' )
 	);
 
@@ -317,8 +337,14 @@
 	$new_update->checked = $checked;
 
 	$response = maybe_unserialize( wp_remote_retrieve_body( $raw_response ) );
-	if ( is_array( $response ) )
+
+	if ( is_array( $response ) ) {
+		if ( isset( $response['language_updates'] ) ) {
+			$new_update->language_updates = $response['language_updates'];
+			unset( $response['language_updates'] );
+		}
 		$new_update->response = $response;
+	}
 
 	set_site_transient( 'update_themes', $new_update );
 }
@@ -433,17 +459,20 @@
 
 add_action( 'admin_init', '_maybe_update_core' );
 add_action( 'wp_version_check', 'wp_version_check' );
+add_action( 'upgrader_process_complete', 'wp_version_check' );
 
 add_action( 'load-plugins.php', 'wp_update_plugins' );
 add_action( 'load-update.php', 'wp_update_plugins' );
 add_action( 'load-update-core.php', 'wp_update_plugins' );
 add_action( 'admin_init', '_maybe_update_plugins' );
 add_action( 'wp_update_plugins', 'wp_update_plugins' );
+add_action( 'upgrader_process_complete', 'wp_update_plugins' );
 
 add_action( 'load-themes.php', 'wp_update_themes' );
 add_action( 'load-update.php', 'wp_update_themes' );
 add_action( 'load-update-core.php', 'wp_update_themes' );
 add_action( 'admin_init', '_maybe_update_themes' );
 add_action( 'wp_update_themes', 'wp_update_themes' );
+add_action( 'upgrader_process_complete', 'wp_update_themes' );
 
 add_action('init', 'wp_schedule_update_checks');
