ICU-20666 Adding insert/appendChar16 to FormattedStringBuilder.
diff --git a/icu4c/source/i18n/formatted_string_builder.cpp b/icu4c/source/i18n/formatted_string_builder.cpp
index 4ffc4c7..3024bff 100644
--- a/icu4c/source/i18n/formatted_string_builder.cpp
+++ b/icu4c/source/i18n/formatted_string_builder.cpp
@@ -144,10 +144,6 @@
     return *this;
 }
 
-int32_t FormattedStringBuilder::appendCodePoint(UChar32 codePoint, Field field, UErrorCode &status) {
-    return insertCodePoint(fLength, codePoint, field, status);
-}
-
 int32_t
 FormattedStringBuilder::insertCodePoint(int32_t index, UChar32 codePoint, Field field, UErrorCode &status) {
     int32_t count = U16_LENGTH(codePoint);
@@ -166,10 +162,6 @@
     return count;
 }
 
-int32_t FormattedStringBuilder::append(const UnicodeString &unistr, Field field, UErrorCode &status) {
-    return insert(fLength, unistr, field, status);
-}
-
 int32_t FormattedStringBuilder::insert(int32_t index, const UnicodeString &unistr, Field field,
                                     UErrorCode &status) {
     if (unistr.length() == 0) {
diff --git a/icu4c/source/i18n/formatted_string_builder.h b/icu4c/source/i18n/formatted_string_builder.h
index cc13b66..2949ae7 100644
--- a/icu4c/source/i18n/formatted_string_builder.h
+++ b/icu4c/source/i18n/formatted_string_builder.h
@@ -85,29 +85,73 @@
 
     FormattedStringBuilder &clear();
 
-    int32_t appendCodePoint(UChar32 codePoint, Field field, UErrorCode &status);
+    /** Appends a UTF-16 code unit. */
+    inline int32_t appendChar16(char16_t codeUnit, Field field, UErrorCode& status) {
+        // appendCodePoint handles both code units and code points.
+        return insertCodePoint(fLength, codeUnit, field, status);
+    }
 
+    /** Inserts a UTF-16 code unit. Note: insert at index 0 is very efficient. */
+    inline int32_t insertChar16(int32_t index, char16_t codeUnit, Field field, UErrorCode& status) {
+        // insertCodePoint handles both code units and code points.
+        return insertCodePoint(index, codeUnit, field, status);
+    }
+
+    /** Appends a Unicode code point. */
+    inline int32_t appendCodePoint(UChar32 codePoint, Field field, UErrorCode &status) {
+        return insertCodePoint(fLength, codePoint, field, status);
+    }
+
+    /** Inserts a Unicode code point. Note: insert at index 0 is very efficient. */
     int32_t insertCodePoint(int32_t index, UChar32 codePoint, Field field, UErrorCode &status);
 
-    int32_t append(const UnicodeString &unistr, Field field, UErrorCode &status);
+    /** Appends a string. */
+    inline int32_t append(const UnicodeString &unistr, Field field, UErrorCode &status) {
+        return insert(fLength, unistr, field, status);
+    }
 
+    /** Inserts a string. Note: insert at index 0 is very efficient. */
     int32_t insert(int32_t index, const UnicodeString &unistr, Field field, UErrorCode &status);
 
+    /** Inserts a substring. Note: insert at index 0 is very efficient.
+     *
+     * @param start Start index of the substring of unistr to be inserted.
+     * @param end End index of the substring of unistr to be inserted (exclusive).
+     */
     int32_t insert(int32_t index, const UnicodeString &unistr, int32_t start, int32_t end, Field field,
                    UErrorCode &status);
 
+    /** Deletes a substring and then inserts a string at that same position.
+     * Similar to JavaScript Array.prototype.splice().
+     *
+     * @param startThis Start of the span to delete.
+     * @param endThis End of the span to delete (exclusive).
+     * @param unistr The string to insert at the deletion position.
+     * @param startOther Start index of the substring of unistr to be inserted.
+     * @param endOther End index of the substring of unistr to be inserted (exclusive).
+     */
     int32_t splice(int32_t startThis, int32_t endThis,  const UnicodeString &unistr,
                    int32_t startOther, int32_t endOther, Field field, UErrorCode& status);
 
+    /** Appends a formatted string. */
     int32_t append(const FormattedStringBuilder &other, UErrorCode &status);
 
+    /** Inserts a formatted string. Note: insert at index 0 is very efficient. */
     int32_t insert(int32_t index, const FormattedStringBuilder &other, UErrorCode &status);
 
+    /**
+     * Ensures that the string buffer contains a NUL terminator. The NUL terminator does
+     * not count toward the string length. Any further changes to the string (insert or
+     * append) may invalidate the NUL terminator.
+     *
+     * You should call this method after the formatted string is completely built if you
+     * plan to return a pointer to the string from a C API.
+     */
     void writeTerminator(UErrorCode& status);
 
     /**
      * Gets a "safe" UnicodeString that can be used even after the FormattedStringBuilder is destructed.
-     * */
+     */
     UnicodeString toUnicodeString() const;
 
     /**
diff --git a/icu4c/source/i18n/measfmt.cpp b/icu4c/source/i18n/measfmt.cpp
index 3c4d5d6..8f306c0 100644
--- a/icu4c/source/i18n/measfmt.cpp
+++ b/icu4c/source/i18n/measfmt.cpp
@@ -777,7 +777,7 @@
             case u'm':
             case u's':
                 if (protect) {
-                    fsb.appendCodePoint(c, undefinedField, status);
+                    fsb.appendChar16(c, undefinedField, status);
                 } else {
                     UnicodeString tmp;
                     if ((i + 1 < patternLength) && pattern[i + 1] == c) { // doubled
@@ -793,14 +793,14 @@
             case u'\'':
                 // '' is escaped apostrophe
                 if ((i + 1 < patternLength) && pattern[i + 1] == c) {
-                    fsb.appendCodePoint(c, undefinedField, status);
+                    fsb.appendChar16(c, undefinedField, status);
                     i++;
                 } else {
                     protect = !protect;
                 }
                 break;
             default:
-                fsb.appendCodePoint(c, undefinedField, status);
+                fsb.appendChar16(c, undefinedField, status);
         }
     }
 
diff --git a/icu4c/source/test/intltest/formatted_string_builder_test.cpp b/icu4c/source/test/intltest/formatted_string_builder_test.cpp
index b0c4552..4ce63da 100644
--- a/icu4c/source/test/intltest/formatted_string_builder_test.cpp
+++ b/icu4c/source/test/intltest/formatted_string_builder_test.cpp
@@ -175,6 +175,18 @@
         sb5.insertCodePoint(2, cas, UNUM_FIELD_COUNT, status);
         assertSuccess("Inserting into sb5", status);
         assertEqualsImpl(sb4, sb5);
+
+        UnicodeString sb6;
+        FormattedStringBuilder sb7;
+        sb6.append(cas);
+        if (U_IS_SUPPLEMENTARY(cas)) {
+            sb7.appendChar16(U16_TRAIL(cas), UNUM_FIELD_COUNT, status);
+            sb7.insertChar16(0, U16_LEAD(cas), UNUM_FIELD_COUNT, status);
+        } else {
+            sb7.insertChar16(0, cas, UNUM_FIELD_COUNT, status);
+        }
+        assertSuccess("Insert/append into sb7", status);
+        assertEqualsImpl(sb6, sb7);
     }
 }
 
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/FormattedStringBuilder.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/FormattedStringBuilder.java
index ca3cb18..0eb54f5 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/impl/FormattedStringBuilder.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/FormattedStringBuilder.java
@@ -106,6 +106,18 @@
         return this;
     }
 
+    public int appendChar16(char codeUnit, Field field) {
+        return insertChar16(length, codeUnit, field);
+    }
+
+    public int insertChar16(int index, char codeUnit, Field field) {
+        int count = 1;
+        int position = prepareForInsert(index, count);
+        chars[position] = codeUnit;
+        fields[position] = field;
+        return count;
+    }
+
     /**
      * Appends the specified codePoint to the end of the string.
      *
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/MeasureFormat.java b/icu4j/main/classes/core/src/com/ibm/icu/text/MeasureFormat.java
index d3f2306..4925c63 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/text/MeasureFormat.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/text/MeasureFormat.java
@@ -904,7 +904,7 @@
                 case 'm':
                 case 's':
                     if (protect) {
-                        fsb.appendCodePoint(c, null);
+                        fsb.appendChar16(c, null);
                     } else {
                         if ((i + 1 < pattern.length()) && pattern.charAt(i + 1) == c) { // doubled
                             fsb.append(numberFormatter2.format(value), null); // TODO: Use proper Field
@@ -917,14 +917,14 @@
                 case '\'':
                     // '' is escaped apostrophe
                     if ((i + 1 < pattern.length()) && pattern.charAt(i + 1) == c) {
-                        fsb.appendCodePoint(c, null);
+                        fsb.appendChar16(c, null);
                         i++;
                     } else {
                         protect = !protect;
                     }
                     break;
                 default:
-                    fsb.appendCodePoint(c, null);
+                    fsb.appendChar16(c, null);
             }
         }
 
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/FormattedStringBuilderTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/FormattedStringBuilderTest.java
index caf2162..b297b1f 100644
--- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/FormattedStringBuilderTest.java
+++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/FormattedStringBuilderTest.java
@@ -37,8 +37,8 @@
             sb1.append(str);
             sb2.append(str, null);
             sb3.append(str, null);
-            assertCharSequenceEquals(sb1, sb2);
-            assertCharSequenceEquals(sb3, str);
+            assertCharSequenceEquals(str, sb1, sb2);
+            assertCharSequenceEquals(str, sb3, str);
 
             StringBuilder sb4 = new StringBuilder();
             FormattedStringBuilder sb5 = new FormattedStringBuilder();
@@ -47,25 +47,25 @@
             sb4.append("xx");
             sb5.append("😇xx", null);
             sb5.insert(2, str, null);
-            assertCharSequenceEquals(sb4, sb5);
+            assertCharSequenceEquals(str, sb4, sb5);
 
             int start = Math.min(1, str.length());
             int end = Math.min(10, str.length());
             sb4.insert(3, str, start, end);
             sb5.insert(3, str, start, end, null);
-            assertCharSequenceEquals(sb4, sb5);
+            assertCharSequenceEquals(str, sb4, sb5);
 
             sb4.append(str.toCharArray());
             sb5.append(str.toCharArray(), null);
-            assertCharSequenceEquals(sb4, sb5);
+            assertCharSequenceEquals(str, sb4, sb5);
 
             sb4.insert(4, str.toCharArray());
             sb5.insert(4, str.toCharArray(), null);
-            assertCharSequenceEquals(sb4, sb5);
+            assertCharSequenceEquals(str, sb4, sb5);
 
             sb4.append(sb4.toString());
             sb5.append(new FormattedStringBuilder(sb5));
-            assertCharSequenceEquals(sb4, sb5);
+            assertCharSequenceEquals(str, sb4, sb5);
         }
     }
 
@@ -96,7 +96,7 @@
                 sb2.clear();
                 sb2.append(input, null);
                 sb2.splice(startThis, endThis, replacement, 0, replacement.length(), null);
-                assertCharSequenceEquals(sb1, sb2);
+                assertCharSequenceEquals(input, sb1, sb2);
 
                 // Test replacement with partial string
                 if (replacement.length() <= 2) {
@@ -108,7 +108,7 @@
                 sb2.clear();
                 sb2.append(input, null);
                 sb2.splice(startThis, endThis, replacement, 1, 3, null);
-                assertCharSequenceEquals(sb1, sb2);
+                assertCharSequenceEquals(input, sb1, sb2);
             }
         }
     }
@@ -124,7 +124,7 @@
             sb1.appendCodePoint(cas);
             sb2.appendCodePoint(cas, null);
             sb3.appendCodePoint(cas, null);
-            assertCharSequenceEquals(sb1, sb2);
+            assertCharSequenceEquals(Integer.toString(cas), sb1, sb2);
             assertEquals(Character.codePointAt(sb3, 0), cas);
 
             StringBuilder sb4 = new StringBuilder();
@@ -134,7 +134,18 @@
             sb4.append("xx");
             sb5.append("😇xx", null);
             sb5.insertCodePoint(2, cas, null);
-            assertCharSequenceEquals(sb4, sb5);
+            assertCharSequenceEquals(Integer.toString(cas), sb4, sb5);
+
+            StringBuilder sb6 = new StringBuilder();
+            FormattedStringBuilder sb7 = new FormattedStringBuilder();
+            sb6.appendCodePoint(cas);
+            if (Character.charCount(cas) == 2) {
+                sb7.appendChar16(Character.lowSurrogate(cas), null);
+                sb7.insertChar16(0, Character.highSurrogate(cas), null);
+            } else {
+                sb7.insertChar16(0, (char) cas, null);
+            }
+            assertCharSequenceEquals(Integer.toString(cas), sb6, sb7);
         }
     }
 
@@ -144,7 +155,7 @@
             FormattedStringBuilder sb1 = new FormattedStringBuilder();
             sb1.append(str, null);
             FormattedStringBuilder sb2 = new FormattedStringBuilder(sb1);
-            assertCharSequenceEquals(sb1, sb2);
+            assertCharSequenceEquals(str, sb1, sb2);
             assertTrue(sb1.contentEquals(sb2));
 
             sb1.append("12345", null);
@@ -251,21 +262,21 @@
         assertEquals("Code point count is 2", 2, nsb.codePointCount());
     }
 
-    private static void assertCharSequenceEquals(CharSequence a, CharSequence b) {
-        assertEquals(a.toString(), b.toString());
+    private static void assertCharSequenceEquals(String msg, CharSequence a, CharSequence b) {
+        assertEquals(msg, a.toString(), b.toString());
 
-        assertEquals(a.length(), b.length());
+        assertEquals(msg, a.length(), b.length());
         for (int i = 0; i < a.length(); i++) {
-            assertEquals(a.charAt(i), b.charAt(i));
+            assertEquals(msg, a.charAt(i), b.charAt(i));
         }
 
         int start = Math.min(2, a.length());
         int end = Math.min(12, a.length());
         if (start != end) {
-            assertCharSequenceEquals(a.subSequence(start, end), b.subSequence(start, end));
+            assertCharSequenceEquals(msg, a.subSequence(start, end), b.subSequence(start, end));
             if (b instanceof FormattedStringBuilder) {
                 FormattedStringBuilder bnsb = (FormattedStringBuilder) b;
-                assertCharSequenceEquals(a.subSequence(start, end), bnsb.subString(start, end));
+                assertCharSequenceEquals(msg, a.subSequence(start, end), bnsb.subString(start, end));
             }
         }
     }