Merge "Fix a view-related crash"
diff --git a/java/res/values-iw/config-spacing-and-punctuations.xml b/java/res/values-ar-sw600dp/config-spacing-and-punctuations.xml
similarity index 83%
rename from java/res/values-iw/config-spacing-and-punctuations.xml
rename to java/res/values-ar-sw600dp/config-spacing-and-punctuations.xml
index 9e637e4..5629636 100644
--- a/java/res/values-iw/config-spacing-and-punctuations.xml
+++ b/java/res/values-ar-sw600dp/config-spacing-and-punctuations.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2012, The Android Open Source Project
+** Copyright 2014, The Android Open Source Project
 **
 ** Licensed under the Apache License, Version 2.0 (the "License");
 ** you may not use this file except in compliance with the License.
@@ -21,5 +21,7 @@
     <!-- The all letters need to be mirrored are found at
          http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt -->
     <!-- Symbols that are suggested between words -->
-    <string name="suggested_punctuations" translatable="false">!,?,\\,,:,;,\",(|),)|(,\',-,/,@,_</string>
+    <!-- U+061F: "؟" ARABIC QUESTION MARK
+         U+061B: "؛" ARABIC SEMICOLON -->
+    <string name="suggested_punctuations" translatable="false">!,&#x061F;,:,&#x061B;,\",\',(|),)|(,-,/,@,_</string>
 </resources>
diff --git a/java/res/values-ar/config-spacing-and-punctuations.xml b/java/res/values-ar/config-spacing-and-punctuations.xml
index 9e637e4..d33a104 100644
--- a/java/res/values-ar/config-spacing-and-punctuations.xml
+++ b/java/res/values-ar/config-spacing-and-punctuations.xml
@@ -21,5 +21,8 @@
     <!-- The all letters need to be mirrored are found at
          http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt -->
     <!-- Symbols that are suggested between words -->
-    <string name="suggested_punctuations" translatable="false">!,?,\\,,:,;,\",(|),)|(,\',-,/,@,_</string>
+    <!-- U+061F: "؟" ARABIC QUESTION MARK
+         U+060C: "،" ARABIC COMMA
+         U+061B: "؛" ARABIC SEMICOLON -->
+    <string name="suggested_punctuations" translatable="false">!,&#x061F;,&#x060C;,:,&#x061B;,\",(|),)|(,\',-,/,@,_</string>
 </resources>
diff --git a/java/res/values-iw/config-spacing-and-punctuations.xml b/java/res/values-fa-sw600dp/config-spacing-and-punctuations.xml
similarity index 83%
copy from java/res/values-iw/config-spacing-and-punctuations.xml
copy to java/res/values-fa-sw600dp/config-spacing-and-punctuations.xml
index 9e637e4..5629636 100644
--- a/java/res/values-iw/config-spacing-and-punctuations.xml
+++ b/java/res/values-fa-sw600dp/config-spacing-and-punctuations.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2012, The Android Open Source Project
+** Copyright 2014, The Android Open Source Project
 **
 ** Licensed under the Apache License, Version 2.0 (the "License");
 ** you may not use this file except in compliance with the License.
@@ -21,5 +21,7 @@
     <!-- The all letters need to be mirrored are found at
          http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt -->
     <!-- Symbols that are suggested between words -->
-    <string name="suggested_punctuations" translatable="false">!,?,\\,,:,;,\",(|),)|(,\',-,/,@,_</string>
+    <!-- U+061F: "؟" ARABIC QUESTION MARK
+         U+061B: "؛" ARABIC SEMICOLON -->
+    <string name="suggested_punctuations" translatable="false">!,&#x061F;,:,&#x061B;,\",\',(|),)|(,-,/,@,_</string>
 </resources>
diff --git a/java/res/values-fa/config-spacing-and-punctuations.xml b/java/res/values-fa/config-spacing-and-punctuations.xml
index 9e637e4..d33a104 100644
--- a/java/res/values-fa/config-spacing-and-punctuations.xml
+++ b/java/res/values-fa/config-spacing-and-punctuations.xml
@@ -21,5 +21,8 @@
     <!-- The all letters need to be mirrored are found at
          http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt -->
     <!-- Symbols that are suggested between words -->
-    <string name="suggested_punctuations" translatable="false">!,?,\\,,:,;,\",(|),)|(,\',-,/,@,_</string>
+    <!-- U+061F: "؟" ARABIC QUESTION MARK
+         U+060C: "،" ARABIC COMMA
+         U+061B: "؛" ARABIC SEMICOLON -->
+    <string name="suggested_punctuations" translatable="false">!,&#x061F;,&#x060C;,:,&#x061B;,\",(|),)|(,\',-,/,@,_</string>
 </resources>
diff --git a/java/res/values-iw/config-spacing-and-punctuations.xml b/java/res/values-sw600dp/config-spacing-and-punctuations.xml
similarity index 72%
copy from java/res/values-iw/config-spacing-and-punctuations.xml
copy to java/res/values-sw600dp/config-spacing-and-punctuations.xml
index 9e637e4..9c12cf4 100644
--- a/java/res/values-iw/config-spacing-and-punctuations.xml
+++ b/java/res/values-sw600dp/config-spacing-and-punctuations.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2012, The Android Open Source Project
+** Copyright 2014, The Android Open Source Project
 **
 ** Licensed under the Apache License, Version 2.0 (the "License");
 ** you may not use this file except in compliance with the License.
@@ -18,8 +18,6 @@
 */
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- The all letters need to be mirrored are found at
-         http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt -->
     <!-- Symbols that are suggested between words -->
-    <string name="suggested_punctuations" translatable="false">!,?,\\,,:,;,\",(|),)|(,\',-,/,@,_</string>
+    <string name="suggested_punctuations" translatable="false">:,;,\",!text/keyspec_left_parenthesis,!text/keyspec_right_parenthesis,\',-,/,@,_</string>
 </resources>
diff --git a/java/res/values/config-spacing-and-punctuations.xml b/java/res/values/config-spacing-and-punctuations.xml
index 2f52edd..9099f4c 100644
--- a/java/res/values/config-spacing-and-punctuations.xml
+++ b/java/res/values/config-spacing-and-punctuations.xml
@@ -21,7 +21,7 @@
     <!-- TODO: these settings depend on the language. They should be put either in the dictionary
          header, or in the subtype maybe? -->
     <!-- Symbols that are suggested between words -->
