Merge "Import translations. DO NOT MERGE"
diff --git a/dictionaries/en_GB_wordlist.combined.gz b/dictionaries/en_GB_wordlist.combined.gz
index 4f008ed..50647b8 100644
--- a/dictionaries/en_GB_wordlist.combined.gz
+++ b/dictionaries/en_GB_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/en_US_wordlist.combined.gz b/dictionaries/en_US_wordlist.combined.gz
index 5595c75..19f9ab4 100644
--- a/dictionaries/en_US_wordlist.combined.gz
+++ b/dictionaries/en_US_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/en_wordlist.combined.gz b/dictionaries/en_wordlist.combined.gz
index 69c39d5..874a5de 100644
--- a/dictionaries/en_wordlist.combined.gz
+++ b/dictionaries/en_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/fr_wordlist.combined.gz b/dictionaries/fr_wordlist.combined.gz
index 1a18320..49dfd79 100644
--- a/dictionaries/fr_wordlist.combined.gz
+++ b/dictionaries/fr_wordlist.combined.gz
Binary files differ
diff --git a/java/res/raw/main_en.dict b/java/res/raw/main_en.dict
index bef6b10..09b6992 100644
--- a/java/res/raw/main_en.dict
+++ b/java/res/raw/main_en.dict
Binary files differ
diff --git a/java/res/raw/main_fr.dict b/java/res/raw/main_fr.dict
index 18f5298..0e5a713 100644
--- a/java/res/raw/main_fr.dict
+++ b/java/res/raw/main_fr.dict
Binary files differ
diff --git a/java/res/values-hy/donottranslate.xml b/java/res/values-hy/donottranslate.xml
index 4a6d188..7b0c566 100644
--- a/java/res/values-hy/donottranslate.xml
+++ b/java/res/values-hy/donottranslate.xml
@@ -26,4 +26,7 @@
     <!-- Symbols that separate words. Adding armenian period and comma. -->
     <!-- Don't remove the enclosing double quotes, they protect whitespace (not just U+0020) -->
     <string name="symbols_word_separators">"&#x0009;&#x0020;\n"()[]{}*&amp;&lt;&gt;+=|.,;:!?/_\"&#x0589;&#x055D;</string>
+    <!-- The sentence separator code point, for capitalization -->
+    <!-- U+0589: "։" ARMENIAN FULL STOP   ; 589h = 1417d -->
+    <integer name="sentence_separator">1417</integer>
 </resources>
diff --git a/java/res/values/donottranslate.xml b/java/res/values/donottranslate.xml
index 42e692d..4733aa2 100644
--- a/java/res/values/donottranslate.xml
+++ b/java/res/values/donottranslate.xml
@@ -31,6 +31,9 @@
     <string name="symbols_word_separators">"&#x0009;&#x0020;\n"()[]{}*&amp;&lt;&gt;+=|.,;:!?/_\"</string>
     <!-- Word connectors -->
     <string name="symbols_word_connectors">\'-</string>
+    <!-- The sentence separator code point, for capitalization -->
+    <!-- U+002E: "." FULL STOP   ; 2Eh = 46d -->
+    <integer name="sentence_separator">46</integer>
     <!-- Whether this language uses spaces between words -->
     <bool name="current_language_has_spaces">true</bool>
 
diff --git a/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java b/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java
index 85ae500..9779c68 100644
--- a/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java
+++ b/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java
@@ -74,6 +74,7 @@
         ViewPager.OnPageChangeListener, View.OnClickListener,
         ScrollKeyboardView.OnKeyClickListener {
     private static final String TAG = EmojiPalettesView.class.getSimpleName();
+    private static final boolean DEBUG_PAGER = false;
     private final int mKeyBackgroundId;
     private final int mEmojiFunctionalKeyBackgroundId;
     private final KeyboardLayoutSet mLayoutSet;
@@ -83,6 +84,7 @@
 
     private TabHost mTabHost;
     private ViewPager mEmojiPager;
+    private int mCurrentPagerPosition = 0;
     private EmojiCategoryPageIndicatorView mEmojiCategoryPageIndicatorView;
 
     private KeyboardActionListener mKeyboardActionListener = KeyboardActionListener.EMPTY_LISTENER;
@@ -459,6 +461,7 @@
         mEmojiPager.setAdapter(mEmojiPalettesAdapter);
         mEmojiPager.setOnPageChangeListener(this);
         mEmojiPager.setOffscreenPageLimit(0);
+        mEmojiPager.setPersistentDrawingCache(ViewPager.PERSISTENT_NO_CACHE);
         final Resources res = getResources();
         final EmojiLayoutParams emojiLp = new EmojiLayoutParams(res);
         emojiLp.setPagerProperties(mEmojiPager);
@@ -505,6 +508,7 @@
         setCurrentCategoryId(newPos.first /* categoryId */, false /* force */);
         mEmojiCategory.setCurrentCategoryPageId(newPos.second /* categoryPageId */);
         updateEmojiCategoryPageIdView();
+        mCurrentPagerPosition = position;
     }
 
     @Override
@@ -565,6 +569,22 @@
         // TODO:
     }
 
