diff --git a/src/libs/utils/stringutils.cpp b/src/libs/utils/stringutils.cpp
index 04df87fb5f2e804ce0bad0e506d5d94d814132d8..b69bfe6f971cfe508df31a8d871f62e891a58ade 100644
--- a/src/libs/utils/stringutils.cpp
+++ b/src/libs/utils/stringutils.cpp
@@ -238,4 +238,12 @@ QTCREATOR_UTILS_EXPORT QString expandMacros(const QString &str, AbstractMacroExp
     return ret;
 }
 
+QTCREATOR_UTILS_EXPORT QString stripAccelerator(const QString &text)
+{
+    QString res = text;
+    for (int index = res.indexOf('&'); index != -1; index = res.indexOf('&', index + 1))
+        res.remove(index, 1);
+    return res;
+}
+
 } // namespace Utils
diff --git a/src/libs/utils/stringutils.h b/src/libs/utils/stringutils.h
index 20f6063710c4c5952c65d8f91fc58d81c4986cac..8ff42bac6a449a2d313d6d222889daab3d0f7b0a 100644
--- a/src/libs/utils/stringutils.h
+++ b/src/libs/utils/stringutils.h
@@ -50,6 +50,9 @@ QTCREATOR_UTILS_EXPORT QString commonPath(const QStringList &files);
 // If path is not sub of home path, or when running on Windows, returns the input
 QTCREATOR_UTILS_EXPORT QString withTildeHomePath(const QString &path);
 
