Index: trunk/src/org/openstreetmap/josm/gui/dialogs/properties/PropertiesCellRenderer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/properties/PropertiesCellRenderer.java	(revision 15500)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/properties/PropertiesCellRenderer.java	(revision 15501)
@@ -10,11 +10,8 @@
 import java.awt.Font;
 import java.util.Collection;
-import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 import javax.swing.JLabel;
@@ -28,4 +25,6 @@
 import org.openstreetmap.josm.data.preferences.CachingProperty;
 import org.openstreetmap.josm.data.preferences.NamedColorProperty;
+import org.openstreetmap.josm.tools.I18n;
+import org.openstreetmap.josm.tools.Pair;
 
 /**
@@ -43,7 +42,4 @@
     private static final CachingProperty<Boolean> DISCARDABLE
             = new BooleanProperty("display.discardable-keys", false).cached();
-
-    // Matches ISO-639 two and three letters language codes
-    private static final Pattern LANGUAGE_NAMES = Pattern.compile("name:(\\p{Lower}{2,3})");
 
     static {
@@ -118,12 +114,10 @@
             boolean knownNameKey = false;
             if (column == 0 && str != null) {
-                Matcher m = LANGUAGE_NAMES.matcher(str);
-                if (m.matches()) {
-                    String code = m.group(1);
-                    String label = new Locale(code).getDisplayLanguage();
-                    knownNameKey = !code.equals(label);
+                Pair<String, Boolean> label = I18n.getLocalizedLanguageName(str);
+                if (label != null) {
+                    knownNameKey = label.b;
                     if (knownNameKey) {
                         str = new StringBuilder("<html><body>").append(str)
-                                .append(" <i>&lt;").append(label).append("&gt;</i></body></html>").toString();
+                                .append(" <i>&lt;").append(label.a).append("&gt;</i></body></html>").toString();
                     }
                 }
Index: trunk/src/org/openstreetmap/josm/tools/I18n.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/I18n.java	(revision 15500)
+++ trunk/src/org/openstreetmap/josm/tools/I18n.java	(revision 15501)
@@ -19,4 +19,6 @@
 import java.util.Locale;
 import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
@@ -140,4 +142,14 @@
         languages.put("zh_TW", PluralMode.MODE_NONE);
     }
+
+    private static final String HIRAGANA = "hira";
+    private static final String KATAKANA = "kana";
+    private static final String LATIN = "latn";
+    private static final String PINYIN = "pinyin";
+    private static final String ROMAJI = "rm";
+
+    // Matches ISO-639 two and three letters language codes + scripts
+    private static final Pattern LANGUAGE_NAMES = Pattern.compile(
+            "name:(\\p{Lower}{2,3})(?:[-_](?i:(" + String.join("|", HIRAGANA, KATAKANA, LATIN, PINYIN, ROMAJI) + ")))?");
 
     private static String format(String text, Object... objects) {
@@ -668,3 +680,50 @@
         return originalLocale;
     }
+
+    /**
+     * Returns the localized name of the given script. Only scripts used in the OSM database are known.
+     * @param script Writing system
+     * @return the localized name of the given script, or null
+     * @since 15501
+     */
+    public static String getLocalizedScript(String script) {
+        if (script != null) {
+            switch (script.toLowerCase(Locale.ENGLISH)) {
+                case HIRAGANA:
+                    return tr("Hiragana");
+                case KATAKANA:
+                    return tr("Katakana");
+                case LATIN:
+                    return tr("Latin");
+                case PINYIN:
+                    return tr("Pinyin");
+                case ROMAJI:
+                    return tr("Rōmaji");
+                default:
+                    Logging.warn("Unsupported script: " + script);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns the localized name of the given language and optional script.
+     * @param language Language
+     * @return the pair of localized name + known state of the given language, or null
+     * @since 15501
+     */
+    public static Pair<String, Boolean> getLocalizedLanguageName(String language) {
+        Matcher m = LANGUAGE_NAMES.matcher(language);
+        if (m.matches()) {
+            String code = m.group(1);
+            String label = new Locale(code).getDisplayLanguage();
+            boolean knownNameKey = !code.equals(label);
+            String script = getLocalizedScript(m.group(2));
+            if (script != null) {
+                label += " (" + script + ")";
+            }
+            return new Pair<>(label, knownNameKey);
+        }
+        return null;
+    }
 }