-    <string name="suggested_punctuations" translatable="false">!,?,\\,,:,;,\",(,),\',-,/,@,_</string>
+    <string name="suggested_punctuations" translatable="false">!,?,\\,,:,;,\",!text/keyspec_left_parenthesis,!text/keyspec_right_parenthesis,\',-,/,@,_</string>
     <!-- Symbols that are normally preceded by a space (used to add an auto-space before these) -->
     <string name="symbols_preceded_by_space" translatable="false">([{&amp;</string>
     <!-- Symbols that are normally followed by a space (used to add an auto-space after these) -->
diff --git a/java/res/xml-sw600dp/key_styles_common.xml b/java/res/xml-sw600dp/key_styles_common.xml
index 6a79c6f..aa64f85 100644
--- a/java/res/xml-sw600dp/key_styles_common.xml
+++ b/java/res/xml-sw600dp/key_styles_common.xml
@@ -161,7 +161,7 @@
         latin:parentStyle="baseForLayoutSwitchKeyStyle" />
     <key-style
         latin:styleName="comKeyStyle"
-        latin:keySpec="!text/keylabel_for_popular_domain|!text/keylabel_for_popular_domain"
+        latin:keySpec="!text/keylabel_for_popular_domain"
         latin:keyLabelFlags="autoXScale|fontNormal|hasPopupHint|preserveCase"
         latin:moreKeys="!text/more_keys_for_popular_domain" />
 </merge>
diff --git a/java/res/xml/key_styles_common.xml b/java/res/xml/key_styles_common.xml
index f04fc45..2330ecb 100644
--- a/java/res/xml/key_styles_common.xml
+++ b/java/res/xml/key_styles_common.xml
@@ -149,7 +149,7 @@
         latin:parentStyle="baseForLayoutSwitchKeyStyle" />
     <key-style
         latin:styleName="comKeyStyle"
-        latin:keySpec="!text/keylabel_for_popular_domain|!text/keylabel_for_popular_domain"
+        latin:keySpec="!text/keylabel_for_popular_domain"
         latin:keyLabelFlags="autoXScale|fontNormal|hasPopupHint|preserveCase"
         latin:moreKeys="!text/more_keys_for_popular_domain"
         latin:backgroundType="functional" />
diff --git a/java/res/xml/method.xml b/java/res/xml/method.xml
index 5e38eb3..94327f9 100644
--- a/java/res/xml/method.xml
+++ b/java/res/xml/method.xml
@@ -55,7 +55,7 @@
     it: Italian/qwerty
     iw: Hebrew/hebrew        # "he" is official language code of Hebrew.
     ka_GE: Georgian (Georgia)/georgian
-    (kk: Kazakh/east_slavic) # disabled temporarily. waiting for string resources.
+    kk: Kazakh/east_slavic
     km_KH: Khmer (Cambodia)/khmer
     ky: Kyrgyz/east_slavic
     lo_LA: Lao (Laos)/lao
@@ -367,7 +367,6 @@
             android:imeSubtypeExtraValue="KeyboardLayoutSet=georgian,EmojiCapable"
             android:isAsciiCapable="false"
     />
-    <!--
     <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x2d73d2f6"
@@ -376,7 +375,6 @@
             android:imeSubtypeExtraValue="KeyboardLayoutSet=east_slavic,EmojiCapable"
             android:isAsciiCapable="false"
     />
-    -->
     <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x1365683a"
diff --git a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
index a32d76c..60f7e2d 100644
--- a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
+++ b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
@@ -23,7 +23,6 @@
 import android.text.TextUtils;
 import android.text.style.SuggestionSpan;
 
-import com.android.inputmethod.latin.Dictionary;
 import com.android.inputmethod.latin.LatinImeLogger;
 import com.android.inputmethod.latin.SuggestedWords;
 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
@@ -70,7 +69,7 @@
     public static CharSequence getTextWithSuggestionSpan(final Context context,
             final String pickedWord, final SuggestedWords suggestedWords) {
         if (TextUtils.isEmpty(pickedWord) || suggestedWords.isEmpty()
-                || suggestedWords.mIsPrediction || suggestedWords.mIsPunctuationSuggestions) {
+                || suggestedWords.mIsPrediction || suggestedWords.isPunctuationSuggestions()) {
             return pickedWord;
         }
 
diff --git a/java/src/com/android/inputmethod/keyboard/KeyDetector.java b/java/src/com/android/inputmethod/keyboard/KeyDetector.java
index 149f10f..282c8e8 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyDetector.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyDetector.java
@@ -18,7 +18,9 @@
 
 import com.android.inputmethod.latin.Constants;
 
-
+/**
+ * This class handles key detection.
+ */
 public class KeyDetector {
     private final int mKeyHysteresisDistanceSquared;
     private final int mKeyHysteresisDistanceForSlidingModifierSquared;
@@ -27,18 +29,12 @@
     private int mCorrectionX;
     private int mCorrectionY;
 
-    /**
-     * This class handles key detection.
-     *
-     * @param keyHysteresisDistance if the pointer movement distance is smaller than this, the
-     * movement will not be handled as meaningful movement. The unit is pixel.
-     */
-    public KeyDetector(float keyHysteresisDistance) {
-        this(keyHysteresisDistance, keyHysteresisDistance);
+    public KeyDetector() {
+        this(0.0f /* keyHysteresisDistance */, 0.0f /* keyHysteresisDistanceForSlidingModifier */);
     }
 
     /**
-     * This class handles key detection.
+     * Key detection object constructor with key hysteresis distances.
      *
      * @param keyHysteresisDistance if the pointer movement distance is smaller than this, the
      * movement will not be handled as meaningful movement. The unit is pixel.
diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java b/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java
index 81b8f04..4a80279 100644
--- a/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java
+++ b/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java
@@ -21,7 +21,7 @@
     private final int mSlideAllowanceSquareTop;
 
     public MoreKeysDetector(float slideAllowance) {
-        super(/* keyHysteresisDistance */0);
+        super();
         mSlideAllowanceSquare = (int)(slideAllowance * slideAllowance);
         // Top slide allowance is slightly longer (sqrt(2) times) than other edges.
         mSlideAllowanceSquareTop = mSlideAllowanceSquare * 2;
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index b5d82fa..befc4e6 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -155,7 +155,7 @@
 
     // The {@link KeyDetector} is set whenever the down event is processed. Also this is updated
     // when new {@link Keyboard} is set by {@link #setKeyDetector(KeyDetector)}.
-    private KeyDetector mKeyDetector;
+    private KeyDetector mKeyDetector = new KeyDetector();
     private Keyboard mKeyboard;
     private int mPhantomSuddenMoveThreshold;
     private final BogusMoveEventDetector mBogusMoveEventDetector = new BogusMoveEventDetector();
@@ -1124,9 +1124,6 @@
 
     private boolean isMajorEnoughMoveToBeOnNewKey(final int x, final int y, final long eventTime,
             final Key newKey) {
-        if (mKeyDetector == null) {
-            throw new NullPointerException("keyboard and/or key detector not set");
-        }
         final Key curKey = mCurrentKey;
         if (newKey == curKey) {
             return false;
diff --git a/java/src/com/android/inputmethod/keyboard/internal/EmojiPageKeyboardView.java b/java/src/com/android/inputmethod/keyboard/internal/EmojiPageKeyboardView.java
index be73965..e175a05 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/EmojiPageKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/EmojiPageKeyboardView.java
@@ -52,7 +52,7 @@
     };
 
     private OnKeyEventListener mListener = EMPTY_LISTENER;
-    private final KeyDetector mKeyDetector = new KeyDetector(0.0f /*keyHysteresisDistance */);
+    private final KeyDetector mKeyDetector = new KeyDetector();
     private final GestureDetector mGestureDetector;
 
     public EmojiPageKeyboardView(final Context context, final AttributeSet attrs) {
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java
index 1e021e5..a7bcef0 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java
@@ -211,31 +211,30 @@
         /* 144: 0 */ "action_previous_as_more_key",
         /* 145: 0 */ "label_to_more_symbol_key",
         /* 146: 0 */ "label_to_more_symbol_for_tablet_key",
-        /* 147: 0 */ "label_tab_key",
-        /* 148: 0 */ "label_to_phone_numeric_key",
-        /* 149: 0 */ "label_to_phone_symbols_key",
-        /* 150: 0 */ "label_time_am",
-        /* 151: 0 */ "label_time_pm",
-        /* 152: 0 */ "keylabel_for_popular_domain",
-        /* 153: 0 */ "more_keys_for_popular_domain",
-        /* 154: 0 */ "keyspecs_for_left_parenthesis_more_keys",
-        /* 155: 0 */ "keyspecs_for_right_parenthesis_more_keys",
-        /* 156: 0 */ "single_laqm_raqm",
-        /* 157: 0 */ "single_raqm_laqm",
-        /* 158: 0 */ "double_laqm_raqm",
-        /* 159: 0 */ "double_raqm_laqm",
-        /* 160: 0 */ "single_lqm_rqm",
-        /* 161: 0 */ "single_9qm_lqm",
-        /* 162: 0 */ "single_9qm_rqm",
-        /* 163: 0 */ "single_rqm_9qm",
-        /* 164: 0 */ "double_lqm_rqm",
-        /* 165: 0 */ "double_9qm_lqm",
-        /* 166: 0 */ "double_9qm_rqm",
-        /* 167: 0 */ "double_rqm_9qm",
-        /* 168: 0 */ "more_keys_for_single_quote",
-        /* 169: 0 */ "more_keys_for_double_quote",
-        /* 170: 0 */ "more_keys_for_tablet_double_quote",
-        /* 171: 0 */ "emoji_key_as_more_key",
+        /* 147: 0 */ "label_to_phone_numeric_key",
+        /* 148: 0 */ "label_to_phone_symbols_key",
+        /* 149: 0 */ "label_time_am",
+        /* 150: 0 */ "label_time_pm",
+        /* 151: 0 */ "keylabel_for_popular_domain",
+        /* 152: 0 */ "more_keys_for_popular_domain",
+        /* 153: 0 */ "keyspecs_for_left_parenthesis_more_keys",
+        /* 154: 0 */ "keyspecs_for_right_parenthesis_more_keys",
+        /* 155: 0 */ "single_laqm_raqm",
+        /* 156: 0 */ "single_raqm_laqm",
+        /* 157: 0 */ "double_laqm_raqm",
+        /* 158: 0 */ "double_raqm_laqm",
+        /* 159: 0 */ "single_lqm_rqm",
+        /* 160: 0 */ "single_9qm_lqm",
+        /* 161: 0 */ "single_9qm_rqm",
+        /* 162: 0 */ "single_rqm_9qm",
+        /* 163: 0 */ "double_lqm_rqm",
+        /* 164: 0 */ "double_9qm_lqm",
+        /* 165: 0 */ "double_9qm_rqm",
+        /* 166: 0 */ "double_rqm_9qm",
+        /* 167: 0 */ "more_keys_for_single_quote",
+        /* 168: 0 */ "more_keys_for_double_quote",
+        /* 169: 0 */ "more_keys_for_tablet_double_quote",
+        /* 170: 0 */ "emoji_key_as_more_key",
     };
 
     private static final String EMPTY = "";
@@ -399,8 +398,6 @@
         /* label_to_more_symbol_key */ "= \\\\ <",
         // Label for "switch to more symbol" modifier key on tablets.  Must be short to fit on key!
         /* label_to_more_symbol_for_tablet_key */ "~ [ <",
-        // Label for "Tab" key.  Must be short to fit on key!
-        /* label_tab_key */ "Tab",
         // Label for "switch to phone numeric" key.  Must be short to fit on key!
         /* label_to_phone_numeric_key */ "123",
         // Label for "switch to phone symbols" key.  Must be short to fit on key!
@@ -3572,7 +3569,7 @@
     // Currently we are dropping the region from the key.
     private static final Object[] LANGUAGES_AND_TEXTS = {
     // "locale", TEXT_ARRAY,  /* numberOfNonNullText/lengthOf_TEXT_ARRAY localeName */
-        "DEFAULT", LANGUAGE_DEFAULT, /* 172/172 default */
+        "DEFAULT", LANGUAGE_DEFAULT, /* 171/171 default */
         "af", LANGUAGE_af,    /*   8/ 12 Afrikaans */
         "ar", LANGUAGE_ar,    /*  58/110 Arabic */
         "az", LANGUAGE_az_AZ, /*   8/ 17 Azerbaijani (Azerbaijan) */
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 7b72e80..d1125af 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -1001,7 +1001,6 @@
                 applicationSuggestedWords, null /* rawSuggestions */,
                 false /* typedWordValid */,
                 false /* willAutoCorrect */,
-                false /* isPunctuationSuggestions */,
                 false /* isObsoleteSuggestions */,
                 false /* isPrediction */);
         // When in fullscreen mode, show completions generated by the application
@@ -1354,7 +1353,7 @@
         if (mSuggestionStripView != null) {
             final boolean showSuggestions;
             if (SuggestedWords.EMPTY == suggestedWords
-                    || suggestedWords.mIsPunctuationSuggestions
+                    || suggestedWords.isPunctuationSuggestions()
                     || !mSettings.getCurrent().isSuggestionsRequested()) {
                 showSuggestions = !mSuggestionStripView.maybeShowImportantNoticeTitle();
             } else {
@@ -1433,7 +1432,6 @@
             return new SuggestedWords(typedWordAndPreviousSuggestions, null /* rawSuggestions */,
                     false /* typedWordValid */,
                     false /* hasAutoCorrectionCandidate */,
-                    false /* isPunctuationSuggestions */,
                     true /* isObsoleteSuggestions */,
                     false /* isPrediction */);
         }
diff --git a/java/src/com/android/inputmethod/latin/PunctuationSuggestions.java b/java/src/com/android/inputmethod/latin/PunctuationSuggestions.java
new file mode 100644
index 0000000..4911bcd
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/PunctuationSuggestions.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin;
+
+import com.android.inputmethod.keyboard.internal.KeySpecParser;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+import com.android.inputmethod.latin.utils.CollectionUtils;
+import com.android.inputmethod.latin.utils.StringUtils;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * The extended {@link SuggestedWords} class to represent punctuation suggestions.
+ *
+ * Each punctuation specification string is the key specification that can be parsed by
+ * {@link KeySpecParser}.
+ */
+public final class PunctuationSuggestions extends SuggestedWords {
+    private PunctuationSuggestions(final ArrayList<SuggestedWordInfo> punctuationsList) {
+        super(punctuationsList,
+                null /* rawSuggestions */,
+                false /* typedWordValid */,
+                false /* hasAutoCorrectionCandidate */,
+                false /* isObsoleteSuggestions */,
+                false /* isPrediction */);
+    }
+
+    /**
+     * Create new instance of {@link PunctuationSuggestions} from the array of punctuation key
+     * specifications.
+     *
+     * @param punctuationSpecs The array of punctuation key specifications.
+     * @return The {@link PunctuationSuggestions} object.
+     */
+    public static PunctuationSuggestions newPunctuationSuggestions(
+            final String[] punctuationSpecs) {
+        final ArrayList<SuggestedWordInfo> puncuationsList = CollectionUtils.newArrayList();
+        for (final String puncSpec : punctuationSpecs) {
+            puncuationsList.add(newHardCodedWordInfo(puncSpec));
+        }
+        return new PunctuationSuggestions(puncuationsList);
+    }
+
+    /**
+     * {@inheritDoc}
+     * Note that {@link super#getWord(int)} returns a punctuation key specification text.
+     * The suggested punctuation should be gotten by parsing the key specification.
+     */
+    @Override
+    public String getWord(final int index) {
+        final String keySpec = super.getWord(index);
+        final int code = KeySpecParser.getCode(keySpec);
+        return (code == Constants.CODE_OUTPUT_TEXT)
+                ? KeySpecParser.getOutputText(keySpec)
+                : StringUtils.newSingleCodePointString(code);
+    }
+
+    /**
+     * {@inheritDoc}
+     * Note that {@link super#getWord(int)} returns a punctuation key specification text.
+     * The displayed text should be gotten by parsing the key specification.
+     */
+    @Override
+    public String getLabel(final int index) {
+        final String keySpec = super.getWord(index);
+        return KeySpecParser.getLabel(keySpec);
+    }
+
+    /**
+     * {@inheritDoc}
+     * Note that {@link #getWord(int)} returns a suggested punctuation. We should create a
+     * {@link SuggestedWordInfo} object that represents a hard coded word.
+     */
+    @Override
+    public SuggestedWordInfo getInfo(final int index) {
+        return newHardCodedWordInfo(getWord(index));
+    }
+
+    /**
+     * The predicator to tell whether this object represents punctuation suggestions.
+     * @return true if this object represents punctuation suggestions.
+     */
+    @Override
+    public boolean isPunctuationSuggestions() {
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return "PunctuationSuggestions: "
+                + " words=" + Arrays.toString(mSuggestedWordInfoList.toArray());
+    }
+
+    private static SuggestedWordInfo newHardCodedWordInfo(final String keySpec) {
+        return new SuggestedWordInfo(keySpec, SuggestedWordInfo.MAX_SCORE,
+                SuggestedWordInfo.KIND_HARDCODED,
+                Dictionary.DICTIONARY_HARDCODED,
+                SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
+                SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */);
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index 5e74d75..abf831a 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -227,7 +227,6 @@
                 // rename the attribute or change the value.
                 !allowsToBeAutoCorrected /* typedWordValid */,
                 hasAutoCorrection, /* willAutoCorrect */
-                false /* isPunctuationSuggestions */,
                 false /* isObsoleteSuggestions */,
                 !wordComposer.isComposingWord() /* isPrediction */, sequenceNumber));
     }
@@ -289,7 +288,6 @@
         callback.onGetSuggestedWords(new SuggestedWords(suggestionsContainer, rawSuggestions,
                 true /* typedWordValid */,
                 false /* willAutoCorrect */,
-                false /* isPunctuationSuggestions */,
                 false /* isObsoleteSuggestions */,
                 false /* isPrediction */, sequenceNumber));
     }
diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java
index b2efc4a..46df3e8 100644
--- a/java/src/com/android/inputmethod/latin/SuggestedWords.java
+++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java
@@ -26,7 +26,7 @@
 import java.util.Arrays;
 import java.util.HashSet;
 
-public final class SuggestedWords {
+public class SuggestedWords {
     public static final int INDEX_OF_TYPED_WORD = 0;
     public static final int INDEX_OF_AUTO_CORRECTION = 1;
     public static final int NOT_A_SEQUENCE_NUMBER = -1;
@@ -37,7 +37,7 @@
     private static final ArrayList<SuggestedWordInfo> EMPTY_WORD_INFO_LIST =
             CollectionUtils.newArrayList(0);
     public static final SuggestedWords EMPTY = new SuggestedWords(
-            EMPTY_WORD_INFO_LIST, null /* rawSuggestions */, false, false, false, false, false);
+            EMPTY_WORD_INFO_LIST, null /* rawSuggestions */, false, false, false, false);
 
     public final String mTypedWord;
     public final boolean mTypedWordValid;
@@ -45,38 +45,34 @@
     // of what this flag means would be "the top suggestion is strong enough to auto-correct",
     // whether this exactly matches the user entry or not.
     public final boolean mWillAutoCorrect;
-    public final boolean mIsPunctuationSuggestions;
     public final boolean mIsObsoleteSuggestions;
     public final boolean mIsPrediction;
     public final int mSequenceNumber; // Sequence number for auto-commit.
-    private final ArrayList<SuggestedWordInfo> mSuggestedWordInfoList;
+    protected final ArrayList<SuggestedWordInfo> mSuggestedWordInfoList;
     public final ArrayList<SuggestedWordInfo> mRawSuggestions;
 
     public SuggestedWords(final ArrayList<SuggestedWordInfo> suggestedWordInfoList,
             final ArrayList<SuggestedWordInfo> rawSuggestions,
             final boolean typedWordValid,
             final boolean willAutoCorrect,
-            final boolean isPunctuationSuggestions,
             final boolean isObsoleteSuggestions,
             final boolean isPrediction) {
         this(suggestedWordInfoList, rawSuggestions, typedWordValid, willAutoCorrect,
-                isPunctuationSuggestions, isObsoleteSuggestions, isPrediction,
-                NOT_A_SEQUENCE_NUMBER);
+                isObsoleteSuggestions, isPrediction, NOT_A_SEQUENCE_NUMBER);
     }
 
     public SuggestedWords(final ArrayList<SuggestedWordInfo> suggestedWordInfoList,
             final ArrayList<SuggestedWordInfo> rawSuggestions,
             final boolean typedWordValid,
             final boolean willAutoCorrect,
-            final boolean isPunctuationSuggestions,
             final boolean isObsoleteSuggestions,
             final boolean isPrediction,
             final int sequenceNumber) {
         this(suggestedWordInfoList, rawSuggestions,
                 suggestedWordInfoList.isEmpty() ? null
                         : suggestedWordInfoList.get(INDEX_OF_TYPED_WORD).mWord,
-                typedWordValid, willAutoCorrect, isPunctuationSuggestions,
-                isObsoleteSuggestions, isPrediction, sequenceNumber);
+                typedWordValid, willAutoCorrect, isObsoleteSuggestions, isPrediction,
+                sequenceNumber);
     }
 
     public SuggestedWords(final ArrayList<SuggestedWordInfo> suggestedWordInfoList,
@@ -84,7 +80,6 @@
             final String typedWord,
             final boolean typedWordValid,
             final boolean willAutoCorrect,
-            final boolean isPunctuationSuggestions,
             final boolean isObsoleteSuggestions,
             final boolean isPrediction,
             final int sequenceNumber) {
@@ -92,7 +87,6 @@
         mRawSuggestions = rawSuggestions;
         mTypedWordValid = typedWordValid;
         mWillAutoCorrect = willAutoCorrect;
-        mIsPunctuationSuggestions = isPunctuationSuggestions;
         mIsObsoleteSuggestions = isObsoleteSuggestions;
         mIsPrediction = isPrediction;
         mSequenceNumber = sequenceNumber;
@@ -107,10 +101,32 @@
         return mSuggestedWordInfoList.size();
     }
 
+    /**
+     * Get suggested word at <code>index</code>.
+     * @param index The index of the suggested word.
+     * @return The suggested word.
+     */
     public String getWord(final int index) {
         return mSuggestedWordInfoList.get(index).mWord;
     }
 
+    /**
+     * Get displayed text at <code>index</code>.
+     * In RTL languages, the displayed text on the suggestion strip may be different from the
+     * suggested word that is returned from {@link #getWord(int)}. For example the displayed text
+     * of punctuation suggestion "(" should be ")".
+     * @param index The index of the text to display.
+     * @return The text to be displayed.
+     */
+    public String getLabel(final int index) {
+        return mSuggestedWordInfoList.get(index).mWord;
+    }
+
+    /**
+     * Get {@link SuggestedWordInfo} object at <code>index</code>.
+     * @param index The index of the {@link SuggestedWordInfo}.
+     * @return The {@link SuggestedWordInfo} object.
+     */
     public SuggestedWordInfo getInfo(final int index) {
         return mSuggestedWordInfoList.get(index);
     }
@@ -130,13 +146,20 @@
         return debugString;
     }
 
+    /**
+     * The predicator to tell whether this object represents punctuation suggestions.
+     * @return false if this object desn't represent punctuation suggestions.
+     */
+    public boolean isPunctuationSuggestions() {
+        return false;
+    }
+
     @Override
     public String toString() {
         // Pretty-print method to help debug
         return "SuggestedWords:"
                 + " mTypedWordValid=" + mTypedWordValid
                 + " mWillAutoCorrect=" + mWillAutoCorrect
-                + " mIsPunctuationSuggestions=" + mIsPunctuationSuggestions
                 + " words=" + Arrays.toString(mSuggestedWordInfoList.toArray());
     }
 
@@ -313,8 +336,8 @@
         // We should never autocorrect, so we say the typed word is valid. Also, in this case,
         // no auto-correction should take place hence willAutoCorrect = false.
         return new SuggestedWords(newSuggestions, null /* rawSuggestions */, typedWord,
-                true /* typedWordValid */, false /* willAutoCorrect */, mIsPunctuationSuggestions,
-                mIsObsoleteSuggestions, mIsPrediction, NOT_A_SEQUENCE_NUMBER);
+                true /* typedWordValid */, false /* willAutoCorrect */, mIsObsoleteSuggestions,
+                mIsPrediction, NOT_A_SEQUENCE_NUMBER);
     }
 
     // Creates a new SuggestedWordInfo from the currently suggested words that removes all but the
@@ -333,7 +356,6 @@
                     SuggestedWordInfo.NOT_A_CONFIDENCE));
         }
         return new SuggestedWords(newSuggestions, null /* rawSuggestions */, mTypedWordValid,
-                mWillAutoCorrect, mIsPunctuationSuggestions, mIsObsoleteSuggestions,
-                mIsPrediction);
+                mWillAutoCorrect, mIsObsoleteSuggestions, mIsPrediction);
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index bd114eb..6f73859 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -1341,8 +1341,8 @@
             final SuggestedWords suggestedWords = new SuggestedWords(suggestions,
                     null /* rawSuggestions */, typedWord,
                     true /* typedWordValid */, false /* willAutoCorrect */,
-                    false /* isPunctuationSuggestions */, false /* isObsoleteSuggestions */,
-                    false /* isPrediction */, SuggestedWords.NOT_A_SEQUENCE_NUMBER);
+                    false /* isObsoleteSuggestions */, false /* isPrediction */,
+                    SuggestedWords.NOT_A_SEQUENCE_NUMBER);
             mIsAutoCorrectionIndicatorOn = false;
             mLatinIME.mHandler.showSuggestionStrip(suggestedWords);
         }