+// Removes first unescaped ampersand in text
+QTCREATOR_UTILS_EXPORT QString stripAccelerator(const QString &text);
+
 class QTCREATOR_UTILS_EXPORT AbstractMacroExpander
 {
 public:
diff --git a/src/plugins/coreplugin/actionmanager/command.cpp b/src/plugins/coreplugin/actionmanager/command.cpp
index 186ac4649f1fecad38449a4cecedc9d40bc8aa2d..45d72ecead6c87b237da204c182e24ee8b14acda 100644
--- a/src/plugins/coreplugin/actionmanager/command.cpp
+++ b/src/plugins/coreplugin/actionmanager/command.cpp
@@ -30,6 +30,7 @@
 #include <coreplugin/id.h>
 
 #include <utils/hostosinfo.h>
+#include <utils/stringutils.h>
 
 #include <QAction>
 #include <QRegExp>
@@ -263,9 +264,8 @@ QString Action::description() const
 {
     if (!m_defaultText.isEmpty())
         return m_defaultText;
-    if (action()) {
-        QString text = action()->text();
-        text.remove(QRegExp(QLatin1String("&(?!&)")));
+    if (QAction *act = action()) {
+        const QString text = Utils::stripAccelerator(act->text());
         if (!text.isEmpty())
             return text;
     }
diff --git a/src/plugins/coreplugin/dialogs/ioptionspage.cpp b/src/plugins/coreplugin/dialogs/ioptionspage.cpp
index 04a7b3131bef3ce49f32be66049c09672442bb77..16ac6e91844e24bf48d0fa28a0640eb432f81fa3 100644
--- a/src/plugins/coreplugin/dialogs/ioptionspage.cpp
+++ b/src/plugins/coreplugin/dialogs/ioptionspage.cpp
@@ -28,6 +28,8 @@
 
 #include "ioptionspage.h"
 
+#include <utils/stringutils.h>
+
 #include <QCheckBox>
 #include <QGroupBox>
 #include <QIcon>
@@ -165,18 +167,14 @@ bool Core::IOptionsPage::matches(const QString &searchKeyWord) const
             return false;
         // find common subwidgets
         foreach (const QLabel *label, widget->findChildren<QLabel *>())
-            m_keywords << label->text();
+            m_keywords << Utils::stripAccelerator(label->text());
         foreach (const QCheckBox *checkbox, widget->findChildren<QCheckBox *>())
-            m_keywords << checkbox->text();
+            m_keywords << Utils::stripAccelerator(checkbox->text());
         foreach (const QPushButton *pushButton, widget->findChildren<QPushButton *>())
-            m_keywords << pushButton->text();
+            m_keywords << Utils::stripAccelerator(pushButton->text());
         foreach (const QGroupBox *groupBox, widget->findChildren<QGroupBox *>())
-            m_keywords << groupBox->title();
+            m_keywords << Utils::stripAccelerator(groupBox->title());
 
-        // clean up accelerators
-        QMutableStringListIterator it(m_keywords);
-        while (it.hasNext())
-            it.next().remove(QLatin1Char('&'));
         m_keywordsInitialized = true;
     }
     foreach (const QString &keyword, m_keywords)
diff --git a/src/plugins/coreplugin/dialogs/readonlyfilesdialog.cpp b/src/plugins/coreplugin/dialogs/readonlyfilesdialog.cpp
index 88eaa2cba62417e20268c1bd012d986fcff8845f..5f7ecedd2311a92b23399bccbe87490fd59dc44b 100644
--- a/src/plugins/coreplugin/dialogs/readonlyfilesdialog.cpp
+++ b/src/plugins/coreplugin/dialogs/readonlyfilesdialog.cpp
@@ -35,6 +35,7 @@
 
 #include <utils/fileutils.h>
 #include <utils/hostosinfo.h>
+#include <utils/stringutils.h>
 
 #include <QDir>
 #include <QFileInfo>
@@ -211,7 +212,7 @@ void ReadOnlyFilesDialogPrivate::promptFailWarning(const QStringList &files, Rea
         switch (type) {
         case ReadOnlyFilesDialog::RO_OpenVCS: {
             if (IVersionControl *vc = versionControls[file]) {
-                const QString openText = vc->vcsOpenText().remove(QLatin1Char('&'));
+                const QString openText = Utils::stripAccelerator(vc->vcsOpenText());
                 title = tr("Failed to %1 File").arg(openText);
                 message = tr("%1 file %2 from version control system %3 failed.")
                         .arg(openText)
@@ -411,9 +412,9 @@ void ReadOnlyFilesDialogPrivate::initDialog(const QStringList &fileNames)
                 && versionControlForFile->openSupportMode(fileName) != IVersionControl::NoOpen;
         if (fileManagedByVCS) {
             const QString vcsOpenTextForFile =
-                    versionControlForFile->vcsOpenText().remove(QLatin1Char('&'));
+                    Utils::stripAccelerator(versionControlForFile->vcsOpenText());
             const QString vcsMakeWritableTextforFile =
-                    versionControlForFile->vcsMakeWritableText().remove(QLatin1Char('&'));
+                    Utils::stripAccelerator(versionControlForFile->vcsMakeWritableText());
             if (!useVCS) {
                 vcsOpenTextForAll = vcsOpenTextForFile;
                 vcsMakeWritableTextForAll = vcsMakeWritableTextforFile;
diff --git a/src/plugins/coreplugin/locator/commandlocator.cpp b/src/plugins/coreplugin/locator/commandlocator.cpp
index bb38336656cdfb583725c65a02c538c11e2b9af9..2768dca3a3b9bd5c88d05a9c1f96148c333daa92 100644
--- a/src/plugins/coreplugin/locator/commandlocator.cpp
+++ b/src/plugins/coreplugin/locator/commandlocator.cpp
@@ -28,6 +28,7 @@
 #include <coreplugin/actionmanager/command.h>
 
 #include <utils/qtcassert.h>
+#include <utils/stringutils.h>
 
 #include <QAction>
 
@@ -66,7 +67,6 @@ QList<LocatorFilterEntry> CommandLocator::matchesFor(QFutureInterface<LocatorFil
     QList<LocatorFilterEntry> betterEntries;
     // Get active, enabled actions matching text, store in list.
     // Reference via index in extraInfo.
-    const QChar ampersand = QLatin1Char('&');
     const Qt::CaseSensitivity entryCaseSensitivity = caseSensitivity(entry);
     const int count = d->commands.size();
     for (int i = 0; i < count; i++) {
@@ -77,8 +77,7 @@ QList<LocatorFilterEntry> CommandLocator::matchesFor(QFutureInterface<LocatorFil
 
         QAction *action = d->commands.at(i)->action();
         if (action && action->isEnabled()) {
-            QString text = action->text();
-            text.remove(ampersand);
+            const QString text = Utils::stripAccelerator(action->text());
             const int index = text.indexOf(entry, 0, entryCaseSensitivity);
             if (index >= 0) {
                 LocatorFilterEntry filterEntry(this, text, QVariant(i));
diff --git a/src/plugins/designer/settingspage.cpp b/src/plugins/designer/settingspage.cpp
index 0730e5fec59610eb77c65d3cac418286c2a17896..137f9a66aa6ccfc76b47aa75d89abc8bde7a4c82 100644
--- a/src/plugins/designer/settingspage.cpp
+++ b/src/plugins/designer/settingspage.cpp
@@ -28,6 +28,8 @@
 
 #include <coreplugin/icontext.h>
 
+#include <utils/stringutils.h>
+
 #include <QDesignerOptionsPageInterface>
 #include <QCoreApplication>
 
@@ -116,7 +118,7 @@ bool SettingsPageProvider::matches(const QString &searchKeyWord) const
     if (m_keywords.isEmpty()) {
         m_keywords.reserve(itemCount);
         for (size_t i = 0; i < itemCount; ++i)
-            m_keywords << QCoreApplication::translate(uitext[i].context, uitext[i].value).remove(QLatin1Char('&'));
+            m_keywords << Utils::stripAccelerator(QCoreApplication::translate(uitext[i].context, uitext[i].value));
     }
     foreach (const QString &key, m_keywords) {
         if (key.contains(searchKeyWord, Qt::CaseInsensitive))
diff --git a/tests/auto/utils/stringutils/tst_stringutils.cpp b/tests/auto/utils/stringutils/tst_stringutils.cpp
index d9d47e18451462c164e3b79dc833e528761eac00..96a950bb4364b3203226dc8b9a5b2833c2c1bb00 100644
--- a/tests/auto/utils/stringutils/tst_stringutils.cpp
+++ b/tests/auto/utils/stringutils/tst_stringutils.cpp
@@ -80,6 +80,8 @@ private slots:
     void testWithTildeHomePath();
     void testMacroExpander_data();
     void testMacroExpander();
+    void testStripAccelerator();
+    void testStripAccelerator_data();
 
 private:
     TestMacroExpander mx;
@@ -176,6 +178,30 @@ void tst_StringUtils::testMacroExpander()
     QCOMPARE(in, out);
 }
 
+void tst_StringUtils::testStripAccelerator()
+{
+    QFETCH(QString, expected);
+
+    QCOMPARE(Utils::stripAccelerator(QTest::currentDataTag()), expected);
+}
+
+void tst_StringUtils::testStripAccelerator_data()
+{
+    QTest::addColumn<QString>("expected");
+
+    QTest::newRow("Test") << "Test";
+    QTest::newRow("&Test") << "Test";
+    QTest::newRow("&&Test") << "&Test";
+    QTest::newRow("T&est") << "Test";
+    QTest::newRow("&Te&&st") << "Te&st";
+    QTest::newRow("T&e&st") << "Test";
+    QTest::newRow("T&&est") << "T&est";
+    QTest::newRow("T&&e&st") << "T&est";
+    QTest::newRow("T&&&est") << "T&est";
+    QTest::newRow("Tes&t") << "Test";
+    QTest::newRow("Test&") << "Test";
+}
+
 QTEST_MAIN(tst_StringUtils)
 
 #include "tst_stringutils.moc"