+    public void startEmojiPalettes() {
+        if (DEBUG_PAGER) {
+            Log.d(TAG, "allocate emoji palettes memory " + mCurrentPagerPosition);
+        }
+        mEmojiPager.setAdapter(mEmojiPalettesAdapter);
+        mEmojiPager.setCurrentItem(mCurrentPagerPosition);
+    }
+
+    public void stopEmojiPalettes() {
+        if (DEBUG_PAGER) {
+            Log.d(TAG, "deallocate emoji palettes memory");
+        }
+        mEmojiPalettesAdapter.flushPendingRecentKeys();
+        mEmojiPager.setAdapter(null);
+    }
+
     public void setKeyboardActionListener(final KeyboardActionListener listener) {
         mKeyboardActionListener = listener;
         mDeleteKeyOnTouchListener.setKeyboardActionListener(mKeyboardActionListener);
@@ -607,7 +627,7 @@
     private static class EmojiPalettesAdapter extends PagerAdapter {
         private final ScrollKeyboardView.OnKeyClickListener mListener;
         private final DynamicGridKeyboard mRecentsKeyboard;
-        private final SparseArray<ScrollKeyboardView> mActiveKeyboardView =
+        private final SparseArray<ScrollKeyboardView> mActiveKeyboardViews =
                 CollectionUtils.newSparseArray();
         private final EmojiCategory mEmojiCategory;
         private int mActivePosition = 0;
@@ -623,7 +643,7 @@
         public void flushPendingRecentKeys() {
             mRecentsKeyboard.flushPendingRecentKeys();
             final KeyboardView recentKeyboardView =
-                    mActiveKeyboardView.get(mEmojiCategory.getRecentTabId());
+                    mActiveKeyboardViews.get(mEmojiCategory.getRecentTabId());
             if (recentKeyboardView != null) {
                 recentKeyboardView.invalidateAllKeys();
             }
@@ -636,7 +656,7 @@
             }
             mRecentsKeyboard.addKeyFirst(key);
             final KeyboardView recentKeyboardView =
-                    mActiveKeyboardView.get(mEmojiCategory.getRecentTabId());
+                    mActiveKeyboardViews.get(mEmojiCategory.getRecentTabId());
             if (recentKeyboardView != null) {
                 recentKeyboardView.invalidateAllKeys();
             }
@@ -652,7 +672,7 @@
             if (mActivePosition == position) {
                 return;
             }
-            final ScrollKeyboardView oldKeyboardView = mActiveKeyboardView.get(mActivePosition);
+            final ScrollKeyboardView oldKeyboardView = mActiveKeyboardViews.get(mActivePosition);
             if (oldKeyboardView != null) {
                 oldKeyboardView.releaseCurrentKey();
                 oldKeyboardView.deallocateMemory();
@@ -662,11 +682,14 @@
 
         @Override
         public Object instantiateItem(final ViewGroup container, final int position) {
-            final ScrollKeyboardView oldKeyboardView = mActiveKeyboardView.get(position);
+            if (DEBUG_PAGER) {
+                Log.d(TAG, "instantiate item: " + position);
+            }
+            final ScrollKeyboardView oldKeyboardView = mActiveKeyboardViews.get(position);
             if (oldKeyboardView != null) {
                 oldKeyboardView.deallocateMemory();
                 // This may be redundant but wanted to be safer..
-                mActiveKeyboardView.remove(position);
+                mActiveKeyboardViews.remove(position);
             }
             final Keyboard keyboard =
                     mEmojiCategory.getKeyboardFromPagePosition(position);
@@ -681,7 +704,7 @@
                     R.id.emoji_keyboard_scroller);
             keyboardView.setScrollView(scrollView);
             container.addView(view);
-            mActiveKeyboardView.put(position, keyboardView);
+            mActiveKeyboardViews.put(position, keyboardView);
             return view;
         }
 
@@ -693,12 +716,19 @@
         @Override
         public void destroyItem(final ViewGroup container, final int position,
                 final Object object) {
-            final ScrollKeyboardView keyboardView = mActiveKeyboardView.get(position);
+            if (DEBUG_PAGER) {
+                Log.d(TAG, "destroy item: " + position + ", " + object.getClass().getSimpleName());
+            }
+            final ScrollKeyboardView keyboardView = mActiveKeyboardViews.get(position);
             if (keyboardView != null) {
                 keyboardView.deallocateMemory();
-                mActiveKeyboardView.remove(position);
+                mActiveKeyboardViews.remove(position);
             }
-            container.removeView(keyboardView);
+            if (object instanceof View) {
+                container.removeView((View)object);
+            } else {
+                Log.w(TAG, "Warning!!! Emoji palette may be leaking. " + object);
+            }
         }
     }
 
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index 4fc1082..9760983 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -260,12 +260,14 @@
     private void setMainKeyboardFrame() {
         mMainKeyboardFrame.setVisibility(View.VISIBLE);
         mEmojiPalettesView.setVisibility(View.GONE);
+        mEmojiPalettesView.stopEmojiPalettes();
     }
 
     // Implements {@link KeyboardState.SwitchActions}.
     @Override
     public void setEmojiKeyboard() {
         mMainKeyboardFrame.setVisibility(View.GONE);
+        mEmojiPalettesView.startEmojiPalettes();
         mEmojiPalettesView.setVisibility(View.VISIBLE);
     }
 
@@ -336,6 +338,16 @@
         return mKeyboardView;
     }
 
+    public void deallocateMemory() {
+        if (mKeyboardView != null) {
+            mKeyboardView.cancelAllOngoingEvents();
+            mKeyboardView.deallocateMemory();
+        }
+        if (mEmojiPalettesView != null) {
+            mEmojiPalettesView.stopEmojiPalettes();
+        }
+    }
+
     public View onCreateInputView(final boolean isHardwareAcceleratedDrawingEnabled) {
         if (mKeyboardView != null) {
             mKeyboardView.closing();
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index aeb9e67..5578713 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -243,6 +243,8 @@
     }
 
     private void freeOffscreenBuffer() {
+        mOffscreenCanvas.setBitmap(null);
+        mOffscreenCanvas.setMatrix(null);
         if (mOffscreenBuffer != null) {
             mOffscreenBuffer.recycle();
             mOffscreenBuffer = null;
diff --git a/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java b/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java
index 08302a7..09766ac 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java
@@ -42,7 +42,6 @@
     private final Object mLock = new Object();
 
     private final SharedPreferences mPrefs;
-    private final int mLeftPadding;
     private final int mHorizontalStep;
     private final int mVerticalStep;
     private final int mColumnsNum;
@@ -58,7 +57,6 @@
         super(templateKeyboard);
         final Key key0 = getTemplateKey(TEMPLATE_KEY_CODE_0);
         final Key key1 = getTemplateKey(TEMPLATE_KEY_CODE_1);
-        mLeftPadding = key0.getX();
         mHorizontalStep = Math.abs(key1.getX() - key0.getX());
         mVerticalStep = key0.getHeight() + mVerticalGap;
         mColumnsNum = mBaseWidth / mHorizontalStep;
@@ -122,9 +120,11 @@
             }
             int index = 0;
             for (final GridKey gridKey : mGridKeys) {
-                final int keyX = getKeyX(index);
-                final int keyY = getKeyY(index);
-                gridKey.updateCorrdinates(keyX, keyY);
+                final int keyX0 = getKeyX0(index);
+                final int keyY0 = getKeyY0(index);
+                final int keyX1 = getKeyX1(index);
+                final int keyY1 = getKeyY1(index);
+                gridKey.updateCorrdinates(keyX0, keyY0, keyX1, keyY1);
                 index++;
             }
         }
@@ -172,14 +172,24 @@
         }
     }
 