diff --git a/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java b/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java
index 03883a4..5954758 100644
--- a/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java
+++ b/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java
@@ -18,17 +18,13 @@
 
 import android.content.res.Resources;
 
-import com.android.inputmethod.keyboard.internal.KeySpecParser;
+import com.android.inputmethod.keyboard.internal.KeyboardTextsSet;
 import com.android.inputmethod.keyboard.internal.MoreKeySpec;
 import com.android.inputmethod.latin.Constants;
-import com.android.inputmethod.latin.Dictionary;
+import com.android.inputmethod.latin.PunctuationSuggestions;
 import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.SuggestedWords;
-import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
-import com.android.inputmethod.latin.utils.CollectionUtils;
 import com.android.inputmethod.latin.utils.StringUtils;
 
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Locale;
 
@@ -37,7 +33,7 @@
     private final int[] mSortedSymbolsFollowedBySpace;
     private final int[] mSortedWordConnectors;
     public final int[] mSortedWordSeparators;
-    public final SuggestedWords mSuggestPuncList;
+    public final PunctuationSuggestions mSuggestPuncList;
     private final int mSentenceSeparator;
     public final String mSentenceSeparatorAndSpace;
     public final boolean mCurrentLanguageHasSpaces;
@@ -56,9 +52,6 @@
                 res.getString(R.string.symbols_word_connectors));
         mSortedWordSeparators = StringUtils.toSortedCodePointArray(
                 res.getString(R.string.symbols_word_separators));
