diff --git a/java/src/com/android/inputmethod/latin/StringUtils.java b/java/src/com/android/inputmethod/latin/StringUtils.java
index 11ef60dc9af4d5ff0a38023136560f6007d97770..3ca209d3403d0a2ecb5e73f47fc51dfdf91cbd8f 100644
--- a/java/src/com/android/inputmethod/latin/StringUtils.java
+++ b/java/src/com/android/inputmethod/latin/StringUtils.java
@@ -222,4 +222,75 @@ public final class StringUtils {
         if (1 == capsCount) return CAPITALIZE_FIRST;
         return (letterCount == capsCount ? CAPITALIZE_ALL : CAPITALIZE_NONE);
     }
+
+    public static boolean isIdenticalAfterUpcase(final String text) {
+        final int len = text.length();
+        for (int i = 0; i < len; i = text.offsetByCodePoints(i, 1)) {
+            final int codePoint = text.codePointAt(i);
+            if (Character.isLetter(codePoint) && !Character.isUpperCase(codePoint)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public static boolean isIdenticalAfterDowncase(final String text) {
+        final int len = text.length();
+        for (int i = 0; i < len; i = text.offsetByCodePoints(i, 1)) {
+            final int codePoint = text.codePointAt(i);
+            if (Character.isLetter(codePoint) && !Character.isLowerCase(codePoint)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public static boolean isIdenticalAfterCapitalizeEachWord(final String text,
+            final String separators) {
+        boolean needCapsNext = true;
+        final int len = text.length();
+        for (int i = 0; i < len; i = text.offsetByCodePoints(i, 1)) {
+            final int codePoint = text.codePointAt(i);
+            if (Character.isLetter(codePoint)) {
+                if ((needCapsNext && !Character.isUpperCase(codePoint))
+                        || (!needCapsNext && !Character.isLowerCase(codePoint))) {
+                    return false;
+                }
+            }
+            // We need a capital letter next if this is a separator.
+            needCapsNext = (-1 != separators.indexOf(codePoint));
+        }
+        return true;
+    }
+
+    // TODO: like capitalizeFirst*, this does not work perfectly for Dutch because of the IJ digraph
+    // which should be capitalized together in *some* cases.
+    public static String capitalizeEachWord(final String text, final String separators,
+            final Locale locale) {
+        final StringBuilder builder = new StringBuilder();
+        boolean needCapsNext = true;
+        final int len = text.length();
+        for (int i = 0; i < len; i = text.offsetByCodePoints(i, 1)) {
+            final String nextChar = text.substring(i, text.offsetByCodePoints(i, 1));
+            if (needCapsNext) {
+                builder.append(nextChar.toUpperCase(locale));
+            } else {
+                builder.append(nextChar.toLowerCase(locale));
+            }
+            // We need a capital letter next if this is a separator.
+            needCapsNext = (-1 != separators.indexOf(nextChar.codePointAt(0)));
+        }
+        return builder.toString();
+    }
+
+    public static boolean containsAny(final String string, final String separators) {
+        final int len = separators.length();
+        for (int i = 0; i < len; i = separators.offsetByCodePoints(i, 1)) {
+            final int separator = separators.codePointAt(i);
+            if (-1 != string.indexOf(separator)) {
+                return true;
+            }
+        }
+        return false;
+    }
 }
diff --git a/tests/src/com/android/inputmethod/latin/StringUtilsTests.java b/tests/src/com/android/inputmethod/latin/StringUtilsTests.java
index b6a05e9fb616a4a0f54cd15b62e80b4432c6c33a..136faff71e5fce69c10201382a179a92e40a117d 100644
--- a/tests/src/com/android/inputmethod/latin/StringUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/StringUtilsTests.java
@@ -154,4 +154,83 @@ public class StringUtilsTests extends AndroidTestCase {
         assertEquals(StringUtils.CAPITALIZE_NONE,
                 StringUtils.getCapitalizationType(""));
     }
+
+    public void testIsIdenticalAfterUpcaseIsIdenticalAfterDowncase() {
+        assertFalse(StringUtils.isIdenticalAfterUpcase("capitalize"));
+        assertTrue(StringUtils.isIdenticalAfterDowncase("capitalize"));
+        assertFalse(StringUtils.isIdenticalAfterUpcase("cApITalize"));
+        assertFalse(StringUtils.isIdenticalAfterDowncase("cApITalize"));
+        assertFalse(StringUtils.isIdenticalAfterUpcase("capitalizE"));
+        assertFalse(StringUtils.isIdenticalAfterDowncase("capitalizE"));
+        assertFalse(StringUtils.isIdenticalAfterUpcase("__c a piu$@tali56ze"));
+        assertTrue(StringUtils.isIdenticalAfterDowncase("__c a piu$@tali56ze"));
+        assertFalse(StringUtils.isIdenticalAfterUpcase("A__c a piu$@tali56ze"));
+        assertFalse(StringUtils.isIdenticalAfterDowncase("A__c a piu$@tali56ze"));
+        assertFalse(StringUtils.isIdenticalAfterUpcase("Capitalize"));
+        assertFalse(StringUtils.isIdenticalAfterDowncase("Capitalize"));
+        assertFalse(StringUtils.isIdenticalAfterUpcase("     Capitalize"));
+        assertFalse(StringUtils.isIdenticalAfterDowncase("     Capitalize"));
+        assertTrue(StringUtils.isIdenticalAfterUpcase("CAPITALIZE"));
+        assertFalse(StringUtils.isIdenticalAfterDowncase("CAPITALIZE"));
+        assertTrue(StringUtils.isIdenticalAfterUpcase("  PI26LIE"));
+        assertFalse(StringUtils.isIdenticalAfterDowncase("  PI26LIE"));
+        assertTrue(StringUtils.isIdenticalAfterUpcase(""));
+        assertTrue(StringUtils.isIdenticalAfterDowncase(""));
+    }
+
+    private void checkCapitalize(final String src, final String dst, final String separators,
+            final Locale locale) {
+        assertEquals(dst, StringUtils.capitalizeEachWord(src, separators, locale));
+        assert(src.equals(dst)
+                == StringUtils.isIdenticalAfterCapitalizeEachWord(src, separators));
+    }
+
+    public void testCapitalizeEachWord() {
+        checkCapitalize("", "", " ", Locale.ENGLISH);
+        checkCapitalize("test", "Test", " ", Locale.ENGLISH);
+        checkCapitalize("    test", "    Test", " ", Locale.ENGLISH);
+        checkCapitalize("Test", "Test", " ", Locale.ENGLISH);
+        checkCapitalize("    Test", "    Test", " ", Locale.ENGLISH);
+        checkCapitalize(".Test", ".test", " ", Locale.ENGLISH);
+        checkCapitalize(".Test", ".Test", " .", Locale.ENGLISH);
+        checkCapitalize(".Test", ".Test", ". ", Locale.ENGLISH);
+        checkCapitalize("test and retest", "Test And Retest", " .", Locale.ENGLISH);
+        checkCapitalize("Test and retest", "Test And Retest", " .", Locale.ENGLISH);
+        checkCapitalize("Test And Retest", "Test And Retest", " .", Locale.ENGLISH);
+        checkCapitalize("Test And.Retest  ", "Test And.Retest  ", " .", Locale.ENGLISH);
+        checkCapitalize("Test And.retest  ", "Test And.Retest  ", " .", Locale.ENGLISH);
+        checkCapitalize("Test And.retest  ", "Test And.retest  ", " ", Locale.ENGLISH);
+        checkCapitalize("Test And.Retest  ", "Test And.retest  ", " ", Locale.ENGLISH);
+        checkCapitalize("test and ietest", "Test And Ä°etest", " .", new Locale("tr"));
+        checkCapitalize("test and ietest", "Test And Ietest", " .", Locale.ENGLISH);
+        checkCapitalize("Test&Retest", "Test&Retest", " \n.!?*()&", Locale.ENGLISH);
+        checkCapitalize("Test&retest", "Test&Retest", " \n.!?*()&", Locale.ENGLISH);
+        checkCapitalize("test&Retest", "Test&Retest", " \n.!?*()&", Locale.ENGLISH);
+        checkCapitalize("rest\nrecreation! And in the end...",
+                "Rest\nRecreation! And In The End...", " \n.!?*,();&", Locale.ENGLISH);
+        checkCapitalize("lorem ipsum dolor sit amet", "Lorem Ipsum Dolor Sit Amet",
+                " \n.,!?*()&;", Locale.ENGLISH);
+        checkCapitalize("Lorem!Ipsum (Dolor) Sit * Amet", "Lorem!Ipsum (Dolor) Sit * Amet",
+                " \n,.;!?*()&", Locale.ENGLISH);
+        checkCapitalize("Lorem!Ipsum (dolor) Sit * Amet", "Lorem!Ipsum (Dolor) Sit * Amet",
+                " \n,.;!?*()&", Locale.ENGLISH);
+    }
+
+    public void testContainsAny() {
+        assertFalse(StringUtils.containsAny("", " "));
+        assertFalse(StringUtils.containsAny("test and retest", ""));
+        assertTrue(StringUtils.containsAny("test and retest", "x3iq o"));
+        assertTrue(StringUtils.containsAny("test and retest", "x3iqo "));
+        assertTrue(StringUtils.containsAny("test and retest", " x3iqo"));
+        assertFalse(StringUtils.containsAny("test and retest", "x3iqo"));
+        assertTrue(StringUtils.containsAny("test and retest", "tse "));
+        assertTrue(StringUtils.containsAny("test and retest.", ".?()"));
+        assertFalse(StringUtils.containsAny("test and retest", ".?()"));
+        // Surrogate pair
+        assertTrue(StringUtils.containsAny("test and \uD861\uDED7 retest.", "\uD861\uDED7"));
+        // Ill-formed string
+        assertFalse(StringUtils.containsAny("test and \uD861 retest.", "\uD861\uDED7"));
+        // Ill-formed string
+        assertFalse(StringUtils.containsAny("test and \uDED7 retest.", "\uD861\uDED7"));
+    }
 }