diff --git a/addressbook/gui/contact-editor/e-contact-editor.c b/addressbook/gui/contact-editor/e-contact-editor.c
index f6e7f0f..e557172 100644
--- a/addressbook/gui/contact-editor/e-contact-editor.c
+++ b/addressbook/gui/contact-editor/e-contact-editor.c
@@ -2245,7 +2260,16 @@ set_address_label (EContact *contact,
 {
 	gchar *address_label = NULL;
 
-	if (address) {
+	if (!address) {
+		e_contact_set (contact, field, NULL);
+		return;
+	}
+
+	address_label = eab_format_address (contact,
+				(field == E_CONTACT_ADDRESS_LABEL_WORK) ? E_CONTACT_ADDRESS_WORK :
+									  E_CONTACT_ADDRESS_HOME);
+
+	if (!address_label) {
 		address_label = append_to_address_label (
 			address_label, address->street, TRUE);
 		address_label = append_to_address_label (
diff --git a/addressbook/gui/widgets/Makefile.am b/addressbook/gui/widgets/Makefile.am
index 511876a..60e6c98 100644
--- a/addressbook/gui/widgets/Makefile.am
+++ b/addressbook/gui/widgets/Makefile.am
@@ -1,5 +1,5 @@
 ruledir = $(privdatadir)
-rule_DATA = addresstypes.xml
+rule_DATA = addresstypes.xml address_formats.dat countrytransl.map
 
 noinst_LTLIBRARIES = libeabwidgets.la
 
diff --git a/addressbook/gui/widgets/eab-contact-display.c b/addressbook/gui/widgets/eab-contact-display.c
index c970afe..bd5c337 100644
--- a/addressbook/gui/widgets/eab-contact-display.c
+++ b/addressbook/gui/widgets/eab-contact-display.c
@@ -480,7 +480,9 @@ render_contact_block (GString *buffer, EContact *contact)
 		g_free (mail);
 	}
 	g_list_foreach (email_list, (GFunc) g_free, NULL);
+	g_list_foreach (email_attr_list, (GFunc) e_vcard_attribute_free, NULL);
 	g_list_free (email_list);
+	g_list_free (email_attr_list);
 
 	if (accum->len) {
 
diff --git a/addressbook/gui/widgets/eab-gui-util.c b/addressbook/gui/widgets/eab-gui-util.c
index 280758f..9cc435e 100644
--- a/addressbook/gui/widgets/eab-gui-util.c
+++ b/addressbook/gui/widgets/eab-gui-util.c
@@ -15,6 +15,7 @@
  *
  * Authors:
  *		Chris Toshok <toshok@ximian.com>
+ *		Dan Vratil <dvratil@redhat.com>
  *
  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  *
@@ -26,6 +27,8 @@
 #include <fcntl.h>
 #include <errno.h>
 #include <string.h>
+#include <locale.h>
+#include <string.h>
 
 #include <gtk/gtk.h>
 #include <glib/gi18n.h>
@@ -46,6 +49,39 @@
 /* we link to camel for decoding quoted printable email addresses */
 #include <camel/camel.h>
 
+/* Template tags for address format localization */
+#define ADDRESS_REALNAME   			"%n"
+#define ADDRESS_REALNAME_UPPER			"%N"
+#define ADDRESS_COMPANY				"%m"
+#define ADDRESS_COMPANY_UPPER			"%M"
+#define ADDRESS_POBOX				"%p"
+#define ADDRESS_STREET				"%s"
+#define ADDRESS_STREET_UPPER			"%S"
+#define ADDRESS_ZIPCODE				"%z"
+#define ADDRESS_LOCATION			"%l"
+#define ADDRESS_LOCATION_UPPER			"%L"
+#define ADDRESS_REGION				"%r"
+#define ADDRESS_REGION_UPPER			"%R"
+#define ADDRESS_CONDCOMMA			"%,"	/* Conditional comma is removed when a surrounding tag is evaluated to zero */
+#define ADDRESS_CONDWHITE			"%w"	/* Conditional whitespace is removed when a surrounding tag is evaluated to zero */
+#define ADDRESS_COND_PURGEEMPTY			"%0"	/* Purge empty has following syntax: %0(...) and is removed when no tag within () is evaluated non-zero */
+
+/* Fallback formats */
+#define ADDRESS_DEFAULT_FORMAT 			"%0(%n\n)%0(%m\n)%0(%s\n)%0(PO BOX %p\n)%0(%l%w%r)%,%z"
+#define ADDRESS_DEFAULT_COUNTRY_POSITION	"below"
+
+enum {
+	LOCALES_LANGUAGE = 0,
+	LOCALES_COUNTRY = 1
+};
+
+typedef enum {
+	ADDRESS_FORMAT_HOME = 0,
+	ADDRESS_FORMAT_BUSINESS = 1
+} AddressFormat;
+
+
+
 void
 eab_error_dialog (EAlertSink *alert_sink, const gchar *msg, const GError *error)
 {
@@ -545,3 +581,485 @@ eab_parse_qp_email_to_html (const gchar *string)
 
 	return value;
 }
+
+
+/*
+ * eab_format_address helper function
+ *
+ * Splits locales from en_US to array "en","us",NULL. When
+ * locales don't have the second part (for example "C"),
+ * the output array is "c",NULL
+ */
+static gchar**
+get_locales (void)
+{
+	gchar *locale, *l_locale;
+	gchar *dot;
+	gchar **split;
+
+	locale = g_strdup (setlocale (LC_ADDRESS, NULL));
+	if (!locale)
+		return NULL;
+
+	l_locale = g_utf8_strdown (locale, -1);
+	g_free (locale);
+
+	dot  = strstr (l_locale, ".");
+	if (dot != NULL) {
+		gchar *p = l_locale;
+		l_locale = g_strndup (l_locale, dot-l_locale);
+		g_free (p);
+	}
+
+	split = g_strsplit (l_locale, "_", 2);
+
+	g_free (l_locale);
+	return split;
+
+}
+
+static gchar*
+get_locales_str (void)
+{
+	gchar *ret;
+	gchar **loc = get_locales ();
+
+	if (!loc)
+		return g_strdup ("C");
+
+	if (!loc[0] ||
+	    (loc[0] && !loc[1])) /* We don't care about language now, we need a country at first! */
+		ret = g_strdup ("C");
+	else if (loc[0] && loc[1]) {
+		if (*loc[0])
+			ret =  g_strconcat (loc[LOCALES_COUNTRY], "_", loc[LOCALES_LANGUAGE], NULL);
+		else
+			ret = g_strdup (loc[LOCALES_COUNTRY]);
+	}
+
+	g_strfreev (loc);
+	return ret;
+}
+
+
+/*
+ * Reads countrytransl.map file, which contains map of localized
+ * country names and their ISO codes and tries to find matching record
+ * for given country. The search is case insensitive.
+ * When no record is found (country is probably in untranslated language), returns
+ * code of local computer country (from locales)
+ */
+static gchar*
+country_to_ISO (const gchar *country)
+{
+	FILE *file = fopen (EVOLUTION_RULEDIR "/countrytransl.map", "r");
+	gchar buffer[100];
+	gint length = 100;
+	gchar **pair;
+	gchar *res;
+	gchar *l_country = g_utf8_strdown (country, -1);
+
+	if (!file) {
+		gchar **loc;
+		g_warning ("%s: Failed to open countrytransl.map. Check your installation.", G_STRFUNC);
+		loc = get_locales ();
+		res = g_strdup (loc[LOCALES_COUNTRY]);
+		g_free (l_country);
+		g_strfreev (loc);
+		return res;
+	}
+
+	while (fgets (buffer, length, file) != NULL) {
+		gchar *low;
+		pair = g_strsplit (buffer, "\t", 2);
+
+		if (pair[0]) {
+			low = g_utf8_strdown (pair[0], -1);
+			if (g_utf8_collate (low, l_country) == 0) {
+				gchar *ret = g_strdup (pair[1]);
+				gchar *pos;
+				/* Remove trailing newline character */
+				if ((pos = g_strrstr (ret, "\n")) != NULL)
+					pos[0] = '\0';
+				fclose (file);
+				g_strfreev (pair);
+				g_free (low);
+				g_free (l_country);
+				return ret;
+			}
+		}
+
+		g_strfreev (pair);
+		g_free (low);
+	}
+
+	/* If we get here, then no match was found in the map file and we
+	   fallback to local system locales */
+	fclose (file);
+
+	pair = get_locales ();
+	res = g_strdup (pair[LOCALES_COUNTRY]);
+	g_strfreev (pair);
+	g_free (l_country);
+	return res;
+}
+
+
+/*
+ * Tries to find given key in "country_LANGUAGE" group. When fails to find
+ * such group, then fallbacks to "country" group. When such group does not
+ * exist either, NULL is returned
+ */
+static gchar*
+get_key_file_locale_string (GKeyFile *key_file,
+			    const gchar *key,
+			    const gchar *locale)
+{
+	gchar *result;
+	gchar *group;
+
+	g_return_val_if_fail (locale, NULL);
+
+	/* Default locale is in "country_lang", but such group may not exist. In such case use group "country" */
+	if (g_key_file_has_group (key_file, locale))
+		group = g_strdup (locale);
+	else {
+		gchar **locales = g_strsplit (locale, "_", 0);
+		group = g_strdup (locales[LOCALES_COUNTRY]);
+		g_strfreev (locales);
+	}
+
+	/* When group or key does not exist, returns NULL and fallback string will be used */
+	result = g_key_file_get_string (key_file, group, key, NULL);
+	g_free (group);
+	return result;
+}
+
+static void
+get_address_format (AddressFormat address_format,
+		    const gchar *locale,
+		    gchar **format,
+		    gchar **country_position)
+{
+	GKeyFile *key_file;
+	GError *error;
+	gchar *loc;
+	const gchar *addr_key, *country_key;
+
+	if (address_format == ADDRESS_FORMAT_HOME) {
+		addr_key = "AddressFormat";
+		country_key = "CountryPosition";
+	} else if (address_format == ADDRESS_FORMAT_BUSINESS) {
+		addr_key = "BusinessAddressFormat";
+		country_key = "BusinessCountryPosition";
+	} else {
+		return;
+	}
+
+	if (locale == NULL)
+		loc = get_locales_str ();
+	else
+		loc = g_strdup (locale);
+
+	error = NULL;
+	key_file = g_key_file_new ();
+	g_key_file_load_from_file (key_file, EVOLUTION_RULEDIR "/address_formats.dat", 0, &error);
+	if (error) {
+		g_warning ("%s: Failed to load address_formats.dat file: %s", G_STRFUNC, error->message);
+		*format = g_strdup (ADDRESS_DEFAULT_FORMAT);
+		*country_position = g_strdup (ADDRESS_DEFAULT_COUNTRY_POSITION);
+		g_key_file_free (key_file);
+		g_free (loc);
+		g_error_free (error);
+		return;
+	}
+
+	if (format) {
+		if (*format)
+			g_free (*format);
+		*format = get_key_file_locale_string (key_file, addr_key, loc);
+		if (!*format && address_format == ADDRESS_FORMAT_HOME) {
+			*format = g_strdup (ADDRESS_DEFAULT_FORMAT);
+		} else if (!*format && address_format == ADDRESS_FORMAT_BUSINESS)
+			get_address_format (ADDRESS_FORMAT_HOME, loc, format, NULL);
+	}
+
+	if (country_position) {
+		if (*country_position)
+			g_free (*country_position);
+		*country_position = get_key_file_locale_string (key_file, country_key, loc);
+		if (!*country_position && address_format == ADDRESS_FORMAT_HOME)
+			*country_position = g_strdup (ADDRESS_DEFAULT_COUNTRY_POSITION);
+		else if (!*country_position && address_format == ADDRESS_FORMAT_BUSINESS)
+			get_address_format (ADDRESS_FORMAT_HOME, loc, NULL, country_position);
+	}
+
+	g_free (loc);
+	g_key_file_free (key_file);
+}
+
+static const gchar*
+find_balanced_bracket (const gchar *str)
+{
+	gint balance_counter = 0;
+	gint i = 0;
+
+	do {
+		if (str[i] == '(')
+			balance_counter++;
+
+		if (str[i] == ')')
+			balance_counter--;
+
+		i++;
+
+	} while ((balance_counter > 0) && (str[i]));
+
+	if (balance_counter > 0)
+		return str;
+
+	return str+i;
+}
+
+static GString*
+string_append_upper (GString *str, const gchar *c)
+{
+	gchar *up_c;
+
+	g_return_val_if_fail (str, NULL);
+
+	if (!c || !*c)
+		return str;
+
+	up_c = g_utf8_strup (c, -1);
+	str = g_string_append (str, up_c);
+	g_free (up_c);
+
+	return str;
+}
+
+static gboolean
+parse_address_template_section (const gchar *format,
+				const gchar *realname,
+				const gchar *org_name,
+				EContactAddress *address,
+				gchar **result)
+
+{
+	const gchar *pos, *old_pos;
+	gboolean ret = FALSE; /* Indicates, wheter at least something was replaced */
+
+	GString *res = g_string_new ("");
+
+	pos = format;
+	old_pos = pos;
+	while ((pos = strstr (pos, "%")) != NULL) {
+
+		if (old_pos != pos)
+			g_string_append_len (res, old_pos, pos - old_pos);
+
+		switch (pos[1]) {
+			case 'n':
+				g_string_append (res, realname);
+				ret = TRUE;
+				pos += 2; /* Jump behind the modifier, see what's next */
+				break;
+			case 'N':
+				if (realname && *realname) {
+					string_append_upper (res, realname);
+					ret = TRUE;
+				}
+				pos += 2;
+				break;
+			case 'm':
+				if (org_name && *org_name) {
+					g_string_append (res, org_name);
+					ret = TRUE;
+				}
+				pos += 2;
+				break;
+			case 'M':
+				if (org_name && *org_name) {
+					string_append_upper (res, org_name);
+					ret = TRUE;
+				}
+				pos += 2;
+				break;
+			case 'p':
+				if (address->po && *(address->po)) {
+					g_string_append (res, address->po);
+					ret = TRUE;
+				}
+				pos += 2;
+				break;
+			case 's':
+				if (address->street && *(address->street)) {
+					g_string_append (res, address->street);
+					ret = TRUE;
+				}
+				pos += 2;
+				break;
+			case 'S':
+				if (address->street && *(address->street)) {
+					string_append_upper (res, address->street);
+					ret = TRUE;
+				}
+				pos += 2;
+				break;
+			case 'z':
+				if (address->code && *(address->code)) {
+					g_string_append (res, address->code);
+					ret = TRUE;
+				}
+				pos += 2;
+				break;
+			case 'l':
+				if (address->locality && *(address->locality)) {
+					g_string_append (res, address->locality);
+					ret = TRUE;
+				}
+				pos += 2;
+				break;
+			case 'L':
+				if (address->locality && *(address->locality)) {
+					string_append_upper (res, address->locality);
+					ret = TRUE;
+				}
+				pos += 2;
+				break;
+			case 'r':
+				if (address->region && *(address->region)) {
+					g_string_append (res, address->region);
+					ret = TRUE;
+				}
+				pos += 2;
+				break;
+			case 'R':
+				if (address->region && *(address->region)) {
+					string_append_upper (res, address->region);
+					ret = TRUE;
+				}
+				pos += 2;
+				break;
+			case ',':
+				if (ret && (pos >= format + 2) &&		/* If there's something before %, */
+				    (g_ascii_strcasecmp (pos - 2, "\n") != 0) &&  /* And if it is not a newline */
+				    (g_ascii_strcasecmp (pos - 2, "%w") != 0))    /* Nor whitespace */
+				    	g_string_append (res, ", ");
+				pos += 2;
+				break;
+			case 'w':
+				if (ret && (pos >= format + 2) &&
+				    (g_ascii_strcasecmp (pos - 2, "\n") != 0) &&
+				    (g_ascii_strcasecmp (pos - 1, " ") != 0))
+				    	g_string_append (res, " ");
+				pos += 2;
+				break;
+			case '0': {
+				const gchar *bpos1, *bpos2;
+				gchar *inner;
+				gchar *ires;
+				gboolean replaced;
+
+				bpos1 = pos + 2;
+				bpos2 = find_balanced_bracket (bpos1);
+
+				inner = g_strndup (bpos1 + 1, bpos2 - bpos1 - 2); /* Get inner content of the %0(...) */
+				replaced = parse_address_template_section (inner, realname, org_name, address, &ires);
+				//g_strstrip (ires); /* Remove opening and trailing whitespaces created by expanding %, */
+				if (replaced)
+					g_string_append (res, ires);
+
+				g_free (ires);
+				g_free (inner);
+
+				ret = replaced;
+				pos += (bpos2 - bpos1 + 2);
+			} break;
+		}
+
+		old_pos = pos;
+	}
+	g_string_append (res, old_pos);
+
+	*result = g_strdup (res->str);
+
+	g_string_free (res, TRUE);
+
+	return ret;
+}
+
+gchar *
+eab_format_address (EContact *contact,
+		    EContactField address_type)
+{
+	gchar *result;
+	gchar *format = NULL;
+	gchar *country_position = NULL;
+	gchar *locale;
+	EContactAddress *addr = e_contact_get (contact, address_type);
+
+        if (!addr)
+        	return NULL;
+
+        if (!addr->po && !addr->ext && !addr->street && !addr->locality && !addr->region &&
+            !addr->code  && !addr->country) {
+            e_contact_address_free (addr);
+            return NULL;
+        }
+
+	if (addr->country) {
+		gchar *cntry = country_to_ISO (addr->country);
+		gchar **loc = get_locales ();
+		locale = g_strconcat (loc[LOCALES_LANGUAGE], "_", cntry, NULL);
+		g_strfreev (loc);
+		g_free (cntry);
+	} else
+		locale = get_locales_str ();
+
+        if (address_type == E_CONTACT_ADDRESS_HOME)
+		get_address_format (ADDRESS_FORMAT_HOME, locale, &format, &country_position);
+	else if (address_type == E_CONTACT_ADDRESS_WORK)
+		get_address_format (ADDRESS_FORMAT_BUSINESS, locale, &format, &country_position);
+	else {
+		e_contact_address_free (addr);
+		g_free (locale);
+		return NULL;
+	}
+
+	/* Expand all the variables in format.
+	   Don't display organization in home address */
+	parse_address_template_section (format,
+					e_contact_get_const (contact, E_CONTACT_FULL_NAME),
+					(address_type == E_CONTACT_ADDRESS_WORK) ? e_contact_get_const (contact, E_CONTACT_ORG): NULL,
+					addr,
+					&result);
+
+	/* Add the country line. In some countries, the address can be located above the
+	   rest of the address */
+	if (addr->country && country_position) {
+		gchar *country_upper = g_utf8_strup (addr->country, -1);
+		gchar *p = result;
+		if (g_strcmp0 (country_position, "BELOW") == 0) {
+			result = g_strconcat (p, "\n\n", country_upper, NULL);
+			g_free (p);
+		} else if (g_strcmp0 (country_position, "below") == 0) {
+			result = g_strconcat (p, "\n\n", addr->country, NULL);
+			g_free (p);
+		} else if (g_strcmp0 (country_position, "ABOVE") == 0) {
+			result = g_strconcat (country_upper, "\n\n", p, NULL);
+			g_free (p);
+		} else if (g_strcmp0 (country_position, "above") == 0) {
+			result = g_strconcat (addr->country, "\n\n", p, NULL);
+			g_free (p);
+		}
+		g_free (country_upper);
+	}
+
+	e_contact_address_free (addr);
+	g_free (locale);
+	g_free (format);
+	g_free (country_position);
+
+	return result;
+}
diff --git a/addressbook/gui/widgets/eab-gui-util.h b/addressbook/gui/widgets/eab-gui-util.h
index 45c7d40..a118ecf 100644
--- a/addressbook/gui/widgets/eab-gui-util.h
+++ b/addressbook/gui/widgets/eab-gui-util.h
@@ -58,6 +58,10 @@ gboolean	eab_parse_qp_email		(const gchar *string,
 						 gchar **email);
 gchar *		eab_parse_qp_email_to_html	(const gchar *string);
 
+
+gchar *		eab_format_address		(EContact *contact,
+						 EContactField address_type);
+
 G_END_DECLS
 
 #endif /* __E_ADDRESSBOOK_UTIL_H__ */