-        final String[] suggestPuncsSpec = MoreKeySpec.splitKeySpecs(res.getString(
-                R.string.suggested_punctuations));
-        mSuggestPuncList = createSuggestPuncList(suggestPuncsSpec);
         mSentenceSeparator = res.getInteger(R.integer.sentence_separator);
         mSentenceSeparatorAndSpace = new String(new int[] {
                 mSentenceSeparator, Constants.CODE_SPACE }, 0, 2);
@@ -68,28 +61,11 @@
         // English variants. German rules (not "German typography") also have small gotchas.
         mUsesAmericanTypography = Locale.ENGLISH.getLanguage().equals(locale.getLanguage());
         mUsesGermanRules = Locale.GERMAN.getLanguage().equals(locale.getLanguage());
-    }
-
-    // Helper functions to create member values.
-    private static SuggestedWords createSuggestPuncList(final String[] puncs) {
-        final ArrayList<SuggestedWordInfo> puncList = CollectionUtils.newArrayList();
-        if (puncs != null) {
-            for (final String puncSpec : puncs) {
-                // TODO: Stop using KeySpecParser.getLabel().
-                // TODO: Punctuation suggestions should honor RTL languages.
-                puncList.add(new SuggestedWordInfo(KeySpecParser.getLabel(puncSpec),
-                        SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_HARDCODED,
-                        Dictionary.DICTIONARY_HARDCODED,
-                        SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
-                        SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */));
-            }
-        }
-        return new SuggestedWords(puncList, null /* rawSuggestions */,
-                false /* typedWordValid */,
-                false /* hasAutoCorrectionCandidate */,
-                true /* isPunctuationSuggestions */,
-                false /* isObsoleteSuggestions */,
-                false /* isPrediction */);
+        final KeyboardTextsSet textsSet = new KeyboardTextsSet();
+        textsSet.setLocale(locale);
+        final String[] suggestPuncsSpec = MoreKeySpec.splitKeySpecs(
+                textsSet.resolveTextReference(res.getString(R.string.suggested_punctuations)));
+        mSuggestPuncList = PunctuationSuggestions.newPunctuationSuggestions(suggestPuncsSpec);
     }
 
     public boolean isWordSeparator(final int code) {
diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
index 2f4c183..a104baa 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
@@ -74,7 +74,7 @@
             int rowStartIndex = fromIndex;
             final int size = Math.min(suggestedWords.size(), SuggestedWords.MAX_SUGGESTIONS);
             while (index < size) {
-                final String word = suggestedWords.getWord(index);
+                final String word = suggestedWords.getLabel(index);
                 // TODO: Should take care of text x-scaling.
                 mWidths[index] = (int)(TypefaceUtils.getStringWidth(word, paint) + padding);
                 final int numColumn = index - rowStartIndex + 1;
@@ -206,7 +206,7 @@
                 final int x = params.getX(index);
                 final int y = params.getY(index);
                 final int width = params.getWidth(index);
-                final String word = mSuggestedWords.getWord(index);
+                final String word = mSuggestedWords.getLabel(index);
                 final String info = mSuggestedWords.getDebugString(index);
                 final int indexInMoreSuggestions = index + SUGGESTION_CODE_BASE;
                 final Key key = new Key(word, KeyboardIconsSet.ICON_UNDEFINED,
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
index 5f05b48..7c11f54 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
@@ -46,6 +46,7 @@
 
 import com.android.inputmethod.compat.TextViewCompatUtils;
 import com.android.inputmethod.latin.LatinImeLogger;
+import com.android.inputmethod.latin.PunctuationSuggestions;
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.SuggestedWords;
 import com.android.inputmethod.latin.utils.AutoCorrectionUtils;
@@ -199,7 +200,7 @@
         if (indexInSuggestedWords >= suggestedWords.size()) {
             return null;
         }
-        final String word = suggestedWords.getWord(indexInSuggestedWords);
+        final String word = suggestedWords.getLabel(indexInSuggestedWords);
         final boolean isAutoCorrect = indexInSuggestedWords == 1
                 && suggestedWords.mWillAutoCorrect;
         final boolean isTypedWordValid = indexInSuggestedWords == 0
@@ -264,8 +265,8 @@
             // is in slot 1.
             if (positionInStrip == mCenterPositionInStrip
                     && AutoCorrectionUtils.shouldBlockAutoCorrectionBySafetyNet(
-                            suggestedWords.getWord(SuggestedWords.INDEX_OF_AUTO_CORRECTION),
-                            suggestedWords.getWord(SuggestedWords.INDEX_OF_TYPED_WORD))) {
+                            suggestedWords.getLabel(SuggestedWords.INDEX_OF_AUTO_CORRECTION),
+                            suggestedWords.getLabel(SuggestedWords.INDEX_OF_TYPED_WORD))) {
                 return 0xFFFF0000;
             }
         }
@@ -299,9 +300,9 @@
      */
     public int layoutAndReturnSuggestionCountInStrip(final SuggestedWords suggestedWords,
             final ViewGroup stripView, final ViewGroup placerView) {
-        if (suggestedWords.mIsPunctuationSuggestions) {
+        if (suggestedWords.isPunctuationSuggestions()) {
             return layoutPunctuationSuggestionsAndReturnSuggestionCountInStrip(
-                    suggestedWords, stripView);
+                    (PunctuationSuggestions)suggestedWords, stripView);
         }
 
         setupWordViewsTextAndColor(suggestedWords, mSuggestionsCountInStrip);
@@ -447,8 +448,8 @@
     }
 
     private int layoutPunctuationSuggestionsAndReturnSuggestionCountInStrip(
-            final SuggestedWords suggestedWords, final ViewGroup stripView) {
-        final int countInStrip = Math.min(suggestedWords.size(), PUNCTUATIONS_IN_STRIP);
+            final PunctuationSuggestions punctuationSuggestions, final ViewGroup stripView) {
+        final int countInStrip = Math.min(punctuationSuggestions.size(), PUNCTUATIONS_IN_STRIP);
         for (int positionInStrip = 0; positionInStrip < countInStrip; positionInStrip++) {
             if (positionInStrip != 0) {
                 // Add divider if this isn't the left most suggestion in suggestions strip.
@@ -461,13 +462,13 @@
             // {@link TextView#getTag()} is used to get the index in suggestedWords at
             // {@link SuggestionStripView#onClick(View)}.
             wordView.setTag(positionInStrip);
-            wordView.setText(suggestedWords.getWord(positionInStrip));
+            wordView.setText(punctuationSuggestions.getLabel(positionInStrip));
             wordView.setTextScaleX(1.0f);
             wordView.setCompoundDrawables(null, null, null, null);
             stripView.addView(wordView);
             setLayoutWeight(wordView, 1.0f, mSuggestionsStripHeight);
         }
-        mMoreSuggestionsAvailable = (suggestedWords.size() > countInStrip);
+        mMoreSuggestionsAvailable = (punctuationSuggestions.size() > countInStrip);
         return countInStrip;
     }
 
diff --git a/java/src/com/android/inputmethod/research/JsonUtils.java b/java/src/com/android/inputmethod/research/JsonUtils.java
index 2beebdf..6170b43 100644
--- a/java/src/com/android/inputmethod/research/JsonUtils.java
+++ b/java/src/com/android/inputmethod/research/JsonUtils.java
@@ -91,7 +91,7 @@
         jsonWriter.name("willAutoCorrect")
                 .value(words.mWillAutoCorrect);
         jsonWriter.name("isPunctuationSuggestions")
-                .value(words.mIsPunctuationSuggestions);
+                .value(words.isPunctuationSuggestions());
         jsonWriter.name("isObsoleteSuggestions").value(words.mIsObsoleteSuggestions);
         jsonWriter.name("isPrediction").value(words.mIsPrediction);
         jsonWriter.name("suggestedWords");
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.cpp
index 8172e70..bf38dff 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.cpp
@@ -20,6 +20,7 @@
 #include "defines.h"
 #include "suggest/core/dicnode/dic_node.h"
 #include "suggest/core/dicnode/dic_node_vector.h"
+#include "suggest/core/dictionary/binary_dictionary_bigrams_iterator.h"
 #include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_helper.h"
 #include "suggest/policyimpl/dictionary/structure/v2/patricia_trie_reading_utils.h"
 #include "suggest/policyimpl/dictionary/utils/probability_utils.h"
@@ -303,4 +304,63 @@
     return siblingPos;
 }
 
+const WordProperty PatriciaTriePolicy::getWordProperty(const int *const codePoints,
+        const int codePointCount) const {
+    const int ptNodePos = getTerminalPtNodePositionOfWord(codePoints, codePointCount,
+            false /* forceLowerCaseSearch */);
+    if (ptNodePos == NOT_A_DICT_POS) {
+        AKLOGE("getWordProperty was called for invalid word.");
+        return WordProperty();
+    }
+    const PtNodeParams ptNodeParams = mPtNodeReader.fetchNodeInfoInBufferFromPtNodePos(ptNodePos);
+    std::vector<int> codePointVector(ptNodeParams.getCodePoints(),
+            ptNodeParams.getCodePoints() + ptNodeParams.getCodePointCount());
+    // Fetch bigram information.
+    std::vector<WordProperty::BigramProperty> bigrams;
+    const int bigramListPos = getBigramsPositionOfPtNode(ptNodePos);
+    int bigramWord1CodePoints[MAX_WORD_LENGTH];
+    BinaryDictionaryBigramsIterator bigramsIt(getBigramsStructurePolicy(), bigramListPos);
+    while (bigramsIt.hasNext()) {
+        // Fetch the next bigram information and forward the iterator.
+        bigramsIt.next();
+        // Skip the entry if the entry has been deleted. This never happens for ver2 dicts.
+        if (bigramsIt.getBigramPos() != NOT_A_DICT_POS) {
+            int word1Probability = NOT_A_PROBABILITY;
+            int word1CodePointCount = getCodePointsAndProbabilityAndReturnCodePointCount(
+                    bigramsIt.getBigramPos(), MAX_WORD_LENGTH, bigramWord1CodePoints,
+                    &word1Probability);
+            std::vector<int> word1(bigramWord1CodePoints,
+                    bigramWord1CodePoints + word1CodePointCount);
+            bigrams.push_back(WordProperty::BigramProperty(&word1, bigramsIt.getProbability(),
+                    NOT_A_TIMESTAMP /* timestamp */, 0 /* level */, 0 /* count */));
+        }
+    }
+    // Fetch shortcut information.
+    std::vector<WordProperty::ShortcutProperty> shortcuts;
+    int shortcutPos = getShortcutPositionOfPtNode(ptNodePos);
+    if (shortcutPos != NOT_A_DICT_POS) {
+        int shortcutTargetCodePoints[MAX_WORD_LENGTH];
+        ShortcutListReadingUtils::getShortcutListSizeAndForwardPointer(mDictRoot, &shortcutPos);
+        bool hasNext = true;
+        while (hasNext) {
+            const ShortcutListReadingUtils::ShortcutFlags shortcutFlags =
+                    ShortcutListReadingUtils::getFlagsAndForwardPointer(mDictRoot, &shortcutPos);
+            hasNext = ShortcutListReadingUtils::hasNext(shortcutFlags);
+            const int shortcutTargetLength = ShortcutListReadingUtils::readShortcutTarget(
+                    mDictRoot, MAX_WORD_LENGTH, shortcutTargetCodePoints, &shortcutPos);
+            std::vector<int> shortcutTarget(shortcutTargetCodePoints,
+                    shortcutTargetCodePoints + shortcutTargetLength);
+            const int shortcutProbability =
+                    ShortcutListReadingUtils::getProbabilityFromFlags(shortcutFlags);
+            shortcuts.push_back(
+                    WordProperty::ShortcutProperty(&shortcutTarget, shortcutProbability));
+        }
+    }
+    return WordProperty(&codePointVector, ptNodeParams.isNotAWord(),
+            ptNodeParams.isBlacklisted(), ptNodeParams.hasBigrams(),
+            ptNodeParams.hasShortcutTargets(), ptNodeParams.getProbability(),
+            NOT_A_TIMESTAMP /* timestamp */, 0 /* level */, 0 /* count */,
+            &bigrams, &shortcuts);
+}
+
 } // namespace latinime
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h
index 1ce7f85..da4be87 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h
@@ -128,10 +128,7 @@
     }
 
     const WordProperty getWordProperty(const int *const codePoints,
-            const int codePointCount) const {
-        // getWordProperty is not supported.
-        return WordProperty();
-    }
+            const int codePointCount) const;
 
     int getNextWordAndNextToken(const int token, int *const outCodePoints) {
         // getNextWordAndNextToken is not supported.
diff --git a/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java b/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java
index 53839b6..8fe4735 100644
--- a/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java
+++ b/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java
@@ -49,7 +49,6 @@
                 list, null /* rawSuggestions */,
                 false /* typedWordValid */,
                 false /* willAutoCorrect */,
-                false /* isPunctuationSuggestions */,
                 false /* isObsoleteSuggestions */,
                 false /* isPrediction*/);
         assertEquals(NUMBER_OF_ADDED_SUGGESTIONS + 1, words.size());
diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java
index 141730d..28388b4 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java
@@ -39,6 +39,7 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map.Entry;
 import java.util.Random;
 import java.util.Set;
@@ -596,4 +597,39 @@
             Log.d(TAG, result);
         }
     }
+
+    public void testVer2DictGetWordProperty() {
+        final FormatOptions formatOptions = BinaryDictUtils.VERSION2_OPTIONS;
+        final ArrayList<String> words = sWords;
+        final HashMap<String, List<String>> shortcuts = sShortcuts;
+        final String dictName = "testGetWordProperty";
+        final String dictVersion = Long.toString(System.currentTimeMillis());
+        final FusionDictionary dict = new FusionDictionary(new PtNodeArray(),
+                BinaryDictUtils.makeDictionaryOptions(dictName, dictVersion, formatOptions));
+        addUnigrams(words.size(), dict, words, shortcuts);
+        addBigrams(dict, words, sEmptyBigrams);
+        final File file = BinaryDictUtils.getDictFile(dictName, dictVersion, formatOptions,
+                getContext().getCacheDir());
+        file.delete();
+        timeWritingDictToFile(file, dict, formatOptions);
+        final BinaryDictionary binaryDictionary = new BinaryDictionary(file.getAbsolutePath(),
+                0 /* offset */, file.length(), true /* useFullEditDistance */,
+                Locale.ENGLISH, dictName, false /* isUpdatable */);
+        for (final String word : words) {
+            final WordProperty wordProperty = binaryDictionary.getWordProperty(word);
+            assertEquals(word, wordProperty.mWord);
+            assertEquals(UNIGRAM_FREQ, wordProperty.getProbability());
+            if (shortcuts.containsKey(word)) {
+                assertEquals(shortcuts.get(word).size(), wordProperty.mShortcutTargets.size());
+                final List<String> shortcutList = shortcuts.get(word);
+                assertTrue(wordProperty.mHasShortcuts);
+                for (final WeightedString shortcutTarget : wordProperty.mShortcutTargets) {
+                    assertTrue(shortcutList.contains(shortcutTarget.mWord));
+                    assertEquals(UNIGRAM_FREQ, shortcutTarget.getProbability());
+                    shortcutList.remove(shortcutTarget.mWord);
+                }
+                assertTrue(shortcutList.isEmpty());
+            }
+        }
+    }
 }
