diff --git a/rsvg-cond.c b/rsvg-cond.c
index 94ada6a..a7bb278 100644
--- a/rsvg-cond.c
+++ b/rsvg-cond.c
@@ -92,23 +92,36 @@ rsvg_cond_fulfills_requirement (const char *value, const char **features, guint
     return permitted;
 }
 
-/* http://www.w3.org/TR/SVG/struct.html#SystemLanguageAttribute */
+/* 
+   http://www.w3.org/TR/SVG/struct.html#SystemLanguageAttribute
+   This subroutine matches IETF langtags; it does not match locale strings.
+   Langtags look like "en-US"; locale strings often look like "en_US".
+   *a is the user agent's langtag preference (Accept-Language set would be better)
+   *b is a langtag found in the systemLanguage attribute
+   The SVG specification requires that all of *a appears in *b.
+   So "zh-Hans" will match "zh-Hans" or "zh-Hans-TW" but not "zh-Hant".
+   (Previous code would match "zh-Hans" to "zh-Hant" and even "en-US" to "enx"!)
+*/
 static gboolean
-rsvg_locale_compare (const char *a, const char *b)
+rsvg_langtag_compare (const char *a, const char *b)
 {
     const char *hyphen;
-
-    /* check for an exact-ish match first */
-    if (!g_ascii_strncasecmp (a, b, strlen (b)))
-        return TRUE;
-
-    /* check to see if there's a hyphen */
-    hyphen = strstr (b, "-");
-    if (!hyphen)
-        return FALSE;
-
-    /* compare up to the hyphen */
-    return !g_ascii_strncasecmp (a, b, (hyphen - b));
+	gsize len_a = strlen(a);
+	gsize len_b = strlen(b);
+
+	/* test for langtag exact match or langtag superset */
+	if (len_a <= len_b &&
+		/* all of a must appear in b */
+		!g_ascii_strncasecmp (a, b, len_a) &&
+		/* and b must be the same length as b or b is at a hyphen (subtag break) */
+		(len_a == len_b || b[len_a] == '-')
+		) {
+		return TRUE;
+	}
+	else {
+		/* the langtags did not match */
+		return FALSE;
+	}
 }
 
 /* http://www.w3.org/TR/SVG/struct.html#SystemLanguageAttribute */
@@ -119,6 +132,7 @@ rsvg_cond_parse_system_language (const char *value)
     char **elems;
     gboolean permitted = TRUE;
 
+	/* systemLanguage is a space separated list of IETF langtags */
     elems = rsvg_css_parse_list (value, &nb_elems);
 
     if (elems && nb_elems) {
@@ -144,14 +158,24 @@ rsvg_cond_parse_system_language (const char *value)
         if (!locale)
             locale = g_strdup (setlocale (LC_ALL, NULL));
 
-        if (!locale || strcmp (locale, "C") == 0) {
+		/* default unknown locales to en */
+        if (!locale || strcmp (locale, "C") == 0 || strcmp (locale, "POSIX")) {
             g_free (locale);
             locale = g_strdup ("en");
         }
 
         if (locale) {
+			/* the locale should be edited to a langtag */
+			/* Locale strings are opaque! */
+			/* they often look like language_territory.codeset@modifier */
+			/* "en_US" should become "en-US" (underscores to hyphens) */
+			/* "en_US.UTF-8" should become "en_US" (period to NUL) */
+			/* "en@modifier" should become "en" (@ to NUL) */
+			/* "x:y:z" should become "x" (colons to NUL) */
+
+			/* Now test systemLanguage langtags */
             for (i = 0; (i < nb_elems) && !permitted; i++) {
-                if (rsvg_locale_compare (locale, elems[i]))
+                if (rsvg_langtag_compare (locale, elems[i]))
                     permitted = TRUE;
             }
 
