diff --git a/indra/llui/llspellcheck.cpp b/indra/llui/llspellcheck.cpp
index 8b3ca50c29c276b1d36713b4a2f500203d454f2b..887714d72071f39cd22a23b5cc2db6636f0c2b45 100644
--- a/indra/llui/llspellcheck.cpp
+++ b/indra/llui/llspellcheck.cpp
@@ -41,6 +41,9 @@ static const std::string DICT_DIR = "dictionaries";
 static const std::string DICT_CUSTOM_SUFFIX = "_custom";
 static const std::string DICT_IGNORE_SUFFIX = "_ignore";
 
+static const std::string DICT_FILE_MAIN = "dictionaries.xml";
+static const std::string DICT_FILE_USER = "user_dictionaries.xml";
+
 LLSD LLSpellChecker::sDictMap;
 LLSpellChecker::settings_change_signal_t LLSpellChecker::sSettingsChangeSignal;
 
@@ -141,10 +144,10 @@ void LLSpellChecker::refreshDictionaryMap()
 	const std::string user_path = getDictionaryUserPath();
 
 	// Load dictionary information (file name, friendly name, ...)
-	llifstream user_file(user_path + "dictionaries.xml", std::ios::binary);
+	llifstream user_file(user_path + DICT_FILE_MAIN, std::ios::binary);
 	if ( (!user_file.is_open()) || (0 == LLSDSerialize::fromXMLDocument(sDictMap, user_file)) || (0 == sDictMap.size()) )
 	{
-		llifstream app_file(app_path + "dictionaries.xml", std::ios::binary);
+		llifstream app_file(app_path + DICT_FILE_MAIN, std::ios::binary);
 		if ( (!app_file.is_open()) || (0 == LLSDSerialize::fromXMLDocument(sDictMap, app_file)) || (0 == sDictMap.size()) )
 		{
 			return;
@@ -152,7 +155,7 @@ void LLSpellChecker::refreshDictionaryMap()
 	}
 
 	// Load user installed dictionary information
-	llifstream custom_file(user_path + "user_dictionaries.xml", std::ios::binary);
+	llifstream custom_file(user_path + DICT_FILE_USER, std::ios::binary);
 	if (custom_file.is_open())
 	{
 		LLSD custom_dict_map;
@@ -245,6 +248,13 @@ void LLSpellChecker::addToDictFile(const std::string& dict_path, const std::stri
 	}
 }
 
+bool LLSpellChecker::isActiveDictionary(const std::string& dict_language) const
+{
+	return
+		(mDictLanguage == dict_language) || 
+		(mDictSecondary.end() != std::find(mDictSecondary.begin(), mDictSecondary.end(), dict_language));
+}
+
 void LLSpellChecker::setSecondaryDictionaries(dict_list_t dict_list)
 {
 	if (!getUseSpellCheck())
@@ -263,8 +273,8 @@ void LLSpellChecker::setSecondaryDictionaries(dict_list_t dict_list)
 	{
 		mDictSecondary = dict_list;
 
-		std::string dict_name = mDictName;
-		initHunspell(dict_name);
+		std::string dict_language = mDictLanguage;
+		initHunspell(dict_language);
 	}
 	else if (end_added != dict_add.begin())		// Add the new secondary dictionaries one by one
 	{
@@ -293,18 +303,18 @@ void LLSpellChecker::setSecondaryDictionaries(dict_list_t dict_list)
 	}
 }
 
-void LLSpellChecker::initHunspell(const std::string& dict_name)
+void LLSpellChecker::initHunspell(const std::string& dict_language)
 {
 	if (mHunspell)
 	{
 		delete mHunspell;
 		mHunspell = NULL;
-		mDictName.clear();
+		mDictLanguage.clear();
 		mDictFile.clear();
 		mIgnoreList.clear();
 	}
 
-	const LLSD dict_entry = (!dict_name.empty()) ? getDictionaryData(dict_name) : LLSD();
+	const LLSD dict_entry = (!dict_language.empty()) ? getDictionaryData(dict_language) : LLSD();
 	if ( (!dict_entry.isDefined()) || (!dict_entry["installed"].asBoolean()) || (!dict_entry["is_primary"].asBoolean()))
 	{
 		sSettingsChangeSignal();
@@ -330,7 +340,7 @@ void LLSpellChecker::initHunspell(const std::string& dict_name)
 			return;
 		}
 
-		mDictName = dict_name;
+		mDictLanguage = dict_language;
 		mDictFile = dict_entry["name"].asString();
 
 		if (dict_entry["has_custom"].asBoolean())
@@ -405,6 +415,73 @@ bool LLSpellChecker::getUseSpellCheck()
 	return (LLSpellChecker::instanceExists()) && (LLSpellChecker::instance().mHunspell);
 }
 
+// static
+bool LLSpellChecker::canRemoveDictionary(const std::string& dict_language)
+{
+	// Only user-installed inactive dictionaries can be removed
+	const LLSD dict_info = getDictionaryData(dict_language);
+	return 
+		(dict_info["user_installed"].asBoolean()) && 
+		( (!getUseSpellCheck()) || (!LLSpellChecker::instance().isActiveDictionary(dict_language)) );
+}
+
+// static
+void LLSpellChecker::removeDictionary(const std::string& dict_language)
+{
+	if (!canRemoveDictionary(dict_language))
+	{
+		return;
+	}
+
+	LLSD dict_map = loadUserDictionaryMap();
+	for (LLSD::array_const_iterator it = dict_map.beginArray(); it != dict_map.endArray(); ++it)
+	{
+		const LLSD& dict_info = *it;
+		if (dict_info["language"].asString() == dict_language)
+		{
+			const std::string dict_dic = getDictionaryUserPath() + dict_info["name"].asString() + ".dic";
+			if (gDirUtilp->fileExists(dict_dic))
+			{
+				LLFile::remove(dict_dic);
+			}
+			const std::string dict_aff = getDictionaryUserPath() + dict_info["name"].asString() + ".aff";
+			if (gDirUtilp->fileExists(dict_aff))
+			{
+				LLFile::remove(dict_aff);
+			}
+			dict_map.erase(it - dict_map.beginArray());
+			break;
+		}
+	}
+	saveUserDictionaryMap(dict_map);
+
+	refreshDictionaryMap();
+}
+
+// static
+LLSD LLSpellChecker::loadUserDictionaryMap()
+{
+	LLSD dict_map;
+	llifstream dict_file(getDictionaryUserPath() + DICT_FILE_USER, std::ios::binary);
+	if (dict_file.is_open())
+	{
+		LLSDSerialize::fromXMLDocument(dict_map, dict_file);
+		dict_file.close();
+	}
+	return dict_map;
+}
+
+// static
+void LLSpellChecker::saveUserDictionaryMap(const LLSD& dict_map)
+{
+	llofstream dict_file(getDictionaryUserPath() + DICT_FILE_USER, std::ios::trunc);
+	if (dict_file.is_open())
+	{
+		LLSDSerialize::toPrettyXML(dict_map, dict_file);
+		dict_file.close();
+	}
+}
+
 // static
 boost::signals2::connection LLSpellChecker::setSettingsChangeCallback(const settings_change_signal_t::slot_type& cb)
 {
@@ -412,12 +489,12 @@ boost::signals2::connection LLSpellChecker::setSettingsChangeCallback(const sett
 }
 
 // static
-void LLSpellChecker::setUseSpellCheck(const std::string& dict_name)
+void LLSpellChecker::setUseSpellCheck(const std::string& dict_language)
 {
-	if ( (((dict_name.empty()) && (getUseSpellCheck())) || (!dict_name.empty())) && 
-		 (LLSpellChecker::instance().mDictName != dict_name) )
+	if ( (((dict_language.empty()) && (getUseSpellCheck())) || (!dict_language.empty())) && 
+		 (LLSpellChecker::instance().mDictLanguage != dict_language) )
 	{
-		LLSpellChecker::instance().initHunspell(dict_name);
+		LLSpellChecker::instance().initHunspell(dict_language);
 	}
 }
 
diff --git a/indra/llui/llspellcheck.h b/indra/llui/llspellcheck.h
index 776565b20ae874f4328894e6bc87e76847e2b1db..4ab80195ead318f7f586563474d0f9fb5706d10d 100644
--- a/indra/llui/llspellcheck.h
+++ b/indra/llui/llspellcheck.h
@@ -48,15 +48,17 @@ class LLSpellChecker : public LLSingleton<LLSpellChecker>, public LLInitClass<LL
 	S32  getSuggestions(const std::string& word, std::vector<std::string>& suggestions) const;
 protected:
 	void addToDictFile(const std::string& dict_path, const std::string& word);
-	void initHunspell(const std::string& dict_name);
+	void initHunspell(const std::string& dict_language);
 
 public:
 	typedef std::list<std::string> dict_list_t;
 
-	const std::string&	getActiveDictionary() const { return mDictName; }
+	const std::string&	getPrimaryDictionary() const { return mDictLanguage; }
 	const dict_list_t&	getSecondaryDictionaries() const { return mDictSecondary; }
+	bool				isActiveDictionary(const std::string& dict_language) const;
 	void				setSecondaryDictionaries(dict_list_t dict_list);
 
+	static bool				 canRemoveDictionary(const std::string& dict_language);
 	static const std::string getDictionaryAppPath();
 	static const std::string getDictionaryUserPath();
 	static const LLSD		 getDictionaryData(const std::string& dict_language);
@@ -64,9 +66,12 @@ class LLSpellChecker : public LLSingleton<LLSpellChecker>, public LLInitClass<LL
 	static bool				 getUseSpellCheck();
 	static bool				 hasDictionary(const std::string& dict_language, bool check_installed = false);
 	static void				 refreshDictionaryMap();
-	static void				 setUseSpellCheck(const std::string& dict_name);
+	static void				 removeDictionary(const std::string& dict_language);
+	static void				 setUseSpellCheck(const std::string& dict_language);
 protected:
+	static LLSD				 loadUserDictionaryMap();
 	static void				 setDictionaryData(const LLSD& dict_info);
+	static void				 saveUserDictionaryMap(const LLSD& dict_map);
 
 public:
 	typedef boost::signals2::signal<void()> settings_change_signal_t;
@@ -76,7 +81,7 @@ class LLSpellChecker : public LLSingleton<LLSpellChecker>, public LLInitClass<LL
 
 protected:
 	Hunspell*	mHunspell;
-	std::string	mDictName;
+	std::string	mDictLanguage;
 	std::string	mDictFile;
 	dict_list_t	mDictSecondary;
 	std::vector<std::string> mIgnoreList;
diff --git a/indra/newview/llfloaterspellchecksettings.cpp b/indra/newview/llfloaterspellchecksettings.cpp
index 0b4f08c327bce6ab861b2d0ebb1e5f7cee189201..059a28fbcd063e6203fab3f76f8d464a75d62d6e 100644
--- a/indra/newview/llfloaterspellchecksettings.cpp
+++ b/indra/newview/llfloaterspellchecksettings.cpp
@@ -46,10 +46,24 @@ LLFloaterSpellCheckerSettings::LLFloaterSpellCheckerSettings(const LLSD& key)
 {
 }
 
+void LLFloaterSpellCheckerSettings::draw()
+{
+	LLFloater::draw();
+
+	std::vector<LLScrollListItem*> sel_items = getChild<LLScrollListCtrl>("spellcheck_available_list")->getAllSelected();
+	bool enable_remove = !sel_items.empty();
+	for (std::vector<LLScrollListItem*>::const_iterator sel_it = sel_items.begin(); sel_it != sel_items.end(); ++sel_it)
+	{
+		enable_remove &= LLSpellChecker::canRemoveDictionary((*sel_it)->getValue().asString());
+	}
+	getChild<LLUICtrl>("spellcheck_remove_btn")->setEnabled(enable_remove);
+}
+
 BOOL LLFloaterSpellCheckerSettings::postBuild(void)
 {
 	gSavedSettings.getControl("SpellCheck")->getSignal()->connect(boost::bind(&LLFloaterSpellCheckerSettings::refreshDictionaries, this, false));
 	LLSpellChecker::setSettingsChangeCallback(boost::bind(&LLFloaterSpellCheckerSettings::onSpellCheckSettingsChange, this));
+	getChild<LLUICtrl>("spellcheck_remove_btn")->setCommitCallback(boost::bind(&LLFloaterSpellCheckerSettings::onBtnRemove, this));
 	getChild<LLUICtrl>("spellcheck_import_btn")->setCommitCallback(boost::bind(&LLFloaterSpellCheckerSettings::onBtnImport, this));
 	getChild<LLUICtrl>("spellcheck_main_combo")->setCommitCallback(boost::bind(&LLFloaterSpellCheckerSettings::refreshDictionaries, this, false));
 	getChild<LLUICtrl>("spellcheck_moveleft_btn")->setCommitCallback(boost::bind(&LLFloaterSpellCheckerSettings::onBtnMove, this, "spellcheck_active_list", "spellcheck_available_list"));
@@ -121,6 +135,15 @@ void LLFloaterSpellCheckerSettings::onOpen(const LLSD& key)
 	refreshDictionaries(true);
 }
 
+void LLFloaterSpellCheckerSettings::onBtnRemove()
+{
+	std::vector<LLScrollListItem*> sel_items = getChild<LLScrollListCtrl>("spellcheck_available_list")->getAllSelected();
+	for (std::vector<LLScrollListItem*>::const_iterator sel_it = sel_items.begin(); sel_it != sel_items.end(); ++sel_it)
+	{
+		LLSpellChecker::instance().removeDictionary((*sel_it)->getValue().asString());
+	}
+}
+
 void LLFloaterSpellCheckerSettings::onSpellCheckSettingsChange()
 {
 	refreshDictionaries(true);
@@ -137,7 +160,7 @@ void LLFloaterSpellCheckerSettings::refreshDictionaries(bool from_settings)
 	std::string dict_cur = dict_combo->getSelectedItemLabel();
 	if ((dict_cur.empty() || from_settings) && (LLSpellChecker::getUseSpellCheck()))
 	{
-		dict_cur = LLSpellChecker::instance().getActiveDictionary();
+		dict_cur = LLSpellChecker::instance().getPrimaryDictionary();
 	}
 	dict_combo->clearRows();
 	dict_combo->setEnabled(enabled);
diff --git a/indra/newview/llfloaterspellchecksettings.h b/indra/newview/llfloaterspellchecksettings.h
index da3239bca38a0eea8afa55fa0821369cd99e1e6f..67bd26004843f5838d79a994addd35b50989b134 100644
--- a/indra/newview/llfloaterspellchecksettings.h
+++ b/indra/newview/llfloaterspellchecksettings.h
@@ -34,6 +34,7 @@ class LLFloaterSpellCheckerSettings : public LLFloater
 public:
 	LLFloaterSpellCheckerSettings(const LLSD& key);
 
+	/*virtual*/ void draw();
 	/*virtual*/ BOOL postBuild();
 	/*virtual*/ void onOpen(const LLSD& key);
 
@@ -42,6 +43,7 @@ class LLFloaterSpellCheckerSettings : public LLFloater
 	void onBtnImport();
 	void onBtnMove(const std::string& from, const std::string& to);
 	void onBtnOK();
+	void onBtnRemove();
 	void onSpellCheckSettingsChange();
 	void refreshDictionaries(bool from_settings);
 };
diff --git a/indra/newview/skins/default/xui/en/floater_spellcheck.xml b/indra/newview/skins/default/xui/en/floater_spellcheck.xml
index 91639ed0dac6964edc2f5b4f4c7cd561e96e8ffc..786b830ad968aa7d38034c78d13361649e185a69 100644
--- a/indra/newview/skins/default/xui/en/floater_spellcheck.xml
+++ b/indra/newview/skins/default/xui/en/floater_spellcheck.xml
@@ -3,10 +3,10 @@
  border="true"
  can_close="true"
  can_minimize="true"
- bottom="275"
+ bottom="300"
  left="300"
  can_resize="false"
- height="330"
+ height="355"
  width="490"
  name="spellcheck_floater"
  title="Spell Checker Settings">
@@ -53,16 +53,6 @@
    top_pad="-15"
    width="175"
   />
-  <button
-   follows="left|top"
-   height="23"
-   label="Import"
-   label_selected="Import"
-   layout="topleft"
-   left_pad="5"
-   name="spellcheck_import_btn"
-   top_delta="0"
-   width="75" />
   <text
    enabled_control="SpellCheck"
    follows="top|left"
@@ -148,6 +138,25 @@
    top_pad="-105"
    width="175"
   />
+  <button
+   enabled="false"
+   follows="left|top"
+   height="23"
+   label="Remove"
+   layout="topleft"
+   left="55"
+   name="spellcheck_remove_btn"
+   top_pad="5"
+   width="80" />
+  <button
+   follows="left|top"
+   height="23"
+   label="Import..."
+   layout="topleft"
+   left_pad="15"
+   name="spellcheck_import_btn"
+   top_delta="0"
+   width="80" />
   <view_border
    top_pad="10"
    left="2"
@@ -159,7 +168,7 @@
    mouse_opaque="false"
    name="divisor4"/>
   <button
-   top_pad="10"
+   top_pad="8"
    right="380"
    height="22"
    width="90"