-    private int getKeyX(final int index) {
+    private int getKeyX0(final int index) {
         final int column = index % mColumnsNum;
-        return column * mHorizontalStep + mLeftPadding;
+        return column * mHorizontalStep;
     }
 
-    private int getKeyY(final int index) {
+    private int getKeyX1(final int index) {
+        final int column = index % mColumnsNum + 1;
+        return column * mHorizontalStep;
+    }
+
+    private int getKeyY0(final int index) {
         final int row = index / mColumnsNum;
-        return row * mVerticalStep + mTopPadding;
+        return row * mVerticalStep;
+    }
+
+    private int getKeyY1(final int index) {
+        final int row = index / mColumnsNum + 1;
+        return row * mVerticalStep;
     }
 
     @Override
@@ -207,10 +217,10 @@
             super(originalKey);
         }
 
-        public void updateCorrdinates(final int x, final int y) {
-            mCurrentX = x;
-            mCurrentY = y;
-            getHitBox().set(x, y, x + getWidth(), y + getHeight());
+        public void updateCorrdinates(final int x0, final int y0, final int x1, final int y1) {
+            mCurrentX = x0;
+            mCurrentY = y0;
+            getHitBox().set(x0, y0, x1, y1);
         }
 
         @Override
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
index d219e81..bcb80b4 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
@@ -2895,33 +2895,69 @@
 
     /* Language sv: Swedish */
     private static final String[] LANGUAGE_sv = {
-        /* 0 */ null,
+        // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
+        // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
+        // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+        // U+0105: "ą" LATIN SMALL LETTER A WITH OGONEK
+        // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
+        /* 0 */ "\u00E1,\u00E0,\u00E2,\u0105,\u00E3",
         // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
         // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
         // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
         // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
         // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
         /* 1 */ "\u00E9,\u00E8,\u00EA,\u00EB,\u0119",
-        /* 2 */ null,
-        // U+0153: "œ" LATIN SMALL LIGATURE OE
-        // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
-        // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+        // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
+        // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
+        // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
+        // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
+        /* 2 */ "\u00ED,\u00EC,\u00EE,\u00EF",
         // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
+        // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+        // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
         // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
         // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
-        /* 3 */ "\u0153,\u00F4,\u00F2,\u00F3,\u00F5,\u014D",
+        /* 3 */ "\u00F3,\u00F2,\u00F4,\u00F5,\u014D",
         // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
-        // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
-        // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
         // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
+        // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
+        // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
         // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
-        /* 4 */ "\u00FC,\u00FB,\u00F9,\u00FA,\u016B",
-        // U+00DF: "ß" LATIN SMALL LETTER SHARP S
+        /* 4 */ "\u00FC,\u00FA,\u00F9,\u00FB,\u016B",
         // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
         // U+0161: "š" LATIN SMALL LETTER S WITH CARON
-        /* 5 */ "\u00DF,\u015B,\u0161",
-        /* 6~ */
-        null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+        // U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA
+        // U+00DF: "ß" LATIN SMALL LETTER SHARP S
+        /* 5 */ "\u015B,\u0161,\u015F,\u00DF",
+        // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
+        // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
+        // U+0148: "ň" LATIN SMALL LETTER N WITH CARON
+        /* 6 */ "\u0144,\u00F1,\u0148",
+        // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
+        // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
+        // U+010D: "č" LATIN SMALL LETTER C WITH CARON
+        /* 7 */ "\u00E7,\u0107,\u010D",
+        // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
+        // U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
+        // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
+        /* 8 */ "\u00FD,\u00FF,\u00FC",
+        // U+00F0: "ð" LATIN SMALL LETTER ETH
+        // U+010F: "ď" LATIN SMALL LETTER D WITH CARON
+        /* 9 */ "\u00F0,\u010F",
+        // U+0159: "ř" LATIN SMALL LETTER R WITH CARON
+        /* 10 */ "\u0159",
+        // U+0165: "ť" LATIN SMALL LETTER T WITH CARON
+        // U+00FE: "þ" LATIN SMALL LETTER THORN
+        /* 11 */ "\u0165,\u00FE",
+        // U+017A: "ź" LATIN SMALL LETTER Z WITH ACUTE
+        // U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
+        // U+017C: "ż" LATIN SMALL LETTER Z WITH DOT ABOVE
+        /* 12 */ "\u017A,\u017E,\u017C",
+        /* 13 */ null,
+        // U+0142: "ł" LATIN SMALL LETTER L WITH STROKE
+        /* 14 */ "\u0142",
+        /* 15~ */
+        null, null, null, null, null,
         /* ~19 */
         // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
         /* 20 */ "\u00E5",
@@ -2930,7 +2966,8 @@
         // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
         /* 22 */ "\u00E4",
         // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
-        /* 23 */ "\u00F8",
+        // U+0153: "œ" LATIN SMALL LIGATURE OE
+        /* 23 */ "\u00F8,\u0153",
         // U+00E6: "æ" LATIN SMALL LETTER AE
         /* 24 */ "\u00E6",
         /* 25~ */
diff --git a/java/src/com/android/inputmethod/keyboard/internal/ScrollKeyboardView.java b/java/src/com/android/inputmethod/keyboard/internal/ScrollKeyboardView.java
index b8ee976..9cf68d4 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/ScrollKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/ScrollKeyboardView.java
@@ -30,8 +30,9 @@
 import com.android.inputmethod.latin.R;
 
 /**
- * This is an extended {@link KeyboardView} class that hosts a scroll keyboard.
+ * This is an extended {@link KeyboardView} class that hosts a vertical scroll keyboard.
  * Multi-touch unsupported. No {@link PointerTracker}s. No gesture support.
+ * TODO: Vertical scroll capability should be removed from this class because it's no longer used.
  */
 // TODO: Implement key popup preview.
 public final class ScrollKeyboardView extends KeyboardView implements
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 0e93590..ccdbd0d 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -926,10 +926,11 @@
                 suggest.setAutoCorrectionThreshold(currentSettingsValues.mAutoCorrectionThreshold);
             }
 
-            if (canReachInputConnection) {
-                // If we can't reach the input connection, we don't want to call loadKeyboard yet.
-                // It will be done in #retryResetCaches.
-                switcher.loadKeyboard(editorInfo, currentSettingsValues);
+            switcher.loadKeyboard(editorInfo, currentSettingsValues);
+            if (!canReachInputConnection) {
+                // If we can't reach the input connection, we will call loadKeyboard again later,
+                // so we need to save its state now. The call will be done in #retryResetCaches.
+                switcher.saveKeyboardState();
             }
         } else if (restarting) {
             // TODO: Come up with a more comprehensive way to reset the keyboard layout when
@@ -1049,11 +1050,7 @@
     private void onFinishInputViewInternal(final boolean finishingInput) {
         super.onFinishInputView(finishingInput);
         mKeyboardSwitcher.onFinishInputView();
-        final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
-        if (mainKeyboardView != null) {
-            mainKeyboardView.cancelAllOngoingEvents();
-            mainKeyboardView.deallocateMemory();
-        }
+        mKeyboardSwitcher.deallocateMemory();
         // Remove pending messages related to update suggestions
         mHandler.cancelUpdateSuggestionStrip();
         // Should do the following in onFinishInputInternal but until JB MR2 it's not called :(
@@ -1411,14 +1408,15 @@
     // Called from the KeyboardSwitcher which needs to know auto caps state to display
     // the right layout.
     public int getCurrentAutoCapsState() {
-        if (!mSettings.getCurrent().mAutoCap) return Constants.TextUtils.CAP_MODE_OFF;
+        final SettingsValues currentSettingsValues = mSettings.getCurrent();
+        if (!currentSettingsValues.mAutoCap) return Constants.TextUtils.CAP_MODE_OFF;
 
         final EditorInfo ei = getCurrentInputEditorInfo();
         if (ei == null) return Constants.TextUtils.CAP_MODE_OFF;
         final int inputType = ei.inputType;
         // Warning: this depends on mSpaceState, which may not be the most current value. If
         // mSpaceState gets updated later, whoever called this may need to be told about it.
-        return mConnection.getCursorCapsMode(inputType, mSubtypeSwitcher.getCurrentSubtypeLocale(),
+        return mConnection.getCursorCapsMode(inputType, currentSettingsValues,
                 SPACE_STATE_PHANTOM == mSpaceState);
     }
 
@@ -1459,9 +1457,9 @@
     }
 
     private boolean maybeDoubleSpacePeriod() {
-        final SettingsValues settingsValues = mSettings.getCurrent();
-        if (!settingsValues.mCorrectionEnabled) return false;
-        if (!settingsValues.mUseDoubleSpacePeriod) return false;
+        final SettingsValues currentSettingsValues = mSettings.getCurrent();
+        if (!currentSettingsValues.mCorrectionEnabled) return false;
+        if (!currentSettingsValues.mUseDoubleSpacePeriod) return false;
         if (!mHandler.isAcceptingDoubleSpacePeriod()) return false;
         // We only do this when we see two spaces and an accepted code point before the cursor.
         // The code point may be a surrogate pair but the two spaces may not, so we need 4 chars.
@@ -1480,7 +1478,9 @@
         if (canBeFollowedByDoubleSpacePeriod(firstCodePoint)) {
             mHandler.cancelDoubleSpacePeriodTimer();
             mConnection.deleteSurroundingText(2, 0);
-            final String textToInsert = ". ";
+            final String textToInsert = new String(
+                    new int[] { currentSettingsValues.mSentenceSeparator, Constants.CODE_SPACE },
+                    0, 2);
             mConnection.commitText(textToInsert, 1);
             if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
                 ResearchLogger.latinIME_maybeDoubleSpacePeriod(textToInsert,
diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java
index 8580a6e..e43cab5 100644
--- a/java/src/com/android/inputmethod/latin/RichInputConnection.java
+++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java
@@ -245,11 +245,11 @@
      * American English, it's just the most common set of rules for English).
      *
      * @param inputType a mask of the caps modes to test for.
-     * @param locale what language should be considered.
+     * @param settingsValues the values of the settings to use for locale and separators.
      * @param hasSpaceBefore if we should consider there should be a space after the string.
      * @return the caps modes that should be on as a set of bits
      */
-    public int getCursorCapsMode(final int inputType, final Locale locale,
+    public int getCursorCapsMode(final int inputType, final SettingsValues settingsValues,
             final boolean hasSpaceBefore) {
         mIC = mParent.getCurrentInputConnection();
         if (null == mIC) return Constants.TextUtils.CAP_MODE_OFF;
@@ -277,8 +277,8 @@
         }
         // This never calls InputConnection#getCapsMode - in fact, it's a static method that
         // never blocks or initiates IPC.
-        return CapsModeUtils.getCapsMode(mCommittedTextBeforeComposingText, inputType, locale,
-                hasSpaceBefore);
+        return CapsModeUtils.getCapsMode(mCommittedTextBeforeComposingText, inputType,
+                settingsValues, hasSpaceBefore);
     }
 
     public int getCodePointBeforeCursor() {
diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
index 2abcdc7..f331c78 100644
--- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
@@ -24,6 +24,7 @@
 
 import com.android.inputmethod.annotations.UsedForTesting;
 import com.android.inputmethod.keyboard.internal.KeySpecParser;
+import com.android.inputmethod.latin.Constants;
 import com.android.inputmethod.latin.Dictionary;
 import com.android.inputmethod.latin.InputAttributes;
 import com.android.inputmethod.latin.R;
@@ -57,6 +58,7 @@
     public final int[] mWordConnectors;
     public final SuggestedWords mSuggestPuncList;
     public final String mWordSeparators;
+    public final int mSentenceSeparator;
     public final CharSequence mHintToSaveText;
     public final boolean mCurrentLanguageHasSpaces;
 
@@ -120,6 +122,7 @@
                 R.string.suggested_punctuations));
         mSuggestPuncList = createSuggestPuncList(suggestPuncsSpec);
         mWordSeparators = res.getString(R.string.symbols_word_separators);
+        mSentenceSeparator = res.getInteger(R.integer.sentence_separator);
         mHintToSaveText = res.getText(R.string.hint_add_to_dictionary);
         mCurrentLanguageHasSpaces = res.getBoolean(R.bool.current_language_has_spaces);
 
@@ -187,6 +190,7 @@
         Arrays.sort(mSymbolsFollowedBySpace);
         mWordConnectors = new int[] { '\'', '-' };
         Arrays.sort(mWordConnectors);
+        mSentenceSeparator = Constants.CODE_PERIOD;
         final String[] suggestPuncsSpec = new String[] { "!", "?", ",", ":", ";" };
         mSuggestPuncList = createSuggestPuncList(suggestPuncsSpec);
         mWordSeparators = "&\t \n()[]{}*&<>+=|.,;:!?/_\"";
diff --git a/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java b/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java
index 60b24d5..3d4404a 100644
--- a/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java
@@ -21,6 +21,7 @@
 
 import com.android.inputmethod.latin.Constants;
 import com.android.inputmethod.latin.WordComposer;
+import com.android.inputmethod.latin.settings.SettingsValues;
 
 import java.util.Locale;
 
@@ -60,11 +61,6 @@
                 || WordComposer.CAPS_MODE_AUTO_SHIFT_LOCKED == mode;
     }
 
-    private static boolean isPeriod(final int codePoint) {
-        // TODO: make this a resource.
-        return codePoint == Constants.CODE_PERIOD || codePoint == Constants.CODE_ARMENIAN_PERIOD;
-    }
-
     /**
      * Determine what caps mode should be in effect at the current offset in
      * the text. Only the mode bits set in <var>reqModes</var> will be
@@ -78,7 +74,7 @@
      * @param reqModes The modes to be checked: may be any combination of
      * {@link TextUtils#CAP_MODE_CHARACTERS}, {@link TextUtils#CAP_MODE_WORDS}, and
      * {@link TextUtils#CAP_MODE_SENTENCES}.
-     * @param locale The locale to consider for capitalization rules
+     * @param settingsValues The current settings values.
      * @param hasSpaceBefore Whether we should consider there is a space inserted at the end of cs
      *
      * @return Returns the actual capitalization modes that can be in effect
@@ -86,8 +82,8 @@
      * {@link TextUtils#CAP_MODE_CHARACTERS}, {@link TextUtils#CAP_MODE_WORDS}, and
      * {@link TextUtils#CAP_MODE_SENTENCES}.
      */
-    public static int getCapsMode(final CharSequence cs, final int reqModes, final Locale locale,
-            final boolean hasSpaceBefore) {
+    public static int getCapsMode(final CharSequence cs, final int reqModes,
+            final SettingsValues settingsValues, final boolean hasSpaceBefore) {
         // Quick description of what we want to do:
         // CAP_MODE_CHARACTERS is always on.
         // CAP_MODE_WORDS is on if there is some whitespace before the cursor.
@@ -172,7 +168,7 @@
         // mark as the exact thing quoted and handling the surrounding punctuation independently,
         // e.g. <<Did he say, "let's go home"?>>
         // Hence, specifically for English, we treat this special case here.
-        if (Locale.ENGLISH.getLanguage().equals(locale.getLanguage())) {
+        if (Locale.ENGLISH.getLanguage().equals(settingsValues.mLocale.getLanguage())) {
             for (; j > 0; j--) {
                 // Here we look to go over any closing punctuation. This is because in dominant
                 // variants of English, the final period is placed within double quotes and maybe
@@ -195,7 +191,7 @@
         if (c == Constants.CODE_QUESTION_MARK || c == Constants.CODE_EXCLAMATION_MARK) {
             return (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_SENTENCES) & reqModes;
         }
-        if (!isPeriod(c) || j <= 0) {
+        if (settingsValues.mSentenceSeparator != c || j <= 0) {
             return (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS) & reqModes;
         }
 
@@ -245,7 +241,7 @@
             case WORD:
                 if (Character.isLetter(c)) {
                     state = WORD;
-                } else if (isPeriod(c)) {
+                } else if (settingsValues.mSentenceSeparator == c) {
                     state = PERIOD;
                 } else {
                     return caps;
@@ -261,7 +257,7 @@
             case LETTER:
                 if (Character.isLetter(c)) {
                     state = LETTER;
-                } else if (isPeriod(c)) {
+                } else if (settingsValues.mSentenceSeparator == c) {
                     state = PERIOD;
                 } else {
                     return noCaps;
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.cpp
index 3d07c9d..a8ea69f 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.cpp
@@ -360,11 +360,11 @@
     } else if (strncmp(query, MAX_UNIGRAM_COUNT_QUERY, maxResultLength) == 0) {
         snprintf(outResult, maxResultLength, "%d",
                 mHeaderPolicy.isDecayingDict() ? ForgettingCurveUtils::MAX_UNIGRAM_COUNT :
-                        DynamicPatriciaTrieWritingHelper::MAX_DICTIONARY_SIZE);
+                        static_cast<int>(DynamicPatriciaTrieWritingHelper::MAX_DICTIONARY_SIZE));
     } else if (strncmp(query, MAX_BIGRAM_COUNT_QUERY, maxResultLength) == 0) {
         snprintf(outResult, maxResultLength, "%d",
                 mHeaderPolicy.isDecayingDict() ? ForgettingCurveUtils::MAX_BIGRAM_COUNT :
-                        DynamicPatriciaTrieWritingHelper::MAX_DICTIONARY_SIZE);
+                        static_cast<int>(DynamicPatriciaTrieWritingHelper::MAX_DICTIONARY_SIZE));
     } else if (strncmp(query, SET_NEEDS_TO_DECAY_FOR_TESTING_QUERY, maxResultLength) == 0) {
         mNeedsToDecayForTesting = true;
     }
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.cpp b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.cpp
index 067c8ec..052558b 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.cpp
@@ -240,7 +240,8 @@
             int parentOffsetFieldPos = nodeReader->getHeadPos()
                     + DynamicPatriciaTrieWritingUtils::NODE_FLAG_FIELD_SIZE;
             if (!DynamicPatriciaTrieWritingUtils::writeParentPosOffsetAndAdvancePosition(
-                    mBuffer, movedPos, nodeReader->getHeadPos(), &parentOffsetFieldPos)) {
+                    mBuffer, bigramLinkedNodePos, nodeReader->getHeadPos(),
+                    &parentOffsetFieldPos)) {
                 // Parent offset cannot be written because of a bug or a broken dictionary; thus,
                 // we give up to update dictionary.
                 return false;
diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
index cecdd2f..cd5384e 100644
--- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
+++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
@@ -18,6 +18,7 @@
 
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.LargeTest;
+import android.util.Pair;
 
 import com.android.inputmethod.latin.makedict.CodePointUtils;
 import com.android.inputmethod.latin.makedict.FormatSpec;
@@ -124,11 +125,16 @@
         binaryDictionary.addBigramWords("a", "c", DUMMY_PROBABILITY);
         assertTrue(binaryDictionary.isValidBigram("a", "c"));
 
+        // Add bigrams of not valid unigrams.
+        binaryDictionary.addBigramWords("x", "y", Dictionary.NOT_A_PROBABILITY);
+        assertFalse(binaryDictionary.isValidBigram("x", "y"));
+        binaryDictionary.addBigramWords("x", "y", DUMMY_PROBABILITY);
+        assertFalse(binaryDictionary.isValidBigram("x", "y"));
+
         binaryDictionary.close();
         dictFile.delete();
     }
 
-    // TODO: Add large tests.
     public void testDecayingProbability() {
         File dictFile = null;
         try {
@@ -233,4 +239,70 @@
         assertTrue(Integer.parseInt(binaryDictionary.getPropertyForTests(
                 BinaryDictionary.UNIGRAM_COUNT_QUERY)) <= maxUnigramCount);
     }
+
+    public void testAddManyBigramsToDecayingDict() {
+        final int unigramCount = 5000;
+        final int bigramCount = 30000;
+        final int bigramTypedCount = 100000;
+        final int codePointSetSize = 50;
+        final long seed = System.currentTimeMillis();
+        final Random random = new Random(seed);
+
+        File dictFile = null;
+        try {
+            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary");
+        } catch (IOException e) {
+            fail("IOException while writing an initial dictionary : " + e);
+        }
+        BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
+                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
+                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+
+        final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random);
+        final ArrayList<String> words = new ArrayList<String>();
+        final ArrayList<Pair<String, String>> bigrams = new ArrayList<Pair<String, String>>();
+
+        for (int i = 0; i < unigramCount; ++i) {
+            final String word = CodePointUtils.generateWord(random, codePointSet);
+            words.add(word);
+        }
+        for (int i = 0; i < bigramCount; ++i) {
+            final int word0Index = random.nextInt(words.size());
+            int word1Index = random.nextInt(words.size() - 1);
+            if (word1Index >= word0Index) {
+                word1Index += 1;
+            }
+            final String word0 = words.get(word0Index);
+            final String word1 = words.get(word1Index);
+            final Pair<String, String> bigram = new Pair<String, String>(word0, word1);
+            bigrams.add(bigram);
+        }
+
+        final int maxBigramCount = Integer.parseInt(
+                binaryDictionary.getPropertyForTests(BinaryDictionary.MAX_BIGRAM_COUNT_QUERY));
+        for (int i = 0; i < bigramTypedCount; ++i) {
+            final Pair<String, String> bigram = bigrams.get(random.nextInt(bigrams.size()));
+            binaryDictionary.addUnigramWord(bigram.first, DUMMY_PROBABILITY);
+            binaryDictionary.addUnigramWord(bigram.second, DUMMY_PROBABILITY);
+            binaryDictionary.addBigramWords(bigram.first, bigram.second, DUMMY_PROBABILITY);
+
+            if (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) {
+                final int bigramCountBeforeGC =
+                        Integer.parseInt(binaryDictionary.getPropertyForTests(
+                                BinaryDictionary.BIGRAM_COUNT_QUERY));
+                while (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) {
+                    binaryDictionary.flushWithGC();
+                }
+                final int bigramCountAfterGC =
+                        Integer.parseInt(binaryDictionary.getPropertyForTests(
+                                BinaryDictionary.BIGRAM_COUNT_QUERY));
+                assertTrue(bigramCountBeforeGC > bigramCountAfterGC);
+            }
+        }
+
+        assertTrue(Integer.parseInt(binaryDictionary.getPropertyForTests(
+                BinaryDictionary.BIGRAM_COUNT_QUERY)) > 0);
+        assertTrue(Integer.parseInt(binaryDictionary.getPropertyForTests(
+                BinaryDictionary.BIGRAM_COUNT_QUERY)) <= maxBigramCount);
+    }
 }
diff --git a/tests/src/com/android/inputmethod/latin/InputTestsBase.java b/tests/src/com/android/inputmethod/latin/InputTestsBase.java
index 234bb1b..b9b52a6 100644
--- a/tests/src/com/android/inputmethod/latin/InputTestsBase.java
+++ b/tests/src/com/android/inputmethod/latin/InputTestsBase.java
@@ -238,12 +238,16 @@
     }
 
     protected void changeLanguage(final String locale) {
+        changeLanguageWithoutWait(locale);
+        waitForDictionaryToBeLoaded();
+    }
+
+    protected void changeLanguageWithoutWait(final String locale) {
         mEditText.mCurrentLocale = LocaleUtils.constructLocaleFromString(locale);
         SubtypeSwitcher.getInstance().forceLocale(mEditText.mCurrentLocale);
         mLatinIME.loadKeyboard();
         runMessages();
         mKeyboard = mLatinIME.mKeyboardSwitcher.getKeyboard();
-        waitForDictionaryToBeLoaded();
     }
 
     protected void changeKeyboardLocaleAndDictLocale(final String keyboardLocale,
diff --git a/tests/src/com/android/inputmethod/latin/LatinImeStressTests.java b/tests/src/com/android/inputmethod/latin/LatinImeStressTests.java
new file mode 100644
index 0000000..5e98cdf
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/LatinImeStressTests.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2013 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 android.test.suitebuilder.annotation.LargeTest;
+
+import com.android.inputmethod.latin.makedict.CodePointUtils;
+
+import java.util.Random;
+
+@LargeTest
+public class LatinImeStressTests extends InputTestsBase {
+    public void testSwitchLanguagesAndInputLatinRandomCodePoints() {
+        final String[] locales = {"en_US", "de", "el", "es", "fi", "it", "nl", "pt", "ru"};
+        final int switchCount = 50;
+        final int maxWordCountToTypeInEachIteration = 20;
+        final long seed = System.currentTimeMillis();
+        final Random random = new Random(seed);
+        final int codePointSetSize = 30;
+        final int[] codePointSet = CodePointUtils.LATIN_ALPHABETS_LOWER;
+        for (int i = 0; i < switchCount; ++i) {
+            changeLanguageWithoutWait(locales[random.nextInt(locales.length)]);
+            final int wordCount = random.nextInt(maxWordCountToTypeInEachIteration);
+            for (int j = 0; j < wordCount; ++j) {
+                final String word = CodePointUtils.generateWord(random, codePointSet);
+                type(word);
+            }
+        }
+    }
+    public void testSwitchLanguagesAndInputRandamCodePoints() {
+        final String[] locales = {"en_US", "de", "el", "es", "fi", "it", "nl", "pt", "ru"};
+        final int switchCount = 50;
+        final int maxWordCountToTypeInEachIteration = 20;
+        final long seed = System.currentTimeMillis();
+        final Random random = new Random(seed);
+        final int codePointSetSize = 30;
+        final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random);
+        for (int i = 0; i < switchCount; ++i) {
+            changeLanguageWithoutWait(locales[random.nextInt(locales.length)]);
+            final int wordCount = random.nextInt(maxWordCountToTypeInEachIteration);
+            for (int j = 0; j < wordCount; ++j) {
+                final String word = CodePointUtils.generateWord(random, codePointSet);
+                type(word);
+            }
+        }
+    }
+}
diff --git a/tests/src/com/android/inputmethod/latin/makedict/CodePointUtils.java b/tests/src/com/android/inputmethod/latin/makedict/CodePointUtils.java
index 36b958a..a270ee7 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/CodePointUtils.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/CodePointUtils.java
@@ -24,6 +24,42 @@
         // This utility class is not publicly instantiable.
     }
 
+    public static final int[] LATIN_ALPHABETS_LOWER = {
+        'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
+        'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
+        0x00E0 /* LATIN SMALL LETTER A WITH GRAVE */,
+        0x00E1 /* LATIN SMALL LETTER A WITH ACUTE */,
+        0x00E2 /* LATIN SMALL LETTER A WITH CIRCUMFLEX */,
+        0x00E3 /* LATIN SMALL LETTER A WITH TILDE */,
+        0x00E4 /* LATIN SMALL LETTER A WITH DIAERESIS */,
+        0x00E5 /* LATIN SMALL LETTER A WITH RING ABOVE */,
+        0x00E6 /* LATIN SMALL LETTER AE */,
+        0x00E7 /* LATIN SMALL LETTER C WITH CEDILLA */,
+        0x00E8 /* LATIN SMALL LETTER E WITH GRAVE */,
+        0x00E9 /* LATIN SMALL LETTER E WITH ACUTE */,
+        0x00EA /* LATIN SMALL LETTER E WITH CIRCUMFLEX */,
+        0x00EB /* LATIN SMALL LETTER E WITH DIAERESIS */,
+        0x00EC /* LATIN SMALL LETTER I WITH GRAVE */,
+        0x00ED /* LATIN SMALL LETTER I WITH ACUTE */,
+        0x00EE /* LATIN SMALL LETTER I WITH CIRCUMFLEX */,
+        0x00EF /* LATIN SMALL LETTER I WITH DIAERESIS */,
+        0x00F0 /* LATIN SMALL LETTER ETH */,
+        0x00F1 /* LATIN SMALL LETTER N WITH TILDE */,
+        0x00F2 /* LATIN SMALL LETTER O WITH GRAVE */,
+        0x00F3 /* LATIN SMALL LETTER O WITH ACUTE */,
+        0x00F4 /* LATIN SMALL LETTER O WITH CIRCUMFLEX */,
+        0x00F5 /* LATIN SMALL LETTER O WITH TILDE */,
+        0x00F6 /* LATIN SMALL LETTER O WITH DIAERESIS */,
+        0x00F7 /* LATIN SMALL LETTER O WITH STROKE */,
+        0x00F9 /* LATIN SMALL LETTER U WITH GRAVE */,
+        0x00FA /* LATIN SMALL LETTER U WITH ACUTE */,
+        0x00FB /* LATIN SMALL LETTER U WITH CIRCUMFLEX */,
+        0x00FC /* LATIN SMALL LETTER U WITH DIAERESIS */,
+        0x00FD /* LATIN SMALL LETTER Y WITH ACUTE */,
+        0x00FE /* LATIN SMALL LETTER THORN */,
+        0x00FF /* LATIN SMALL LETTER Y WITH DIAERESIS */
+    };
+
     public static int[] generateCodePointSet(final int codePointSetSize, final Random random) {
         final int[] codePointSet = new int[codePointSetSize];
         for (int i = codePointSet.length - 1; i >= 0; ) {
diff --git a/tests/src/com/android/inputmethod/latin/utils/CapsModeUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/CapsModeUtilsTests.java
index cf3bdd6..1fd5c98 100644
--- a/tests/src/com/android/inputmethod/latin/utils/CapsModeUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/utils/CapsModeUtilsTests.java
@@ -16,6 +16,8 @@
 
 package com.android.inputmethod.latin.utils;
 
+import com.android.inputmethod.latin.settings.SettingsValues;
+
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.text.TextUtils;
@@ -25,64 +27,64 @@
 @SmallTest
 public class CapsModeUtilsTests extends AndroidTestCase {
     private static void onePathForCaps(final CharSequence cs, final int expectedResult,
-            final int mask, final Locale l, final boolean hasSpaceBefore) {
+            final int mask, final SettingsValues sv, final boolean hasSpaceBefore) {
         int oneTimeResult = expectedResult & mask;
         assertEquals("After >" + cs + "<", oneTimeResult,
-                CapsModeUtils.getCapsMode(cs, mask, l, hasSpaceBefore));
+                CapsModeUtils.getCapsMode(cs, mask, sv, hasSpaceBefore));
     }
 
     private static void allPathsForCaps(final CharSequence cs, final int expectedResult,
-            final Locale l, final boolean hasSpaceBefore) {
+            final SettingsValues sv, final boolean hasSpaceBefore) {
         final int c = TextUtils.CAP_MODE_CHARACTERS;
         final int w = TextUtils.CAP_MODE_WORDS;
         final int s = TextUtils.CAP_MODE_SENTENCES;
-        onePathForCaps(cs, expectedResult, c | w | s, l, hasSpaceBefore);
-        onePathForCaps(cs, expectedResult, w | s, l, hasSpaceBefore);
-        onePathForCaps(cs, expectedResult, c | s, l, hasSpaceBefore);
-        onePathForCaps(cs, expectedResult, c | w, l, hasSpaceBefore);
-        onePathForCaps(cs, expectedResult, c, l, hasSpaceBefore);
-        onePathForCaps(cs, expectedResult, w, l, hasSpaceBefore);
-        onePathForCaps(cs, expectedResult, s, l, hasSpaceBefore);
+        onePathForCaps(cs, expectedResult, c | w | s, sv, hasSpaceBefore);
+        onePathForCaps(cs, expectedResult, w | s, sv, hasSpaceBefore);
+        onePathForCaps(cs, expectedResult, c | s, sv, hasSpaceBefore);
+        onePathForCaps(cs, expectedResult, c | w, sv, hasSpaceBefore);
+        onePathForCaps(cs, expectedResult, c, sv, hasSpaceBefore);
+        onePathForCaps(cs, expectedResult, w, sv, hasSpaceBefore);
+        onePathForCaps(cs, expectedResult, s, sv, hasSpaceBefore);
     }
 
     public void testGetCapsMode() {
         final int c = TextUtils.CAP_MODE_CHARACTERS;
         final int w = TextUtils.CAP_MODE_WORDS;
         final int s = TextUtils.CAP_MODE_SENTENCES;
-        Locale l = Locale.ENGLISH;
-        allPathsForCaps("", c | w | s, l, false);
-        allPathsForCaps("Word", c, l, false);
-        allPathsForCaps("Word.", c, l, false);
-        allPathsForCaps("Word ", c | w, l, false);
-        allPathsForCaps("Word. ", c | w | s, l, false);
-        allPathsForCaps("Word..", c, l, false);
-        allPathsForCaps("Word.. ", c | w | s, l, false);
-        allPathsForCaps("Word... ", c | w | s, l, false);
-        allPathsForCaps("Word ... ", c | w | s, l, false);
-        allPathsForCaps("Word . ", c | w, l, false);
-        allPathsForCaps("In the U.S ", c | w, l, false);
-        allPathsForCaps("In the U.S. ", c | w, l, false);
-        allPathsForCaps("Some stuff (e.g. ", c | w, l, false);
-        allPathsForCaps("In the U.S.. ", c | w | s, l, false);
-        allPathsForCaps("\"Word.\" ", c | w | s, l, false);
-        allPathsForCaps("\"Word\". ", c | w | s, l, false);
-        allPathsForCaps("\"Word\" ", c | w, l, false);
+        SettingsValues sv = SettingsValues.makeDummySettingsValuesForTest(Locale.ENGLISH);
+        allPathsForCaps("", c | w | s, sv, false);
+        allPathsForCaps("Word", c, sv, false);
+        allPathsForCaps("Word.", c, sv, false);
+        allPathsForCaps("Word ", c | w, sv, false);
+        allPathsForCaps("Word. ", c | w | s, sv, false);
+        allPathsForCaps("Word..", c, sv, false);
+        allPathsForCaps("Word.. ", c | w | s, sv, false);
+        allPathsForCaps("Word... ", c | w | s, sv, false);
+        allPathsForCaps("Word ... ", c | w | s, sv, false);
+        allPathsForCaps("Word . ", c | w, sv, false);
+        allPathsForCaps("In the U.S ", c | w, sv, false);
+        allPathsForCaps("In the U.S. ", c | w, sv, false);
+        allPathsForCaps("Some stuff (e.g. ", c | w, sv, false);
+        allPathsForCaps("In the U.S.. ", c | w | s, sv, false);
+        allPathsForCaps("\"Word.\" ", c | w | s, sv, false);
+        allPathsForCaps("\"Word\". ", c | w | s, sv, false);
+        allPathsForCaps("\"Word\" ", c | w, sv, false);
 
         // Test for phantom space
-        allPathsForCaps("Word", c | w, l, true);
-        allPathsForCaps("Word.", c | w | s, l, true);
+        allPathsForCaps("Word", c | w, sv, true);
+        allPathsForCaps("Word.", c | w | s, sv, true);
 
         // Tests after some whitespace
-        allPathsForCaps("Word\n", c | w | s, l, false);
-        allPathsForCaps("Word\n", c | w | s, l, true);
-        allPathsForCaps("Word\n ", c | w | s, l, true);
-        allPathsForCaps("Word.\n", c | w | s, l, false);
-        allPathsForCaps("Word.\n", c | w | s, l, true);
-        allPathsForCaps("Word.\n ", c | w | s, l, true);
+        allPathsForCaps("Word\n", c | w | s, sv, false);
+        allPathsForCaps("Word\n", c | w | s, sv, true);
+        allPathsForCaps("Word\n ", c | w | s, sv, true);
+        allPathsForCaps("Word.\n", c | w | s, sv, false);
+        allPathsForCaps("Word.\n", c | w | s, sv, true);
+        allPathsForCaps("Word.\n ", c | w | s, sv, true);
 
-        l = Locale.FRENCH;
-        allPathsForCaps("\"Word.\" ", c | w, l, false);
-        allPathsForCaps("\"Word\". ", c | w | s, l, false);
-        allPathsForCaps("\"Word\" ", c | w, l, false);
+        sv = SettingsValues.makeDummySettingsValuesForTest(Locale.FRENCH);
+        allPathsForCaps("\"Word.\" ", c | w, sv, false);
+        allPathsForCaps("\"Word\". ", c | w | s, sv, false);
+        allPathsForCaps("\"Word\" ", c | w, sv, false);
     }
 }
diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtils.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtils.java
index bd06e9f..e571bc2 100644
--- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtils.java
+++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtils.java
@@ -183,7 +183,11 @@
                             filename + " does not seem to be a dictionary file"));
                 } else if (CombinedInputOutput.isCombinedDictionary(
                         decodedSpec.mFile.getAbsolutePath())){
-                    if (report) System.out.println("Format : Combined format");
+                    if (report) {
+                        System.out.println("Format : Combined format");
+                        System.out.println("Packaging : " + decodedSpec.describeChain());
+                        System.out.println("Uncompressed size : " + decodedSpec.mFile.length());
+                    }
                     return CombinedInputOutput.readDictionaryCombined(
                             new BufferedInputStream(new FileInputStream(decodedSpec.mFile)));
                 } else {
diff --git a/tools/make-keyboard-text/res/values-sv/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-sv/donottranslate-more-keys.xml
index a36a13e..2472364 100644
--- a/tools/make-keyboard-text/res/values-sv/donottranslate-more-keys.xml
+++ b/tools/make-keyboard-text/res/values-sv/donottranslate-more-keys.xml
@@ -18,39 +18,77 @@
 */
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
+         U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
+         U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+         U+0105: "ą" LATIN SMALL LETTER A WITH OGONEK
+         U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE -->
+    <string name="more_keys_for_a">&#x00E1;,&#x00E0;,&#x00E2;,&#x0105;,&#x00E3;</string>
+    <!-- U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
+         U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
+         U+010D: "č" LATIN SMALL LETTER C WITH CARON -->
+    <string name="more_keys_for_c">&#x00E7;,&#x0107;,&#x010D;</string>
+    <!-- U+00F0: "ð" LATIN SMALL LETTER ETH
+         U+010F: "ď" LATIN SMALL LETTER D WITH CARON -->
+    <string name="more_keys_for_d">&#x00F0;,&#x010F;</string>
     <!-- U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
          U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
          U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
          U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
          U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK -->
     <string name="more_keys_for_e">&#x00E9;,&#x00E8;,&#x00EA;,&#x00EB;,&#x0119;</string>
-    <!-- U+0153: "œ" LATIN SMALL LIGATURE OE
-         U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
+    <!-- U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
+         U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
+         U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
+         U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS -->
+    <string name="more_keys_for_i">&#x00ED;,&#x00EC;,&#x00EE;,&#x00EF;</string>
+    <!-- U+0142: "ł" LATIN SMALL LETTER L WITH STROKE -->
+    <string name="more_keys_for_l">&#x0142;</string>
+    <!-- U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
+         U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
+         U+0148: "ň" LATIN SMALL LETTER N WITH CARON -->
+    <string name="more_keys_for_n">&#x0144;,&#x00F1;,&#x0148;</string>
+    <!-- U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
          U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
-         U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
+         U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
          U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
          U+014D: "ō" LATIN SMALL LETTER O WITH MACRON -->
-    <string name="more_keys_for_o">&#x0153;,&#x00F4;,&#x00F2;,&#x00F3;,&#x00F5;,&#x014D;</string>
+    <string name="more_keys_for_o">&#x00F3;,&#x00F2;,&#x00F4;,&#x00F5;,&#x014D;</string>
+    <!-- U+0159: "ř" LATIN SMALL LETTER R WITH CARON -->
+    <string name="more_keys_for_r">&#x0159;</string>
+    <!-- U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
+         U+0161: "š" LATIN SMALL LETTER S WITH CARON
+         U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA
+         U+00DF: "ß" LATIN SMALL LETTER SHARP S -->
+    <string name="more_keys_for_s">&#x015B;,&#x0161;,&#x015F;,&#x00DF;</string>
+    <!-- U+0165: "ť" LATIN SMALL LETTER T WITH CARON
+         U+00FE: "þ" LATIN SMALL LETTER THORN -->
+    <string name="more_keys_for_t">&#x0165;,&#x00FE;</string>
     <!-- U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
-         U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
-         U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
          U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
+         U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
+         U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
          U+016B: "ū" LATIN SMALL LETTER U WITH MACRON -->
-    <string name="more_keys_for_u">&#x00FC;,&#x00FB;,&#x00F9;,&#x00FA;,&#x016B;</string>
-    <!-- U+00DF: "ß" LATIN SMALL LETTER SHARP S
-         U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
-         U+0161: "š" LATIN SMALL LETTER S WITH CARON -->
-    <string name="more_keys_for_s">&#x00DF;,&#x015B;,&#x0161;</string>
+    <string name="more_keys_for_u">&#x00FC;,&#x00FA;,&#x00F9;,&#x00FB;,&#x016B;</string>
+    <!-- U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
+         U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
+         U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS -->
+    <string name="more_keys_for_y">&#x00FD;,&#x00FF;,&#x00FC;</string>
+    <!-- U+017A: "ź" LATIN SMALL LETTER Z WITH ACUTE
+         U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
+         U+017C: "ż" LATIN SMALL LETTER Z WITH DOT ABOVE -->
+    <string name="more_keys_for_z">&#x017A;,&#x017E;,&#x017C;</string>
     <!-- U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE -->
     <string name="keylabel_for_nordic_row1_11">&#x00E5;</string>
-    <!-- U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS -->
-    <string name="keylabel_for_nordic_row2_10">&#x00F6;</string>
     <!-- U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS -->
     <string name="keylabel_for_nordic_row2_11">&#x00E4;</string>
-    <!-- U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE -->
-    <string name="more_keys_for_nordic_row2_10">&#x00F8;</string>
     <!-- U+00E6: "æ" LATIN SMALL LETTER AE -->
     <string name="more_keys_for_nordic_row2_11">&#x00E6;</string>
+    <!-- U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS -->
+    <string name="keylabel_for_nordic_row2_10">&#x00F6;</string>
+    <!-- U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
+         U+0153: "œ" LATIN SMALL LIGATURE OE -->
+    <string name="more_keys_for_nordic_row2_10">&#x00F8;,&#x0153;</string>
     <string name="single_angle_quotes">!text/single_raqm_laqm</string>
     <string name="double_angle_quotes">!text/double_raqm_laqm</string>
 </resources>