Index: /trunk/src/org/openstreetmap/josm/actions/upload/ApiPreconditionCheckerHook.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/actions/upload/ApiPreconditionCheckerHook.java	(revision 19436)
+++ /trunk/src/org/openstreetmap/josm/actions/upload/ApiPreconditionCheckerHook.java	(revision 19437)
@@ -23,4 +23,5 @@
 import org.openstreetmap.josm.io.OsmTransferCanceledException;
 import org.openstreetmap.josm.tools.Logging;
+import org.openstreetmap.josm.tools.Utils;
 
 /**
@@ -64,5 +65,5 @@
                 String key = entry.getKey();
                 String value = entry.getValue();
-                if (key.length() > Tagged.MAX_TAG_LENGTH) {
+                if (!Utils.checkCodePointCount(value, Tagged.MAX_TAG_LENGTH)) {
                     if (osmPrimitive.isDeleted()) {
                         // if OsmPrimitive is going to be deleted we automatically shorten the value
@@ -73,10 +74,10 @@
                                 )
                         );
-                        osmPrimitive.put(key, value.substring(0, Tagged.MAX_TAG_LENGTH));
+                        osmPrimitive.put(key, Utils.shortenString(value, Tagged.MAX_TAG_LENGTH));
                         continue;
                     }
                     JOptionPane.showMessageDialog(MainApplication.getMainFrame(),
                             tr("Length of value for tag ''{0}'' on object {1} exceeds the max. allowed length {2}. Values length is {3}.",
-                                    key, Long.toString(osmPrimitive.getId()), Tagged.MAX_TAG_LENGTH, value.length()
+                                    key, Long.toString(osmPrimitive.getId()), Tagged.MAX_TAG_LENGTH, Utils.getCodePointCount(value)
                             ),
                             tr("Precondition violation"),
Index: /trunk/src/org/openstreetmap/josm/data/osm/Changeset.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/osm/Changeset.java	(revision 19436)
+++ /trunk/src/org/openstreetmap/josm/data/osm/Changeset.java	(revision 19437)
@@ -17,4 +17,5 @@
 import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.tools.CheckParameterUtil;
+import org.openstreetmap.josm.tools.Utils;
 
 /**
@@ -313,5 +314,5 @@
         CheckParameterUtil.ensureParameterNotNull(keys, "keys");
         keys.values().stream()
-                .filter(value -> value != null && value.length() > MAX_CHANGESET_TAG_LENGTH)
+                .filter(value -> !Utils.checkCodePointCount(value, MAX_CHANGESET_TAG_LENGTH))
                 .findFirst()
                 .ifPresent(value -> {
@@ -340,7 +341,8 @@
     public void put(String key, String value) {
         CheckParameterUtil.ensureParameterNotNull(key, "key");
-        if (value != null && value.length() > MAX_CHANGESET_TAG_LENGTH) {
-            throw new IllegalArgumentException("Changeset tag value is too long: "+value);
+        if (!Utils.checkCodePointCount(value, MAX_CHANGESET_TAG_LENGTH)) {
+            throw new IllegalArgumentException("Changeset tag value is too long: " + value);
         }
+
         this.tags.put(key, value);
     }
Index: /trunk/src/org/openstreetmap/josm/data/validation/tests/TagChecker.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/validation/tests/TagChecker.java	(revision 19436)
+++ /trunk/src/org/openstreetmap/josm/data/validation/tests/TagChecker.java	(revision 19437)
@@ -1016,7 +1016,8 @@
             withErrors.put(p, "UUCV");
         }
-        if ((value.length() > Tagged.MAX_TAG_LENGTH) && !withErrors.contains(p, "LV")) {
+        final int codePoints = Utils.getCodePointCount(value);
+        if (codePoints > Tagged.MAX_TAG_LENGTH && !withErrors.contains(p, "LV")) {
             errors.add(TestError.builder(this, Severity.ERROR, LONG_VALUE)
-                    .message(tr("Tag value longer than {0} characters ({1} characters)", Tagged.MAX_TAG_LENGTH, value.length()), s, key)
+                    .message(tr("Tag value longer than {0} characters ({1} characters)", Tagged.MAX_TAG_LENGTH, codePoints), s, key)
                     .primitives(p)
                     .build());
@@ -1065,7 +1066,8 @@
             withErrors.put(p, "ICK");
         }
-        if (key.length() > Tagged.MAX_TAG_LENGTH && !withErrors.contains(p, "LK")) {
+        final int codePoints = Utils.getCodePointCount(key);
+        if (codePoints > Tagged.MAX_TAG_LENGTH && !withErrors.contains(p, "LK")) {
             errors.add(TestError.builder(this, Severity.ERROR, LONG_KEY)
-                    .message(tr("Tag key longer than {0} characters ({1} characters)", Tagged.MAX_TAG_LENGTH, key.length()), s, key)
+                    .message(tr("Tag key longer than {0} characters ({1} characters)", Tagged.MAX_TAG_LENGTH, codePoints), s, key)
                     .primitives(p)
                     .build());
Index: /trunk/src/org/openstreetmap/josm/tools/TextTagParser.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/tools/TextTagParser.java	(revision 19436)
+++ /trunk/src/org/openstreetmap/josm/tools/TextTagParser.java	(revision 19437)
@@ -155,6 +155,6 @@
                 if (r == 2 || r == 3) return false; if (r == 4) return true;
             }
-            if (value.length() > MAX_VALUE_LENGTH) {
-                r = callback.warning(tr("Value is too long (max {0} characters):", MAX_VALUE_LENGTH), value, "tags.paste.valuetoolong");
+            if (!Utils.checkCodePointCount(value, MAX_VALUE_LENGTH)) {
+                r = callback.warning(tr("Value is too long (max {0} characters):", MAX_VALUE_LENGTH), value, "");
                 if (r == 2 || r == 3) return false; if (r == 4) return true;
             }
Index: /trunk/src/org/openstreetmap/josm/tools/Utils.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/tools/Utils.java	(revision 19436)
+++ /trunk/src/org/openstreetmap/josm/tools/Utils.java	(revision 19437)
@@ -2026,3 +2026,27 @@
         }
     }
+
+    /**
+     * Calculate the number of unicode code points. See #24446
+     * @param s the string
+     * @return 0 if s is null or empty, else the number of code points
+     * @since 19437
+     */
+    public static int getCodePointCount(String s) {
+        if (s == null)
+            return 0;
+        return s.codePointCount(0, s.length());
+    }
+
+    /**
+     * Check if a given string has more than the allowed number of code points.
+     * See #24446. The OSM server checks this number, not the value returned by String.length()
+     * @param s the string
+     * @param maxLen the maximum number of code points
+     * @return true if s is null or within the given limit, false else
+     * @since 19437
+     */
+    public static boolean checkCodePointCount(String s, int maxLen) {
+        return getCodePointCount(s) <= maxLen;
+    }
 }