diff --git a/tests/src/com/android/inputmethod/latin/settings/SpacingAndPunctuationsTests.java b/tests/src/com/android/inputmethod/latin/settings/SpacingAndPunctuationsTests.java
index 424e7ff..24af094 100644
--- a/tests/src/com/android/inputmethod/latin/settings/SpacingAndPunctuationsTests.java
+++ b/tests/src/com/android/inputmethod/latin/settings/SpacingAndPunctuationsTests.java
@@ -373,29 +373,53 @@
         assertTrue(SWISS_GERMAN.mUsesGermanRules);
     }
 
-    private static void testingStandardPunctuationSuggestions(final SpacingAndPunctuations sp) {
+    private static final String[] PUNCTUATION_LABELS_LTR = {
+        "!", "?", ",", ":", ";", "\"", "(", ")", "'", "-", "/", "@", "_"
+    };
+    private static final String[] PUNCTUATION_WORDS_LTR = PUNCTUATION_LABELS_LTR;
+    private static final String[] PUNCTUATION_WORDS_HEBREW = {
+        "!", "?", ",", ":", ";", "\"", ")", "(", "'", "-", "/", "@", "_"
+    };
+    // U+061F: "؟" ARABIC QUESTION MARK
+    // U+060C: "،" ARABIC COMMA
+    // U+061B: "؛" ARABIC SEMICOLON
+    private static final String[] PUNCTUATION_LABELS_ARABIC_PERSIAN = {
+        "!", "\u061F", "\u060C", ":", "\u061B", "\"", "(", ")", "'", "-", "/", "@", "_"
+    };
+    private static final String[] PUNCTUATION_WORDS_ARABIC_PERSIAN = {
+        "!", "\u061F", "\u060C", ":", "\u061B", "\"", ")", "(", "'", "-", "/", "@", "_"
+    };
+
+    private static void testingStandardPunctuationSuggestions(final SpacingAndPunctuations sp,
+            final String[] punctuationLabels, final String[] punctuationWords) {
         final SuggestedWords suggestedWords = sp.mSuggestPuncList;
         assertFalse("typedWordValid", suggestedWords.mTypedWordValid);
         assertFalse("willAutoCorrect", suggestedWords.mWillAutoCorrect);
-        assertTrue("isPunctuationSuggestions", suggestedWords.mIsPunctuationSuggestions);
+        assertTrue("isPunctuationSuggestions", suggestedWords.isPunctuationSuggestions());
         assertFalse("isObsoleteSuggestions", suggestedWords.mIsObsoleteSuggestions);
         assertFalse("isPrediction", suggestedWords.mIsPrediction);
-        final String[] punctuations = {
-            "!", "?", ",", ":", ";", "\"", "(", ")", "'", "-", "/", "@", "_"
-        };
-        assertEquals("size", punctuations.length, suggestedWords.size());
-        for (int index = 0; index < punctuations.length; index++) {
-            assertEquals("punctuation at " + index,
-                    punctuations[index], suggestedWords.getWord(index));
+        assertEquals("size", punctuationLabels.length, suggestedWords.size());
+        for (int index = 0; index < suggestedWords.size(); index++) {
+            assertEquals("punctuation label at " + index,
+                    punctuationLabels[index], suggestedWords.getLabel(index));
+            assertEquals("punctuation word at " + index,
+                    punctuationWords[index], suggestedWords.getWord(index));
         }
     }
+
+    // TODO: Add tests for tablet as well
     public void testPunctuationSuggestions() {
-        testingStandardPunctuationSuggestions(ENGLISH);
-        testingStandardPunctuationSuggestions(FRENCH);
-        testingStandardPunctuationSuggestions(GERMAN);
-        // TODO: Should fix these RTL languages
-        testingStandardPunctuationSuggestions(ARABIC);
-        testingStandardPunctuationSuggestions(PERSIAN);
-        testingStandardPunctuationSuggestions(HEBREW);
+        testingStandardPunctuationSuggestions(ENGLISH,
+                PUNCTUATION_LABELS_LTR, PUNCTUATION_WORDS_LTR);
+        testingStandardPunctuationSuggestions(FRENCH,
+                PUNCTUATION_LABELS_LTR, PUNCTUATION_WORDS_LTR);
+        testingStandardPunctuationSuggestions(GERMAN,
+                PUNCTUATION_LABELS_LTR, PUNCTUATION_WORDS_LTR);
+        testingStandardPunctuationSuggestions(ARABIC,
+                PUNCTUATION_LABELS_ARABIC_PERSIAN, PUNCTUATION_WORDS_ARABIC_PERSIAN);
+        testingStandardPunctuationSuggestions(PERSIAN,
+                PUNCTUATION_LABELS_ARABIC_PERSIAN, PUNCTUATION_WORDS_ARABIC_PERSIAN);
+        testingStandardPunctuationSuggestions(HEBREW,
+                PUNCTUATION_LABELS_LTR, PUNCTUATION_WORDS_HEBREW);
     }
 }
diff --git a/tools/make-keyboard-text/res/values-ar/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-ar/donottranslate-more-keys.xml
index 9b0a059..d997685 100644
--- a/tools/make-keyboard-text/res/values-ar/donottranslate-more-keys.xml
+++ b/tools/make-keyboard-text/res/values-ar/donottranslate-more-keys.xml
@@ -75,7 +75,7 @@
     <!-- U+066A: "٪" ARABIC PERCENT SIGN -->
     <string name="keylabel_for_symbols_percent">&#x066A;</string>
     <!-- U+00BF: "¿" INVERTED QUESTION MARK -->
-    <string name="more_keys_for_question">\?,&#x00BF;</string>
+    <string name="more_keys_for_question">?,&#x00BF;</string>
     <string name="more_keys_for_symbols_semicolon">;</string>
     <!-- U+2030: "‰" PER MILLE SIGN -->
     <string name="more_keys_for_symbols_percent">\\%,&#x2030;</string>
@@ -85,7 +85,7 @@
     <string name="keylabel_for_tablet_comma">"&#x060C;"</string>
     <string name="keyhintlabel_for_tablet_comma">"&#x061F;"</string>
     <string name="more_keys_for_tablet_comma">"!fixedColumnOrder!4,:,!,&#x061F;,&#x061B;,-,/,\",\'"</string>
-    <string name="more_keys_for_punctuation">"!fixedColumnOrder!8,\",\',#,-,:,!,&#x060C;,&#x061F;,\@,&amp;,\\%,+,&#x061B;,/,(|),)|("</string>
+    <string name="more_keys_for_punctuation">"!fixedColumnOrder!8,\",\',#,-,:,!,&#x060C;,&#x061F;,@,&amp;,\\%,+,&#x061B;,/,(|),)|("</string>
     <!-- U+266A: "♪" EIGHTH NOTE -->
     <string name="more_keys_for_bullet">&#x266A;</string>
     <!-- U+2605: "★" BLACK STAR
diff --git a/tools/make-keyboard-text/res/values-ca/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-ca/donottranslate-more-keys.xml
index f63e85b..8865a60 100644
--- a/tools/make-keyboard-text/res/values-ca/donottranslate-more-keys.xml
+++ b/tools/make-keyboard-text/res/values-ca/donottranslate-more-keys.xml
@@ -71,8 +71,8 @@
          U+0142: "ł" LATIN SMALL LETTER L WITH STROKE -->
     <string name="more_keys_for_l">l&#x00B7;l,&#x0142;</string>
     <!-- U+00B7: "·" MIDDLE DOT -->
-    <string name="more_keys_for_punctuation">"!fixedColumnOrder!9,;,/,(,),#,&#x00B7;,!,\\,,\?,&amp;,\\%,+,\",-,:,',\@"</string>
-    <string name="more_keys_for_tablet_punctuation">"!fixedColumnOrder!8,;,/,(,),#,&#x00B7;,',\\,,&amp;,\\%,+,\",-,:,\@"</string>
+    <string name="more_keys_for_punctuation">"!fixedColumnOrder!9,;,/,(,),#,&#x00B7;,!,\\,,?,&amp;,\\%,+,\",-,:,',@"</string>
+    <string name="more_keys_for_tablet_punctuation">"!fixedColumnOrder!8,;,/,(,),#,&#x00B7;,',\\,,&amp;,\\%,+,\",-,:,@"</string>
     <!-- U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA -->
     <string name="keylabel_for_spanish_row2_10">&#x00E7;</string>
 </resources>
diff --git a/tools/make-keyboard-text/res/values-es/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-es/donottranslate-more-keys.xml
index f4fe7f7..453d5c1 100644
--- a/tools/make-keyboard-text/res/values-es/donottranslate-more-keys.xml
+++ b/tools/make-keyboard-text/res/values-es/donottranslate-more-keys.xml
@@ -69,5 +69,5 @@
     <string name="more_keys_for_c">&#x00E7;,&#x0107;,&#x010D;</string>
     <!-- U+00A1: "¡" INVERTED EXCLAMATION MARK
          U+00BF: "¿" INVERTED QUESTION MARK -->
-    <string name="more_keys_for_punctuation">"!fixedColumnOrder!9,&#x00A1;,;,/,(,),#,!,\\,,\?,&#x00BF;,&amp;,\\%,+,\",-,:,',\@"</string>
+    <string name="more_keys_for_punctuation">"!fixedColumnOrder!9,&#x00A1;,;,/,(,),#,!,\\,,?,&#x00BF;,&amp;,\\%,+,\",-,:,',@"</string>
 </resources>
diff --git a/tools/make-keyboard-text/res/values-fa/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-fa/donottranslate-more-keys.xml
index 3f12784..6ea0433 100644
--- a/tools/make-keyboard-text/res/values-fa/donottranslate-more-keys.xml
+++ b/tools/make-keyboard-text/res/values-fa/donottranslate-more-keys.xml
@@ -75,7 +75,7 @@
     <!-- U+066A: "٪" ARABIC PERCENT SIGN -->
     <string name="keylabel_for_symbols_percent">&#x066A;</string>
     <!-- U+00BF: "¿" INVERTED QUESTION MARK -->
-    <string name="more_keys_for_question">\?,&#x00BF;</string>
+    <string name="more_keys_for_question">?,&#x00BF;</string>
     <string name="more_keys_for_symbols_semicolon">;</string>
     <!-- U+2030: "‰" PER MILLE SIGN -->
     <string name="more_keys_for_symbols_percent">\\%,&#x2030;</string>
@@ -92,7 +92,7 @@
     <!-- U+061F: "؟" ARABIC QUESTION MARK
          U+060C: "،" ARABIC COMMA
          U+061B: "؛" ARABIC SEMICOLON -->
-    <string name="more_keys_for_punctuation">"!fixedColumnOrder!8,\",\',#,-,:,!,&#x060C;,&#x061F;,\@,&amp;,\\%,+,&#x061B;,/,!text/keyspec_left_parenthesis,!text/keyspec_right_parenthesis"</string>
+    <string name="more_keys_for_punctuation">"!fixedColumnOrder!8,\",\',#,-,:,!,&#x060C;,&#x061F;,@,&amp;,\\%,+,&#x061B;,/,!text/keyspec_left_parenthesis,!text/keyspec_right_parenthesis"</string>
     <!-- U+266A: "♪" EIGHTH NOTE -->
     <string name="more_keys_for_bullet">&#x266A;</string>
     <!-- U+2605: "★" BLACK STAR
diff --git a/tools/make-keyboard-text/res/values-hi/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-hi/donottranslate-more-keys.xml
index b0d010f..de10a01 100644
--- a/tools/make-keyboard-text/res/values-hi/donottranslate-more-keys.xml
+++ b/tools/make-keyboard-text/res/values-hi/donottranslate-more-keys.xml
@@ -44,7 +44,7 @@
     <!-- U+0966: "०" DEVANAGARI DIGIT ZERO -->
     <string name="keylabel_for_symbols_0">&#x0966;</string>
     <!-- Label for "switch to symbols" key. -->
-    <string name="label_to_symbol_key">\?&#x0967;&#x0968;&#x0969;</string>
+    <string name="label_to_symbol_key">?&#x0967;&#x0968;&#x0969;</string>
     <!-- Label for "switch to symbols with microphone" key. This string shouldn't include the "mic"
          part because it'll be appended by the code. -->
     <string name="label_to_symbol_with_microphone_key">&#x0967;&#x0968;&#x0969;</string>
diff --git a/tools/make-keyboard-text/res/values-ne-rNP/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-ne-rNP/donottranslate-more-keys.xml
index 9205e53..e92a87e 100644
--- a/tools/make-keyboard-text/res/values-ne-rNP/donottranslate-more-keys.xml
+++ b/tools/make-keyboard-text/res/values-ne-rNP/donottranslate-more-keys.xml
@@ -44,7 +44,7 @@
     <!-- U+0966: "०" DEVANAGARI DIGIT ZERO -->
     <string name="keylabel_for_symbols_0">&#x0966;</string>
     <!-- Label for "switch to symbols" key. -->
-    <string name="label_to_symbol_key">\?&#x0967;&#x0968;&#x0969;</string>
+    <string name="label_to_symbol_key">?&#x0967;&#x0968;&#x0969;</string>
     <!-- Label for "switch to symbols with microphone" key. This string shouldn't include the "mic"
          part because it'll be appended by the code. -->
     <string name="label_to_symbol_with_microphone_key">&#x0967;&#x0968;&#x0969;</string>
diff --git a/tools/make-keyboard-text/res/values/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values/donottranslate-more-keys.xml
index 8d3d61c..9cdcb46 100644
--- a/tools/make-keyboard-text/res/values/donottranslate-more-keys.xml
+++ b/tools/make-keyboard-text/res/values/donottranslate-more-keys.xml
@@ -83,8 +83,8 @@
     <string name="more_keys_for_currency_dollar">&#x00A2;,&#x00A3;,&#x20AC;,&#x00A5;,&#x20B1;</string>
     <string name="keylabel_for_currency">$</string>
     <string name="more_keys_for_currency">$,&#x00A2;,&#x20AC;,&#x00A3;,&#x00A5;,&#x20B1;</string>
-    <string name="more_keys_for_punctuation">"!fixedColumnOrder!8,;,/,!text/keyspec_left_parenthesis,!text/keyspec_right_parenthesis,#,!,\\,,\?,&amp;,\\%,+,\",-,:,',\@"</string>
-    <string name="more_keys_for_tablet_punctuation">"!fixedColumnOrder!7,;,/,!text/keyspec_left_parenthesis,!text/keyspec_right_parenthesis,#,',\\,,&amp;,\\%,+,\",-,:,\@"</string>
+    <string name="more_keys_for_punctuation">"!fixedColumnOrder!8,;,/,!text/keyspec_left_parenthesis,!text/keyspec_right_parenthesis,#,!,\\,,?,&amp;,\\%,+,\",-,:,',@"</string>
+    <string name="more_keys_for_tablet_punctuation">"!fixedColumnOrder!7,;,/,!text/keyspec_left_parenthesis,!text/keyspec_right_parenthesis,#,',\\,,&amp;,\\%,+,\",-,:,@"</string>
     <!-- U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE -->
     <string name="keylabel_for_spanish_row2_10">&#x00F1;</string>
     <!-- U+2020: "†" DAGGER
@@ -115,7 +115,7 @@
     <string name="keylabel_for_symbols_9">9</string>
     <string name="keylabel_for_symbols_0">0</string>
     <!-- Label for "switch to symbols" key. -->
-    <string name="label_to_symbol_key">\?123</string>
+    <string name="label_to_symbol_key">?123</string>
     <!-- Label for "switch to symbols with microphone" key. This string shouldn't include the "mic"
          part because it'll be appended by the code. -->
     <string name="label_to_symbol_with_microphone_key">123</string>
@@ -189,7 +189,7 @@
     <string name="keylabel_for_tablet_period">.</string>
     <string name="keyhintlabel_for_tablet_period"></string>
     <string name="more_keys_for_tablet_period">!text/more_keys_for_tablet_punctuation</string>
-    <string name="keylabel_for_symbols_question">\?</string>
+    <string name="keylabel_for_symbols_question">?</string>
     <string name="keylabel_for_symbols_semicolon">;</string>
     <string name="keylabel_for_symbols_percent">%</string>
     <!-- U+00A1: "¡" INVERTED EXCLAMATION MARK -->
@@ -205,17 +205,15 @@
     <string name="keylabel_for_w">w</string>
     <string name="keylabel_for_y">y</string>
     <string name="keylabel_for_x">x</string>
-    <string name="more_keys_for_am_pm">!fixedColumnOrder!2,!hasLabels!,\@string/label_time_am,\@string/label_time_pm</string>
+    <string name="more_keys_for_am_pm">!fixedColumnOrder!2,!hasLabels!,!text/label_time_am,!text/label_time_pm</string>
     <string name="settings_as_more_key">!icon/settings_key|!code/key_settings</string>
     <string name="shortcut_as_more_key">!icon/shortcut_key|!code/key_shortcut</string>
-    <string name="action_next_as_more_key">!hasLabels!,\@string/label_next_key|!code/key_action_next</string>
-    <string name="action_previous_as_more_key">!hasLabels!,\@string/label_previous_key|!code/key_action_previous</string>
+    <string name="action_next_as_more_key">!hasLabels!,!text/label_next_key|!code/key_action_next</string>
+    <string name="action_previous_as_more_key">!hasLabels!,!text/label_previous_key|!code/key_action_previous</string>
     <!-- Label for "switch to more symbol" modifier key ("= \ <"). Must be short to fit on key! -->
     <string name="label_to_more_symbol_key">= \\\\ &lt;</string>
     <!-- Label for "switch to more symbol" modifier key on tablets.  Must be short to fit on key! -->
     <string name="label_to_more_symbol_for_tablet_key">~ [ &lt;</string>
-    <!-- Label for "Tab" key.  Must be short to fit on key! -->
-    <string name="label_tab_key">Tab</string>
     <!-- Label for "switch to phone numeric" key.  Must be short to fit on key! -->
     <string name="label_to_phone_numeric_key">123</string>
     <!-- Label for "switch to phone symbols" key.  Must be short to fit on key! -->
diff --git a/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/MoreKeysResources.java b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/MoreKeysResources.java
index 872ef19..e9d6c86 100644
--- a/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/MoreKeysResources.java
+++ b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/MoreKeysResources.java
@@ -29,6 +29,7 @@
 import java.util.HashMap;
 import java.util.Locale;
 import java.util.jar.JarFile;
+import java.util.regex.Pattern;
 
 public class MoreKeysResources {
     private static final String TEXT_RESOURCE_NAME = "donottranslate-more-keys.xml";
@@ -287,23 +288,7 @@
                 sb.append(String.format("\\u%04X", (int)c));
             }
         }
-        return replaceIncompatibleEscape(sb.toString());
-    }
-
-    private static String replaceIncompatibleEscape(final String text) {
-        String t = text;
-        t = replaceAll(t, "\\?", "?");
-        t = replaceAll(t, "\\@", "@");
-        t = replaceAll(t, "@string/", "!text/");
-        return t;
-    }
-
-    private static String replaceAll(final String text, final String target, final String replace) {
-        String t = text;
-        while (t.indexOf(target) >= 0) {
-            t = t.replace(target, replace);
-        }
-        return t;
+        return sb.toString();
     }
 
     private static void close(final Closeable stream) {