diff --git a/loader.js b/loader.js
index e0bfac6258..20f9d49f88 100644
--- a/loader.js
+++ b/loader.js
@@ -6,6 +6,18 @@ var	nconf = require('nconf'),
 	start = function() {
 		var	fork = require('child_process').fork,
 			nbb_start = function() {
+				if (timesStarted > 3) {
+					console.log('\n[loader] Experienced three start attempts in 10 seconds, most likely am error on startup. Halting.');
+					process.exit();
+					return;
+				}
+
+				timesStarted++;
+				if (startTimer) {
+					clearTimeout(startTimer);
+				}
+				startTimer = setTimeout(resetTimer, 1000*10);
+
 				nbb = fork('./app', process.argv.slice(2), {
 						env: {
 							'NODE_ENV': process.env.NODE_ENV
@@ -29,6 +41,10 @@ var	nconf = require('nconf'),
 				});
 			},
 			nbb_stop = function() {
+				if (startTimer) {
+					clearTimeout(startTimer);
+				}
+
 				nbb.kill();
 				if (fs.existsSync(pidFilePath)) {
 					var	pid = parseInt(fs.readFileSync(pidFilePath, { encoding: 'utf-8' }), 10);
@@ -42,7 +58,13 @@ var	nconf = require('nconf'),
 					nbb_start();
 				});
 				nbb.kill();
-			};
+			},
+			resetTimer = function() {
+				clearTimeout(startTimer);
+				timesStarted = 0;
+			},
+			timesStarted = 0,
+			startTimer;
 
 		process.on('SIGINT', nbb_stop);
 		process.on('SIGTERM', nbb_stop);
diff --git a/public/language/ar/unread.json b/public/language/ar/unread.json
index 0c763e2d90..3a973748af 100644
--- a/public/language/ar/unread.json
+++ b/public/language/ar/unread.json
@@ -1,6 +1,9 @@
 {
     "title": "Unread",
     "no_unread_topics": "ليس هناك أي موضوع غير مقروء",
-    "mark_all_read": "إجعل كل المواضيع مقرؤة",
-    "load_more": "حمل المزيد"
+    "load_more": "حمل المزيد",
+    "mark_as_read": "Mark as Read",
+    "selected": "Selected",
+    "all": "All",
+    "topics_marked_as_read.success": "Topics marked as read!"
 }
\ No newline at end of file
diff --git a/public/language/cs/unread.json b/public/language/cs/unread.json
index 88f2fd2255..e3e5bc1ce5 100644
--- a/public/language/cs/unread.json
+++ b/public/language/cs/unread.json
@@ -1,6 +1,9 @@
 {
     "title": "Unread",
     "no_unread_topics": "Nejsou zde žádné nepřečtené témata.",
-    "mark_all_read": "Označit vše jako přečtené",
-    "load_more": "Načíst další"
+    "load_more": "Načíst další",
+    "mark_as_read": "Mark as Read",
+    "selected": "Selected",
+    "all": "All",
+    "topics_marked_as_read.success": "Topics marked as read!"
 }
\ No newline at end of file
diff --git a/public/language/de/category.json b/public/language/de/category.json
index b0a4cd36a5..85e6c3da99 100644
--- a/public/language/de/category.json
+++ b/public/language/de/category.json
@@ -7,6 +7,6 @@
     "browsing": "Aktiv",
     "no_replies": "Niemand hat geantwortet",
     "replied": "geantwortet",
-    "last_edited_by": "zuletzt editiert durch",
-    "share_this_category": "Share this category"
+    "last_edited_by": "zuletzt editiert von",
+    "share_this_category": "Teile diese Kategorie"
 }
\ No newline at end of file
diff --git a/public/language/de/unread.json b/public/language/de/unread.json
index 0ca36e3266..3362e0dee9 100644
--- a/public/language/de/unread.json
+++ b/public/language/de/unread.json
@@ -1,6 +1,9 @@
 {
     "title": "Ungelesen",
     "no_unread_topics": "Es gibt keine ungelesenen Themen.",
-    "mark_all_read": "alle als gelesen markieren",
-    "load_more": "mehr laden"
+    "load_more": "mehr laden",
+    "mark_as_read": "Mark as Read",
+    "selected": "Selected",
+    "all": "All",
+    "topics_marked_as_read.success": "Topics marked as read!"
 }
\ No newline at end of file
diff --git a/public/language/en@pirate/unread.json b/public/language/en@pirate/unread.json
index a4bb2fd315..17e7089fdf 100644
--- a/public/language/en@pirate/unread.json
+++ b/public/language/en@pirate/unread.json
@@ -1,6 +1,9 @@
 {
     "title": "Unread",
     "no_unread_topics": "There be no unread topics.",
-    "mark_all_read": "I seen 'em all!",
-    "load_more": "Giv'er more"
+    "load_more": "Giv'er more",
+    "mark_as_read": "Mark as Read",
+    "selected": "Selected",
+    "all": "All",
+    "topics_marked_as_read.success": "Topics marked as read!"
 }
\ No newline at end of file
diff --git a/public/language/en_GB/error.json b/public/language/en_GB/error.json
index dcab1f9288..584a472327 100644
--- a/public/language/en_GB/error.json
+++ b/public/language/en_GB/error.json
@@ -5,11 +5,32 @@
 
 	"invalid-cid": "Invalid Category ID",
 	"invalid-tid": "Invalid Topic ID",
+	"invalid-pid": "Invalid Post ID",
+	"invalid-uid": "Invalid User ID",
+
+	"invalid-username": "Invalid Username",
+	"invalid-email": "Invalid Email",
+	"invalid-title": "Invalid title",
+	"invalid-user-data": "Invalid User Data",
+	"invalid-password": "Invalid Password",
+
+	"invalid-pagination-value": "Invalid pagination value",
+
+	"username-taken": "Username taken",
+	"email-taken": "Email taken",
+
+	"user-banned": "User banned",
 
 	"no-category": "Category doesn't exist",
 	"no-topic": "Topic doesn't exist",
+	"no-post": "Post doesn't exist",
+	"no-group": "Group doesn't exist",
+	"no-user": "User doesn't exist",
+	"no-teaser": "Teaser doesn't exist",
 	"no-privileges": "You don't have enough privileges for this action.",
 
+	"category-disabled": "Category disabled",
+
 	"topic-locked": "Topic Locked",
 
 	"still-uploading" : "Please wait for uploads to complete.",
@@ -17,5 +38,32 @@
 	"title-too-short" : "Please enter a longer title. At least %1 characters.",
 	"title-too-long" : "Please enter a shorter title. Titles can't be longer than %1 characters.",
 	"too-many-posts" : "You can only post every %1 seconds.'",
-	"file-too-big" : "Maximum allowed file size is %1 kbs"
-}
\ No newline at end of file
+	"file-too-big" : "Maximum allowed file size is %1 kbs",
+
+	"cant-vote-self-post": "You cannot vote for your own post",
+	"already-favourited": "You already favourited this post",
+	"already-unfavourited": "You alread unfavourited this post",
+
+	"cant-ban-other-admins": "You can't ban other admins!",
+
+	"invalid-image-type": "Invalid image type",
+
+	"group-name-too-short": "Group name too short",
+	"group-already-exists": "Group already exists",
+	"group-name-change-not-allowed": "Group name change not allowed",
+
+	"post-already-deleted": "Post already deleted",
+	"post-already-restored": "Post already restored",
+
+	"topic-already-deleted": "Topic already deleted",
+	"topic-already-restored": "Topic already restored",
+
+
+	"topic-thumbnails-are-disabled": "Topic thumbnails are disabled.",
+	"invalid-file": "Invalid File",
+	"uploads-are-disabled": "Uploads are disabled",
+
+	"signature-too-long" : "Signature can't be longer than %1 characters!",
+
+	"cant-chat-with-yourself": "You can't chat with yourself!"
+}
diff --git a/public/language/en_GB/success.json b/public/language/en_GB/success.json
index 397b857c38..d38b90c6fa 100644
--- a/public/language/en_GB/success.json
+++ b/public/language/en_GB/success.json
@@ -1,4 +1,5 @@
 {
-	"topic-post": "You have successfully posted."
+	"topic-post": "You have successfully posted.",
+	"authentication-successful": "Authentication Successful"
 
 }
\ No newline at end of file
diff --git a/public/language/en_GB/topic.json b/public/language/en_GB/topic.json
index 1a38c9f776..a11a3b941a 100644
--- a/public/language/en_GB/topic.json
+++ b/public/language/en_GB/topic.json
@@ -69,15 +69,8 @@
 
 	"favourite": "Favourite",
 	"favourites": "Favourites",
-	"favourites.not_logged_in.title": "Not Logged In",
-	"favourites.not_logged_in.message": "Please log in in order to favourite this post",
 	"favourites.has_no_favourites": "You don't have any favourites, favourite some posts to see them here!",
 
-	"vote.not_logged_in.title": "Not Logged In",
-	"vote.not_logged_in.message": "Please log in in order to vote",
-	"vote.cant_vote_self.title": "Invalid Vote",
-	"vote.cant_vote_self.message": "You cannot vote for your own post",
-
 	"loading_more_posts": "Loading More Posts",
 	"move_topic": "Move Topic",
 	"move_post": "Move Post",
diff --git a/public/language/en_GB/user.json b/public/language/en_GB/user.json
index d7f2d83b2d..110dd9698e 100644
--- a/public/language/en_GB/user.json
+++ b/public/language/en_GB/user.json
@@ -23,6 +23,7 @@
 	"follow": "Follow",
 	"unfollow": "Unfollow",
 
+	"profile_update_success": "Profile has been updated successfully!",
 	"change_picture": "Change Picture",
 	"edit": "Edit",
 	"uploaded_picture": "Uploaded Picture",
diff --git a/public/language/es/unread.json b/public/language/es/unread.json
index b06f14f687..29f15be5f2 100644
--- a/public/language/es/unread.json
+++ b/public/language/es/unread.json
@@ -1,6 +1,9 @@
 {
     "title": "No leído",
     "no_unread_topics": "No hay temas nuevos para leer.",
-    "mark_all_read": "Marcar todo como leído",
-    "load_more": "Cargar más"
+    "load_more": "Cargar más",
+    "mark_as_read": "Mark as Read",
+    "selected": "Selected",
+    "all": "All",
+    "topics_marked_as_read.success": "Topics marked as read!"
 }
\ No newline at end of file
diff --git a/public/language/et/unread.json b/public/language/et/unread.json
index a8de01c463..606ffe7fcd 100644
--- a/public/language/et/unread.json
+++ b/public/language/et/unread.json
@@ -1,6 +1,9 @@
 {
     "title": "Lugemata",
     "no_unread_topics": "Siin ei ole lugemata teemasi.",
-    "mark_all_read": "Märgi kõik loetuks",
-    "load_more": "Lae rohkem"
+    "load_more": "Lae rohkem",
+    "mark_as_read": "Mark as Read",
+    "selected": "Selected",
+    "all": "All",
+    "topics_marked_as_read.success": "Topics marked as read!"
 }
\ No newline at end of file
diff --git a/public/language/fa_IR/unread.json b/public/language/fa_IR/unread.json
index fcfe933e92..b65f56a41f 100644
--- a/public/language/fa_IR/unread.json
+++ b/public/language/fa_IR/unread.json
@@ -1,6 +1,9 @@
 {
     "title": "نخوانده‌ها",
     "no_unread_topics": "جستار خوانده نشده‌ای وجود ندارد.",
-    "mark_all_read": "همه را خوانده شده بگیر",
-    "load_more": "بارگذاری بیش‌تر"
+    "load_more": "بارگذاری بیش‌تر",
+    "mark_as_read": "Mark as Read",
+    "selected": "Selected",
+    "all": "All",
+    "topics_marked_as_read.success": "Topics marked as read!"
 }
\ No newline at end of file
diff --git a/public/language/fi/unread.json b/public/language/fi/unread.json
index 928290c7ce..f6f1990845 100644
--- a/public/language/fi/unread.json
+++ b/public/language/fi/unread.json
@@ -1,6 +1,9 @@
 {
     "title": "Lukematon",
     "no_unread_topics": "Ei lukemattomia aiheita.",
-    "mark_all_read": "Merkitse kaikki luetuiksi",
-    "load_more": "Lataa lisää"
+    "load_more": "Lataa lisää",
+    "mark_as_read": "Mark as Read",
+    "selected": "Selected",
+    "all": "All",
+    "topics_marked_as_read.success": "Topics marked as read!"
 }
\ No newline at end of file
diff --git a/public/language/fr/unread.json b/public/language/fr/unread.json
index 69c70b8de9..f48f6def05 100644
--- a/public/language/fr/unread.json
+++ b/public/language/fr/unread.json
@@ -1,6 +1,9 @@
 {
     "title": "Non Lu",
     "no_unread_topics": "Aucun sujet non lu.",
-    "mark_all_read": "Marquer tout comme lu",
-    "load_more": "Charger la suite"
+    "load_more": "Charger la suite",
+    "mark_as_read": "Mark as Read",
+    "selected": "Selected",
+    "all": "All",
+    "topics_marked_as_read.success": "Topics marked as read!"
 }
\ No newline at end of file
diff --git a/public/language/he/unread.json b/public/language/he/unread.json
index e191fbf760..1b4cd01f78 100644
--- a/public/language/he/unread.json
+++ b/public/language/he/unread.json
@@ -1,6 +1,9 @@
 {
     "title": "לא נקרא",
     "no_unread_topics": "אין נושאים שלא נקראו",
-    "mark_all_read": "סמן הכל כנקרא",
-    "load_more": "טען עוד"
+    "load_more": "טען עוד",
+    "mark_as_read": "Mark as Read",
+    "selected": "Selected",
+    "all": "All",
+    "topics_marked_as_read.success": "Topics marked as read!"
 }
\ No newline at end of file
diff --git a/public/language/hu/unread.json b/public/language/hu/unread.json
index 8c3a6a1887..e120ac3948 100644
--- a/public/language/hu/unread.json
+++ b/public/language/hu/unread.json
@@ -1,6 +1,9 @@
 {
     "title": "Olvasatlan",
     "no_unread_topics": "Nincsenek olvasatlan topikok.",
-    "mark_all_read": "Az összes olvasottként jelölése",
-    "load_more": "Több betöltése"
+    "load_more": "Több betöltése",
+    "mark_as_read": "Mark as Read",
+    "selected": "Selected",
+    "all": "All",
+    "topics_marked_as_read.success": "Topics marked as read!"
 }
\ No newline at end of file
diff --git a/public/language/it/unread.json b/public/language/it/unread.json
index 362b11e8f5..bb6af18556 100644
--- a/public/language/it/unread.json
+++ b/public/language/it/unread.json
@@ -1,6 +1,9 @@
 {
     "title": "Non letto",
     "no_unread_topics": "Non ci sono discussioni non lette.",
-    "mark_all_read": "Segna tutto come già letto",
-    "load_more": "Carica Altro"
+    "load_more": "Carica Altro",
+    "mark_as_read": "Mark as Read",
+    "selected": "Selected",
+    "all": "All",
+    "topics_marked_as_read.success": "Topics marked as read!"
 }
\ No newline at end of file
diff --git a/public/language/nb/unread.json b/public/language/nb/unread.json
index 3e3150d7f1..f3a501649c 100644
--- a/public/language/nb/unread.json
+++ b/public/language/nb/unread.json
@@ -1,6 +1,9 @@
 {
     "title": "Uleste",
     "no_unread_topics": "Det er ingen uleste emner.",
-    "mark_all_read": "Marker alle som lest",
-    "load_more": "Last mer"
+    "load_more": "Last mer",
+    "mark_as_read": "Mark as Read",
+    "selected": "Selected",
+    "all": "All",
+    "topics_marked_as_read.success": "Topics marked as read!"
 }
\ No newline at end of file
diff --git a/public/language/nl/unread.json b/public/language/nl/unread.json
index ce0b9a43e9..efee7eb23c 100644
--- a/public/language/nl/unread.json
+++ b/public/language/nl/unread.json
@@ -1,6 +1,9 @@
 {
     "title": "Ongelezen",
     "no_unread_topics": "Er zijn geen ongelezen onderwerpen",
-    "mark_all_read": "Alles markeren als Gelezen",
-    "load_more": "Meer Laden"
+    "load_more": "Meer Laden",
+    "mark_as_read": "Mark as Read",
+    "selected": "Selected",
+    "all": "All",
+    "topics_marked_as_read.success": "Topics marked as read!"
 }
\ No newline at end of file
diff --git a/public/language/pl/unread.json b/public/language/pl/unread.json
index a69a8570a7..74197b2f5a 100644
--- a/public/language/pl/unread.json
+++ b/public/language/pl/unread.json
@@ -1,6 +1,9 @@
 {
     "title": "Nieprzeczytane",
     "no_unread_topics": "Nie masz żadnych nieprzeczytanych wątków.",
-    "mark_all_read": "Oznacz wszystkie jako przeczytane",
-    "load_more": "Więcej"
+    "load_more": "Więcej",
+    "mark_as_read": "Mark as Read",
+    "selected": "Selected",
+    "all": "All",
+    "topics_marked_as_read.success": "Topics marked as read!"
 }
\ No newline at end of file
diff --git a/public/language/pt_BR/unread.json b/public/language/pt_BR/unread.json
index e5f60bcefa..b053b20a6f 100644
--- a/public/language/pt_BR/unread.json
+++ b/public/language/pt_BR/unread.json
@@ -1,6 +1,9 @@
 {
     "title": "Não Lido",
     "no_unread_topics": "Todos os tópicos lidos.",
-    "mark_all_read": "Marcar todos como Lido",
-    "load_more": "Carregar"
+    "load_more": "Carregar",
+    "mark_as_read": "Mark as Read",
+    "selected": "Selected",
+    "all": "All",
+    "topics_marked_as_read.success": "Topics marked as read!"
 }
\ No newline at end of file
diff --git a/public/language/ru/unread.json b/public/language/ru/unread.json
index d1184cb5c8..1f7a121182 100644
--- a/public/language/ru/unread.json
+++ b/public/language/ru/unread.json
@@ -1,6 +1,9 @@
 {
     "title": "Непрочитанные темы",
     "no_unread_topics": "Нет непрочитанных тем.",
-    "mark_all_read": "Отметить все темы как прочитанные",
-    "load_more": "Загрузить еще"
+    "load_more": "Загрузить еще",
+    "mark_as_read": "Mark as Read",
+    "selected": "Selected",
+    "all": "All",
+    "topics_marked_as_read.success": "Topics marked as read!"
 }
\ No newline at end of file
diff --git a/public/language/sc/unread.json b/public/language/sc/unread.json
index 6e0664abfc..db7e78b8c9 100644
--- a/public/language/sc/unread.json
+++ b/public/language/sc/unread.json
@@ -1,6 +1,9 @@
 {
     "title": "De Lèghere",
     "no_unread_topics": "Non bi sunt arresonadas de lèghere.",
-    "mark_all_read": "Signala comente Lèghidos",
-    "load_more": "Càrriga de Prus"
+    "load_more": "Càrriga de Prus",
+    "mark_as_read": "Mark as Read",
+    "selected": "Selected",
+    "all": "All",
+    "topics_marked_as_read.success": "Topics marked as read!"
 }
\ No newline at end of file
diff --git a/public/language/sk/unread.json b/public/language/sk/unread.json
index 252ab9d3e4..99a9b42acf 100644
--- a/public/language/sk/unread.json
+++ b/public/language/sk/unread.json
@@ -1,6 +1,9 @@
 {
     "title": "Neprečítané",
     "no_unread_topics": "Nie sú tu žiadne neprečítané témy.",
-    "mark_all_read": "Označit všetko ako prečítané",
-    "load_more": "Načítať dalšie"
+    "load_more": "Načítať dalšie",
+    "mark_as_read": "Mark as Read",
+    "selected": "Selected",
+    "all": "All",
+    "topics_marked_as_read.success": "Topics marked as read!"
 }
\ No newline at end of file
diff --git a/public/language/sv/unread.json b/public/language/sv/unread.json
index 12701bdde5..4bc578abc8 100644
--- a/public/language/sv/unread.json
+++ b/public/language/sv/unread.json
@@ -1,6 +1,9 @@
 {
     "title": "Olästa",
     "no_unread_topics": "Det finns inga ilästa ämnen.",
-    "mark_all_read": "Markera alla som lästa",
-    "load_more": "Ladda fler"
+    "load_more": "Ladda fler",
+    "mark_as_read": "Mark as Read",
+    "selected": "Selected",
+    "all": "All",
+    "topics_marked_as_read.success": "Topics marked as read!"
 }
\ No newline at end of file
diff --git a/public/language/th/unread.json b/public/language/th/unread.json
index f0d186b444..37451a65ba 100644
--- a/public/language/th/unread.json
+++ b/public/language/th/unread.json
@@ -1,6 +1,9 @@
 {
     "title": "ไม่ได้อ่าน",
     "no_unread_topics": "ไม่มีกระทู้ที่ยังไม่ได้อ่านเป็น",
-    "mark_all_read": "มาร์คทั้งหมดเป็นอ่าน",
-    "load_more": "โหลดเพิ่มเติม"
+    "load_more": "โหลดเพิ่มเติม",
+    "mark_as_read": "Mark as Read",
+    "selected": "Selected",
+    "all": "All",
+    "topics_marked_as_read.success": "Topics marked as read!"
 }
\ No newline at end of file
diff --git a/public/language/tr/unread.json b/public/language/tr/unread.json
index feabfb6e03..0473f2dd3a 100644
--- a/public/language/tr/unread.json
+++ b/public/language/tr/unread.json
@@ -1,6 +1,9 @@
 {
     "title": "Okunmadı",
     "no_unread_topics": "Okunmamış başlık mevcut değil.",
-    "mark_all_read": "Hepsini Okundu Olarak İşaretle",
-    "load_more": "Daha Fazla"
+    "load_more": "Daha Fazla",
+    "mark_as_read": "Mark as Read",
+    "selected": "Selected",
+    "all": "All",
+    "topics_marked_as_read.success": "Topics marked as read!"
 }
\ No newline at end of file
diff --git a/public/language/zh_CN/unread.json b/public/language/zh_CN/unread.json
index b5107e3b47..296a432e01 100644
--- a/public/language/zh_CN/unread.json
+++ b/public/language/zh_CN/unread.json
@@ -1,6 +1,9 @@
 {
     "title": "未读",
     "no_unread_topics": "没有未读主题。",
-    "mark_all_read": "标记全部为已读",
-    "load_more": "载入更多"
+    "load_more": "载入更多",
+    "mark_as_read": "Mark as Read",
+    "selected": "Selected",
+    "all": "All",
+    "topics_marked_as_read.success": "Topics marked as read!"
 }
\ No newline at end of file
diff --git a/public/language/zh_TW/unread.json b/public/language/zh_TW/unread.json
index 050c12403d..7f8beef22f 100644
--- a/public/language/zh_TW/unread.json
+++ b/public/language/zh_TW/unread.json
@@ -1,6 +1,9 @@
 {
     "title": "未讀",
     "no_unread_topics": "沒有未讀主題。",
-    "mark_all_read": "標記全部為已讀",
-    "load_more": "載入更多"
+    "load_more": "載入更多",
+    "mark_as_read": "Mark as Read",
+    "selected": "Selected",
+    "all": "All",
+    "topics_marked_as_read.success": "Topics marked as read!"
 }
\ No newline at end of file
diff --git a/public/src/app.js b/public/src/app.js
index 86e6033c45..108635da2e 100644
--- a/public/src/app.js
+++ b/public/src/app.js
@@ -320,25 +320,11 @@ var socket,
 
 	app.openChat = function (username, touid) {
 		if (username === app.username) {
-			app.alert({
-				type: 'warning',
-				title: 'Invalid Chat',
-				message: "You can't chat with yourself!",
-				timeout: 5000
-			});
-
-			return;
+			return app.alertError('[[error:cant-chat-with-yourself]]');
 		}
 
 		if (!app.uid) {
-			app.alert({
-				type: 'danger',
-				title: 'Not Logged In',
-				message: 'Please log in to chat with <strong>' + username + '</strong>',
-				timeout: 5000
-			});
-
-			return;
+			return app.alertError('[[error:not-logged-in]]');
 		}
 
 		require(['chat'], function (chat) {
diff --git a/public/src/forum/accountedit.js b/public/src/forum/accountedit.js
index 701c97ff74..071cb31a3a 100644
--- a/public/src/forum/accountedit.js
+++ b/public/src/forum/accountedit.js
@@ -1,82 +1,82 @@
+'use strict';
+
+/* globals define, ajaxify, socket, app, config, utils, translator */
+
 define(['forum/accountheader', 'uploader'], function(header, uploader) {
 	var AccountEdit = {},
 		gravatarPicture = '',
-		uploadedPicture = '';
+		uploadedPicture = '',
+		selectedImageType = '';
 
 	AccountEdit.init = function() {
-		header.init();
-
 		gravatarPicture = ajaxify.variables.get('gravatarpicture');
 		uploadedPicture = ajaxify.variables.get('uploadedpicture');
 
-		var selectedImageType = '';
-
-		$('#submitBtn').on('click', function() {
+		header.init();
 
-			var userData = {
-				uid: $('#inputUID').val(),
-				username: $('#inputUsername').val(),
-				email: $('#inputEmail').val(),
-				fullname: $('#inputFullname').val(),
-				website: $('#inputWebsite').val(),
-				birthday: $('#inputBirthday').val(),
-				location: $('#inputLocation').val(),
-				signature: $('#inputSignature').val()
-			};
+		$('#submitBtn').on('click', updateProfile);
 
-			socket.emit('user.updateProfile', userData, function(err, data) {
-				if(err) {
-					return app.alertError(err.message);
-				}
+		handleImageChange();
+		handleImageUpload();
+		handlePasswordChange();
+		updateSignature();
+	};
 
-				if (!data) {
-					return app.alertError('There was an error updating the profile!');
-				}
+	function updateProfile() {
+		var userData = {
+			uid: $('#inputUID').val(),
+			username: $('#inputUsername').val(),
+			email: $('#inputEmail').val(),
+			fullname: $('#inputFullname').val(),
+			website: $('#inputWebsite').val(),
+			birthday: $('#inputBirthday').val(),
+			location: $('#inputLocation').val(),
+			signature: $('#inputSignature').val()
+		};
 
-				app.alertSuccess('Profile has been updated successfully!');
-				if (data.picture) {
-					$('#user-current-picture').attr('src', data.picture);
-					$('#user_label img').attr('src', data.picture);
-				}
+		socket.emit('user.updateProfile', userData, function(err, data) {
+			if (err) {
+				return app.alertError(err.message);
+			}
 
-				if (data.gravatarpicture) {
-					$('#user-gravatar-picture').attr('src', data.gravatarpicture);
-					gravatarPicture = data.gravatarpicture;
-				}
+			app.alertSuccess('[[user:profile_update_success]]');
 
-				if(data.userslug) {
-					var oldslug = $('.account-username-box').attr('data-userslug');
-					$('.account-username-box a').each(function(index) {
-						$(this).attr('href', $(this).attr('href').replace(oldslug, data.userslug));
-					});
+			if (data.picture) {
+				$('#user-current-picture').attr('src', data.picture);
+				$('#user_label img').attr('src', data.picture);
+			}
 
-					$('.account-username-box').attr('data-userslug', data.userslug);
+			if (data.gravatarpicture) {
+				$('#user-gravatar-picture').attr('src', data.gravatarpicture);
+				gravatarPicture = data.gravatarpicture;
+			}
 
-					$('#user-profile-link').attr('href', config.relative_path + '/user/' + data.userslug);
-					$('#user-profile-link span').html(' ' + userData.username);
-				}
-			});
-			return false;
-		});
+			if (data.userslug) {
+				var oldslug = $('.account-username-box').attr('data-userslug');
+				$('.account-username-box a').each(function(index) {
+					$(this).attr('href', $(this).attr('href').replace(oldslug, data.userslug));
+				});
 
+				$('.account-username-box').attr('data-userslug', data.userslug);
 
-		function getSignatureCharsLeft() {
-			if($('#inputSignature').length) {
-				return '(' + $('#inputSignature').val().length + '/' + config.maximumSignatureLength + ')';
-			} else {
-				return '';
+				$('#user-profile-link').attr('href', config.relative_path + '/user/' + data.userslug);
+				$('#user-profile-link span').html(' ' + userData.username);
 			}
-		}
+		});
 
-		$('#signatureCharCountLeft').html(getSignatureCharsLeft());
+		return false;
+	}
 
-		$('#inputSignature').on('keyup change', function(ev) {
-			$('#signatureCharCountLeft').html(getSignatureCharsLeft());
-		});
+	function handleImageChange() {
+		function selectImageType(type) {
+			$('#gravatar-box .fa-check').toggle(type === 'gravatar');
+			$('#uploaded-box .fa-check').toggle(type === 'uploaded');
+			selectedImageType = type;
+		}
 
 		$('#changePictureBtn').on('click', function() {
 			selectedImageType = '';
-			AccountEdit.updateImages();
+			updateImages();
 
 			$('#change-picture-modal').modal('show');
 			$('#change-picture-modal').removeClass('hide');
@@ -85,31 +85,29 @@ define(['forum/accountheader', 'uploader'], function(header, uploader) {
 		});
 
 		$('#gravatar-box').on('click', function() {
-			$('#gravatar-box .fa-check').show();
-			$('#uploaded-box .fa-check').hide();
-			selectedImageType = 'gravatar';
+			selectImageType('gravatar');
 		});
 
 		$('#uploaded-box').on('click', function() {
-			$('#gravatar-box .fa-check').hide();
-			$('#uploaded-box .fa-check').show();
-			selectedImageType = 'uploaded';
+			selectImageType('uploaded');
 		});
 
 		$('#savePictureChangesBtn').on('click', function() {
 			$('#change-picture-modal').modal('hide');
 
 			if (selectedImageType) {
-				AccountEdit.changeUserPicture(selectedImageType);
+				changeUserPicture(selectedImageType);
 
-				if (selectedImageType == 'gravatar')
+				if (selectedImageType === 'gravatar') {
 					$('#user-current-picture').attr('src', gravatarPicture);
-				else if (selectedImageType == 'uploaded')
+				} else if (selectedImageType === 'uploaded') {
 					$('#user-current-picture').attr('src', uploadedPicture);
+				}
 			}
-
 		});
+	}
 
+	function handleImageUpload() {
 		$('#upload-picture-modal').on('hide', function() {
 			$('#userPhotoInput').val('');
 		});
@@ -117,7 +115,7 @@ define(['forum/accountheader', 'uploader'], function(header, uploader) {
 		$('#uploadPictureBtn').on('click', function() {
 
 			$('#change-picture-modal').modal('hide');
-			uploader.open(RELATIVE_PATH + '/user/uploadpicture', {uid: ajaxify.variables.get('theirid')}, config.maximumProfileImageSize, function(imageUrlOnServer) {
+			uploader.open(config.relative_path + '/user/uploadpicture', {uid: ajaxify.variables.get('theirid')}, config.maximumProfileImageSize, function(imageUrlOnServer) {
 				imageUrlOnServer = imageUrlOnServer + '?' + new Date().getTime();
 
 				$('#user-current-picture').attr('src', imageUrlOnServer);
@@ -130,137 +128,129 @@ define(['forum/accountheader', 'uploader'], function(header, uploader) {
 				}, app.updateHeader);
 			});
 
-
 			return false;
 		});
+	}
 
-		function showError(element, msg) {
-			translator.translate(msg, function(msg) {
-				element.html(msg);
-				element.parent()
-					.removeClass('alert-success')
-					.addClass('alert-danger');
-				element.show();
-				validationError = true;
-			});
-		}
-
-		function showSuccess(element, msg) {
-			translator.translate(msg, function(msg) {
-				element.html(msg);
-				element.parent()
-					.removeClass('alert-danger')
-					.addClass('alert-success');
-				element.show();
-			});
+	function handlePasswordChange() {
+		var currentPassword = $('#inputCurrentPassword');
+		var password_notify = $('#password-notify');
+		var password_confirm_notify = $('#password-confirm-notify');
+		var password = $('#inputNewPassword');
+		var password_confirm = $('#inputNewPasswordAgain');
+		var passwordvalid = false;
+		var passwordsmatch = false;
+		var successIcon = '<i class="fa fa-check"></i>';
+
+		function onPasswordChanged() {
+			passwordvalid = utils.isPasswordValid(password.val());
+			if (password.val().length < config.minimumPasswordLength) {
+				showError(password_notify, '[[user:change_password_error_length]]');
+			} else if (!passwordvalid) {
+				showError(password_notify, '[[user:change_password_error]]');
+			} else {
+				showSuccess(password_notify, successIcon);
+			}
 		}
 
-		(function handlePasswordChange() {
-			var currentPassword = $('#inputCurrentPassword');
-			var password_notify = $('#password-notify');
-			var password_confirm_notify = $('#password-confirm-notify');
-			var password = $('#inputNewPassword');
-			var password_confirm = $('#inputNewPasswordAgain');
-			var passwordvalid = false;
-			var passwordsmatch = false;
-			var successIcon = '<i class="fa fa-check"></i>';
-
-
-			function onPasswordChanged() {
-				passwordvalid = utils.isPasswordValid(password.val());
-				if (password.val().length < config.minimumPasswordLength) {
-					showError(password_notify, '[[user:change_password_error_length]]');
-				} else if (!passwordvalid) {
-					showError(password_notify, '[[user:change_password_error]]');
+		function onPasswordConfirmChanged() {
+			if(password.val()) {
+				if (password.val() !== password_confirm.val()) {
+					showError(password_confirm_notify, '[[user:change_password_error_match]]');
+					passwordsmatch = false;
 				} else {
-					showSuccess(password_notify, successIcon);
+					showSuccess(password_confirm_notify, successIcon);
+					passwordsmatch = true;
 				}
 			}
+		}
 
-			function onPasswordConfirmChanged() {
-				if(password.val()) {
-					if (password.val() !== password_confirm.val()) {
-						showError(password_confirm_notify, '[[user:change_password_error_match]]')
-						passwordsmatch = false;
-					} else {
-						showSuccess(password_confirm_notify, successIcon);
-						passwordsmatch = true;
+		password.on('blur', onPasswordChanged);
+		password_confirm.on('blur', onPasswordConfirmChanged);
+
+		$('#changePasswordBtn').on('click', function() {
+			if ((passwordvalid && passwordsmatch) || app.isAdmin) {
+				socket.emit('user.changePassword', {
+					'currentPassword': currentPassword.val(),
+					'newPassword': password.val(),
+					'uid': ajaxify.variables.get('theirid')
+				}, function(err) {
+					currentPassword.val('');
+					password.val('');
+					password_confirm.val('');
+					passwordsmatch = false;
+					passwordvalid = false;
+
+					if (err) {
+						return app.alertError(err.message);
 					}
-				}
-			}
-
-			password.on('blur', onPasswordChanged);
-			password_confirm.on('blur', onPasswordConfirmChanged);
-
-			$('#changePasswordBtn').on('click', function() {
-				if ((passwordvalid && passwordsmatch) || app.isAdmin) {
-					socket.emit('user.changePassword', {
-						'currentPassword': currentPassword.val(),
-						'newPassword': password.val(),
-						'uid': ajaxify.variables.get('theirid')
-					}, function(err) {
-						currentPassword.val('');
-						password.val('');
-						password_confirm.val('');
-						passwordsmatch = false;
-						passwordvalid = false;
-
-						if (err) {
-							return app.alertError(err.message);
-						}
-
-						app.alertSuccess('[[user:change_password_success]]');
-					});
-				}
-				return false;
-			});
-
-		}());
-	};
 
+					app.alertSuccess('[[user:change_password_success]]');
+				});
+			}
+			return false;
+		});
+	}
 
-	AccountEdit.changeUserPicture = function(type) {
-		var userData = {
+	function changeUserPicture(type) {
+		socket.emit('user.changePicture', {
 			type: type,
 			uid: ajaxify.variables.get('theirid')
-		};
-
-		socket.emit('user.changePicture', userData, function(err) {
+		}, function(err) {
 			if(err) {
 				app.alertError(err.message);
 			}
 		});
 	}
 
-	AccountEdit.updateImages = function() {
+	function updateImages() {
 		var currentPicture = $('#user-current-picture').attr('src');
 
 		if (gravatarPicture) {
 			$('#user-gravatar-picture').attr('src', gravatarPicture);
-			$('#gravatar-box').show();
-		} else {
-			$('#gravatar-box').hide();
 		}
 
 		if (uploadedPicture) {
 			$('#user-uploaded-picture').attr('src', uploadedPicture);
-			$('#uploaded-box').show();
-		} else {
-			$('#uploaded-box').hide();
 		}
 
+		$('#gravatar-box').toggle(!!gravatarPicture);
+		$('#uploaded-box').toggle(!!uploadedPicture);
 
-		if (currentPicture == gravatarPicture) {
-			$('#gravatar-box .fa-check').show();
-		} else {
-			$('#gravatar-box .fa-check').hide();
-		}
+		$('#gravatar-box .fa-check').toggle(currentPicture === gravatarPicture);
+		$('#uploaded-box .fa-check').toggle(currentPicture === uploadedPicture);
+	}
 
-		if (currentPicture == uploadedPicture) {
-			$('#uploaded-box .fa-check').show();
-		} else {
-			$('#uploaded-box .fa-check').hide();
+	function updateSignature() {
+		function getSignatureCharsLeft() {
+			return $('#inputSignature').length ? '(' + $('#inputSignature').val().length + '/' + config.maximumSignatureLength + ')' : '';
 		}
+
+		$('#signatureCharCountLeft').html(getSignatureCharsLeft());
+
+		$('#inputSignature').on('keyup change', function(ev) {
+			$('#signatureCharCountLeft').html(getSignatureCharsLeft());
+		});
+	}
+
+	function showError(element, msg) {
+		translator.translate(msg, function(msg) {
+			element.html(msg);
+			element.parent()
+				.removeClass('alert-success')
+				.addClass('alert-danger');
+			element.show();
+		});
+	}
+
+	function showSuccess(element, msg) {
+		translator.translate(msg, function(msg) {
+			element.html(msg);
+			element.parent()
+				.removeClass('alert-danger')
+				.addClass('alert-success');
+			element.show();
+		});
 	}
 
 	return AccountEdit;
diff --git a/public/src/forum/topic.js b/public/src/forum/topic.js
index 6df80606ea..ab9eb9d7ad 100644
--- a/public/src/forum/topic.js
+++ b/public/src/forum/topic.js
@@ -143,7 +143,7 @@ define(['forum/pagination', 'forum/topic/threadTools', 'forum/topic/postTools',
 
 
 		ajaxify.register_events([
-			'event:rep_up', 'event:rep_down', 'event:favourited', 'event:unfavourited', 'event:new_post', 'get_users_in_room',
+			'event:voted', 'event:favourited', 'event:new_post', 'get_users_in_room',
 			'event:topic_deleted', 'event:topic_restored', 'event:topic:locked',
 			'event:topic_unlocked', 'event:topic_pinned', 'event:topic_unpinned',
 			'event:topic_moved', 'event:post_edited', 'event:post_deleted', 'event:post_restored',
@@ -241,20 +241,12 @@ define(['forum/pagination', 'forum/topic/threadTools', 'forum/topic/postTools',
 			app.populateOnlineUsers();
 		});
 
-		socket.on('event:rep_up', function(data) {
-			adjust_rep(1, data.pid, data.uid);
-		});
-
-		socket.on('event:rep_down', function(data) {
-			adjust_rep(-1, data.pid, data.uid);
+		socket.on('event:voted', function(data) {
+			updatePostVotesAndUserReputation(data);
 		});
 
 		socket.on('event:favourited', function(data) {
-			adjust_favourites(1, data.pid, data.uid);
-		});
-
-		socket.on('event:unfavourited', function(data) {
-			adjust_favourites(-1, data.pid, data.uid);
+			updateFavouriteCount(data.post.pid, data.post.reputation);
 		});
 
 		socket.on('event:new_post', function(data) {
@@ -338,37 +330,31 @@ define(['forum/pagination', 'forum/topic/threadTools', 'forum/topic/postTools',
 			});
 		});
 
-		socket.on('posts.upvote', function(data) {
-			if (data && data.pid) {
-				$('li[data-pid="' + data.pid + '"] .upvote').addClass('btn-primary upvoted');
-			}
+		socket.on('posts.upvote', function(pid) {
+			toggleUpvoteDownvote(pid, true, false);
 		});
 
-		socket.on('posts.downvote', function(data) {
-			if (data && data.pid) {
-				$('li[data-pid="' + data.pid + '"] .downvote').addClass('btn-primary downvoted');
-			}
+		socket.on('posts.downvote', function(pid) {
+			toggleUpvoteDownvote(pid, false, true);
 		});
 
-		socket.on('posts.unvote', function(data) {
-			if (data && data.pid) {
-				var post = $('li[data-pid="' + data.pid + '"]');
-
-				post.find('.upvote').removeClass('btn-primary upvoted');
-				post.find('.downvote').removeClass('btn-primary downvoted');
-			}
+		socket.on('posts.unvote', function(pid) {
+			toggleUpvoteDownvote(pid, false, false);
 		});
 
-		socket.on('posts.favourite', function(data) {
-			if (data && data.pid) {
-				toggleFavourite(data.pid, true);
-			}
+		function toggleUpvoteDownvote(pid, upvote, downvote) {
+			var post = $('li[data-pid="' + pid + '"]');
+
+			post.find('.upvote').toggleClass('btn-primary upvoted', upvote);
+			post.find('.downvote').toggleClass('btn-primary downvoted', downvote);
+		}
+
+		socket.on('posts.favourite', function(pid) {
+			toggleFavourite(pid, true);
 		});
 
-		socket.on('posts.unfavourite', function(data) {
-			if (data && data.pid) {
-				toggleFavourite(data.pid, false);
-			}
+		socket.on('posts.unfavourite', function(pid) {
+			toggleFavourite(pid, false);
 		});
 
 		function toggleFavourite(pid, isFavourited) {
@@ -406,26 +392,16 @@ define(['forum/pagination', 'forum/topic/threadTools', 'forum/topic/postTools',
 			$('.thread_active_users [data-uid="' + uid + '"]').removeClass('replying');
 		});
 
-		function adjust_rep(value, pid, uid) {
-			var votes = $('li[data-pid="' + pid + '"] .votes'),
-				reputationElements = $('.reputation[data-uid="' + uid + '"]'),
-				currentVotes = parseInt(votes.attr('data-votes'), 10),
-				reputation = parseInt(reputationElements.attr('data-reputation'), 10);
-
-			currentVotes += value;
-			reputation += value;
+		function updatePostVotesAndUserReputation(data) {
+			var votes = $('li[data-pid="' + data.post.pid + '"] .votes'),
+				reputationElements = $('.reputation[data-uid="' + data.post.uid + '"]');
 
-			votes.html(currentVotes).attr('data-votes', currentVotes);
-			reputationElements.html(reputation).attr('data-reputation', reputation);
+			votes.html(data.post.votes).attr('data-votes', data.post.votes);
+			reputationElements.html(data.user.reputation).attr('data-reputation', data.user.reputation);
 		}
 
-		function adjust_favourites(value, pid, uid) {
-			var favourites = $('li[data-pid="' + pid + '"] .favouriteCount'),
-				currentFavourites = parseInt(favourites.attr('data-favourites'), 10);
-
-			currentFavourites += value;
-
-			favourites.html(currentFavourites).attr('data-favourites', currentFavourites);
+		function updateFavouriteCount(pid, value) {
+			$('li[data-pid="' + pid + '"] .favouriteCount').html(value).attr('data-favourites', value);
 		}
 
 		function set_locked_state(locked, alert) {
diff --git a/public/src/forum/topic/postTools.js b/public/src/forum/topic/postTools.js
index 36243dad79..bbe9a109ca 100644
--- a/public/src/forum/topic/postTools.js
+++ b/public/src/forum/topic/postTools.js
@@ -111,6 +111,10 @@ define(['composer', 'share'], function(composer, share) {
 		socket.emit(method, {
 			pid: pid,
 			room_id: app.currentRoom
+		}, function(err) {
+			if (err) {
+				app.alertError(err.message);
+			}
 		});
 
 		return false;
@@ -123,6 +127,10 @@ define(['composer', 'share'], function(composer, share) {
 		socket.emit(currentState ? 'posts.unvote' : method , {
 			pid: post.attr('data-pid'),
 			room_id: app.currentRoom
+		}, function(err) {
+			if (err) {
+				app.alertError(err.message);
+			}
 		});
 
 		return false;
diff --git a/src/categories.js b/src/categories.js
index 809c77c8e5..409e8e7aeb 100644
--- a/src/categories.js
+++ b/src/categories.js
@@ -64,7 +64,7 @@ var db = require('./database'),
 	Categories.getCategoryById = function(cid, start, end, uid, callback) {
 		Categories.getCategoryData(cid, function(err, category) {
 			if(err || !category) {
-				return callback(err || new Error('category-not-found [' + cid + ']'));
+				return callback(err || new Error('[[error:invalid-cid]]'));
 			}
 
 			if(parseInt(uid, 10)) {
@@ -278,7 +278,7 @@ var db = require('./database'),
 	Categories.getCategories = function(cids, uid, callback) {
 
 		if (!Array.isArray(cids) || cids.length === 0) {
-			return callback(new Error('invalid-cids'));
+			return callback(new Error('[[error:invalid-cid]]'));
 		}
 
 		async.parallel({
diff --git a/src/controllers/accounts.js b/src/controllers/accounts.js
index 369449e9aa..518c170445 100644
--- a/src/controllers/accounts.js
+++ b/src/controllers/accounts.js
@@ -33,7 +33,7 @@ function userNotAllowed(res) {
 function getUserDataByUserSlug(userslug, callerUID, callback) {
 	user.getUidByUserslug(userslug, function(err, uid) {
 		if(err || !uid) {
-			return callback(err || new Error('invalid-user'));
+			return callback(err || new Error('[[error:invalid-uid]]'));
 		}
 
 		async.parallel({
@@ -54,7 +54,7 @@ function getUserDataByUserSlug(userslug, callerUID, callback) {
 			}
 		}, function(err, results) {
 			if(err || !results.userData) {
-				return callback(err || new Error('invalid-user'));
+				return callback(err || new Error('[[error:invalid-uid]]'));
 			}
 
 			var userData = results.userData;
diff --git a/src/controllers/categories.js b/src/controllers/categories.js
index 93112a2524..f30dcc13b3 100644
--- a/src/controllers/categories.js
+++ b/src/controllers/categories.js
@@ -70,7 +70,7 @@ categoriesController.get = function(req, res, next) {
 			categoryTools.privileges(cid, uid, function(err, categoryPrivileges) {
 				if (!err) {
 					if (!categoryPrivileges.read) {
-						next(new Error('not-enough-privileges'));
+						next(new Error('[[error:no-privileges]]'));
 					} else {
 						next(null, categoryPrivileges);
 					}
@@ -95,7 +95,7 @@ categoriesController.get = function(req, res, next) {
 
 					if (categoryData) {
 						if (parseInt(categoryData.disabled, 10) === 1) {
-							return next(new Error('Category disabled'), null);
+							return next(new Error('[[error:category-disabled]]'));
 						}
 					}
 
diff --git a/src/controllers/topics.js b/src/controllers/topics.js
index aa5488295d..5fdf606d47 100644
--- a/src/controllers/topics.js
+++ b/src/controllers/topics.js
@@ -26,7 +26,7 @@ topicsController.get = function(req, res, next) {
 				}
 
 				if (!userPrivileges.read) {
-					return next(new Error('not-enough-privileges'));
+					return next(new Error('[[error:no-privileges]]'));
 				}
 
 				privileges = userPrivileges;
@@ -45,7 +45,7 @@ topicsController.get = function(req, res, next) {
 				topics.getTopicWithPosts(tid, uid, start, end, function (err, topicData) {
 					if (topicData) {
 						if (parseInt(topicData.deleted, 10) === 1 && parseInt(topicData.expose_tools, 10) === 0) {
-							return next(new Error('Topic deleted'));
+							return next(new Error('[[error:no-topic]]'));
 						}
 						topicData.currentPage = page;
 					}
diff --git a/src/database/mongo.js b/src/database/mongo.js
index aaf161ee42..e792d41b1a 100644
--- a/src/database/mongo.js
+++ b/src/database/mongo.js
@@ -538,21 +538,15 @@
 		getSortedSetRange(key, start, stop, -1, callback);
 	};
 
-	module.getSortedSetRangeByScore = function(args, callback) {
-		getSortedSetRangeByScore(args, 1, callback);
+	module.getSortedSetRangeByScore = function(key, start, count, min, max, callback) {
+		getSortedSetRangeByScore(key, start, count, min, max, 1, callback);
 	};
 
-	module.getSortedSetRevRangeByScore = function(args, callback) {
-		getSortedSetRangeByScore(args, -1, callback);
+	module.getSortedSetRevRangeByScore = function(key, start, count, max, min, callback) {
+		getSortedSetRangeByScore(key, start, count, min, max, -1, callback);
 	};
 
-	function getSortedSetRangeByScore(args, sort, callback) {
-		var key = args[0],
-			max = (args[1] === '+inf') ? Number.MAX_VALUE : args[1],
-			min = args[2],
-			start = args[4],
-			count = args[5];
-
+	function getSortedSetRangeByScore(key, start, count, min, max, sort, callback) {
 		if(parseInt(count, 10) === -1) {
 			count = 0;
 		}
diff --git a/src/database/redis.js b/src/database/redis.js
index 90daed3e74..f14863a83b 100644
--- a/src/database/redis.js
+++ b/src/database/redis.js
@@ -359,12 +359,12 @@
 		redisClient.zrevrange(key, start, stop, callback);
 	};
 
-	module.getSortedSetRangeByScore = function(args, callback) {
-		redisClient.zrangebyscore(args, callback);
+	module.getSortedSetRangeByScore = function(key, start, count, min, max, callback) {
+		redisClient.zrangebyscore([key, min, max, 'LIMIT', start, count], callback);
 	};
 
-	module.getSortedSetRevRangeByScore = function(args, callback) {
-		redisClient.zrevrangebyscore(args, callback);
+	module.getSortedSetRevRangeByScore = function(key, start, count, max, min, callback) {
+		redisClient.zrevrangebyscore([key, max, min, 'LIMIT', start, count], callback);
 	};
 
 	module.sortedSetCount = function(key, min, max, callback) {
diff --git a/src/favourites.js b/src/favourites.js
index 67c52b8d8c..30ef254cfb 100644
--- a/src/favourites.js
+++ b/src/favourites.js
@@ -8,67 +8,50 @@ var async = require('async'),
 (function (Favourites) {
 	"use strict";
 
-	function vote(type, unvote, pid, room_id, uid, socket, callback) {
-		var	websockets = require('./socket.io');
+	function vote(type, unvote, pid, uid, callback) {
+		uid = parseInt(uid, 10);
 
 		if (uid === 0) {
-			return socket.emit('event:alert', {
-				alert_id: 'post_vote',
-				title: '[[topic:vote.not_logged_in.title]]',
-				message: '[[topic:vote.not_logged_in.message]]',
-				type: 'danger',
-				timeout: 5000
-			});
+			return callback(new Error('[[error:not-logged-in]]'));
 		}
 
-		posts.getPostFields(pid, ['uid', 'timestamp'], function (err, postData) {
-			if (uid === parseInt(postData.uid, 10)) {
-				socket.emit('event:alert', {
-					alert_id: 'post_vote',
-					title: '[[topic:vote.cant_vote_self.title]]',
-					message: '[[topic:vote.cant_vote_self.message]]',
-					type: 'danger',
-					timeout: 5000
-				});
-
-				if (callback) {
-					callback(false);
-				}
+		posts.getPostFields(pid, ['pid', 'uid', 'timestamp'], function (err, postData) {
+			if (err) {
+				return callback(err);
+			}
 
-				return false;
+			if (uid === parseInt(postData.uid, 10)) {
+				return callback(new Error('[[error:cant-vote-self-post]]'));
 			}
 
-			if(type === 'upvote' || !unvote) {
-				db.sortedSetAdd('uid: ' + uid + ':upvote', postData.timestamp, pid);
+			if(type === 'upvote' && !unvote) {
+				db.sortedSetAdd('uid:' + uid + ':upvote', postData.timestamp, pid);
 			} else {
-				db.sortedSetRemove('uid: ' + uid + ':upvote', pid);
+				db.sortedSetRemove('uid:' + uid + ':upvote', pid);
 			}
 
 			if(type === 'upvote' || unvote) {
-				db.sortedSetRemove('uid: ' + uid + ':downvote', pid);
+				db.sortedSetRemove('uid:' + uid + ':downvote', pid);
 			} else {
-				db.sortedSetAdd('uid: ' + uid + ':downvote', postData.timestamp, pid);
+				db.sortedSetAdd('uid:' + uid + ':downvote', postData.timestamp, pid);
 			}
 
 			user[type === 'upvote' ? 'incrementUserFieldBy' : 'decrementUserFieldBy'](postData.uid, 'reputation', 1, function (err, newreputation) {
+				if (err) {
+					return callback(err);
+				}
+
 				db.sortedSetAdd('users:reputation', newreputation, postData.uid);
-			});
 
-			if (room_id) {
-				websockets.in(room_id).emit('event:' + (type === 'upvote' ? 'rep_up' : 'rep_down'), {
-					uid: postData.uid,
-					pid: pid
+				adjustPostVotes(pid, uid, type, unvote, function(err, votes) {
+					postData.votes = votes;
+					callback(err, {
+						user: {
+							reputation: newreputation
+						},
+						post: postData
+					});
 				});
-			}
-
-			socket.emit('posts.' + (unvote ? 'unvote' : type), {
-				pid: pid
-			});
-
-			adjustPostVotes(pid, uid, type, unvote, function() {
-				if (callback) {
-					callback();
-				}
 			});
 		});
 	}
@@ -79,21 +62,19 @@ var async = require('async'),
 		async.series([
 			function(next) {
 				if (unvote) {
-					db.setRemove('pid:' + pid + ':' + type, uid, function(err) {
-						next(err);
-					});
+					db.setRemove('pid:' + pid + ':' + type, uid, next);
 				} else {
-					db.setAdd('pid:' + pid + ':' + type, uid, function(err) {
-						next(err);
-					});
+					db.setAdd('pid:' + pid + ':' + type, uid, next);
 				}
 			},
 			function(next) {
-				db.setRemove('pid:' + pid + ':' + notType, uid, function(err) {
-					next(err);
-				});
+				db.setRemove('pid:' + pid + ':' + notType, uid, next);
 			}
 		], function(err) {
+			if (err) {
+				return callback(err);
+			}
+
 			async.parallel({
 				upvotes: function(next) {
 					db.setCount('pid:' + pid + ':upvote', next);
@@ -102,48 +83,46 @@ var async = require('async'),
 					db.setCount('pid:' + pid + ':downvote', next);
 				}
 			}, function(err, results) {
-				posts.setPostField(pid, 'votes', parseInt(results.upvotes, 10) - parseInt(results.downvotes, 10));
+				if (err) {
+					return callback(err);
+				}
+				var voteCount = parseInt(results.upvotes, 10) - parseInt(results.downvotes, 10);
+				posts.setPostField(pid, 'votes', voteCount, function(err) {
+					callback(err, voteCount);
+				});
 			});
-
-			if (callback) {
-				callback();
-			}
 		});
 	}
 
-	Favourites.upvote = function(pid, room_id, uid, socket) {
-		toggleVote('upvote', pid, room_id, uid, socket);
+	Favourites.upvote = function(pid, uid, callback) {
+		toggleVote('upvote', pid, uid, callback);
 	};
 
-	Favourites.downvote = function(pid, room_id, uid, socket) {
-		toggleVote('downvote', pid, room_id, uid, socket);
+	Favourites.downvote = function(pid, uid, callback) {
+		toggleVote('downvote', pid, uid, callback);
 	};
 
-	function toggleVote(type, pid, room_id, uid, socket) {
-		Favourites.unvote(pid, room_id, uid, socket, function(err) {
-			vote(type, false, pid, room_id, uid, socket);
+	function toggleVote(type, pid, uid, callback) {
+		Favourites.unvote(pid, uid, function(err) {
+			if (err) {
+				return callback(err);
+			}
+
+			vote(type, false, pid, uid, callback);
 		});
 	}
 
-	Favourites.unvote = function(pid, room_id, uid, socket, callback) {
-		var	websockets = require('./socket.io');
-
+	Favourites.unvote = function(pid, uid, callback) {
 		Favourites.hasVoted(pid, uid, function(err, voteStatus) {
-			if (voteStatus.upvoted || voteStatus.downvoted) {
-				socket.emit('posts.unvote', {
-					pid: pid
-				});
-
-				return vote(voteStatus.upvoted ? 'downvote' : 'upvote', true, pid, room_id, uid, socket, function() {
-					if (callback) {
-						callback(err);
-					}
-				});
+			if (err) {
+				return callback(err);
 			}
 
-			if (callback) {
-				callback(err);
+			if (!voteStatus || (!voteStatus.upvoted && !voteStatus.downvoted)) {
+				return callback();
 			}
+
+			vote(voteStatus.upvoted ? 'downvote' : 'upvote', true, pid, uid, callback);
 		});
 	};
 
@@ -164,77 +143,64 @@ var async = require('async'),
 		}, callback);
 	};
 
-	Favourites.favourite = function (pid, room_id, uid, socket) {
-		var	websockets = require('./socket.io');
+	Favourites.favourite = function (pid, uid, callback) {
+		toggleFavourite('favourite', pid, uid, callback);
+	};
+
+	Favourites.unfavourite = function(pid, uid, callback) {
+		toggleFavourite('unfavourite', pid, uid, callback);
+	};
 
+	function toggleFavourite(type, pid, uid, callback) {
 		if (uid === 0) {
-			return socket.emit('event:alert', {
-				alert_id: 'post_favourite',
-				title: '[[topic:favourites.not_logged_in.title]]',
-				message: '[[topic:favourites.not_logged_in.message]]',
-				type: 'danger',
-				timeout: 5000
-			});
+			return callback(new Error('[[error:not-logged-in]]'));
 		}
 
-		posts.getPostFields(pid, ['uid', 'timestamp'], function (err, postData) {
-			Favourites.hasFavourited(pid, uid, function (err, hasFavourited) {
-				if (!hasFavourited) {
-					db.sortedSetAdd('uid:' + uid + ':favourites', postData.timestamp, pid);
-					db.setAdd('pid:' + pid + ':users_favourited', uid, function(err) {
-						db.setCount('pid:' + pid + ':users_favourited', function(err, count) {
-							posts.setPostField(pid, 'reputation', count);
-						});
-					});
-
-					if (room_id) {
-						websockets.in(room_id).emit('event:favourited', {
-							uid: uid !== postData.uid ? postData.uid : 0,
-							pid: pid
-						});
-					}
+		posts.getPostFields(pid, ['pid', 'uid', 'timestamp'], function (err, postData) {
+			if (err) {
+				return callback(err);
+			}
 
-					socket.emit('posts.favourite', {
-						pid: pid
-					});
+			Favourites.hasFavourited(pid, uid, function (err, hasFavourited) {
+				if (err) {
+					return callback(err);
 				}
-			});
-		});
-	};
 
-	Favourites.unfavourite = function(pid, room_id, uid, socket) {
-		var	websockets = require('./socket.io');
+				if (type === 'favourite' && hasFavourited) {
+					return callback(new Error('[[error:already-favourited]]'));
+				}
 
-		if (uid === 0) {
-			return;
-		}
+				if (type === 'unfavourite' && !hasFavourited) {
+					return callback(new Error('[[error:alrady-unfavourited]]'));
+				}
 
-		posts.getPostField(pid, 'uid', function (err, uid_of_poster) {
-			Favourites.hasFavourited(pid, uid, function (err, hasFavourited) {
-				if (hasFavourited) {
+				if (type === 'favourite') {
+					db.sortedSetAdd('uid:' + uid + ':favourites', postData.timestamp, pid);
+				} else {
 					db.sortedSetRemove('uid:' + uid + ':favourites', pid);
-					db.setRemove('pid:' + pid + ':users_favourited', uid, function(err) {
-						db.setCount('pid:' + pid + ':users_favourited', function(err, count) {
-							posts.setPostField(pid, 'reputation', count);
-						});
-					});
+				}
 
-					if (room_id) {
-						websockets.in(room_id).emit('event:unfavourited', {
-							uid: uid !== uid_of_poster ? uid_of_poster : 0,
-							pid: pid
-						});
+
+				db[type === 'favourite' ? 'setAdd' : 'setRemove']('pid:' + pid + ':users_favourited', uid, function(err) {
+					if (err) {
+						return callback(err);
 					}
 
-					if (socket) {
-						socket.emit('posts.unfavourite', {
-							pid: pid
+					db.setCount('pid:' + pid + ':users_favourited', function(err, count) {
+						if (err) {
+							return callback(err);
+						}
+						postData.reputation = count;
+						posts.setPostField(pid, 'reputation', count, function(err) {
+							callback(err, {
+								post: postData
+							});
 						});
-					}
-				}
+					});
+				});
 			});
 		});
-	};
+	}
 
 	Favourites.hasFavourited = function(pid, uid, callback) {
 		db.isSetMember('pid:' + pid + ':users_favourited', uid, callback);
diff --git a/src/groups.js b/src/groups.js
index 24894ff7fb..6501b3dc66 100644
--- a/src/groups.js
+++ b/src/groups.js
@@ -137,7 +137,7 @@
 
 	Groups.create = function(name, description, callback) {
 		if (name.length === 0) {
-			return callback(new Error('name-too-short'));
+			return callback(new Error('[[error:group-name-too-short]]'));
 		}
 
 		if (name === 'administrators' || name === 'registered-users') {
@@ -145,28 +145,32 @@
 		}
 
 		Groups.exists(name, function (err, exists) {
-			if (!exists) {
-				var groupData = {
-					name: name,
-					description: description,
-					deleted: '0',
-					hidden: '0',
-					system: system ? '1' : '0'
-				};
+			if (err) {
+				return callback(err);
+			}
 
-				async.parallel([
-					function(next) {
-						db.setAdd('groups', name, next);
-					},
-					function(next) {
-						db.setObject('group:' + name, groupData, function(err) {
-							Groups.get(name, {}, next);
-						});
-					}
-				], callback);
-			} else {
-				callback(new Error('group-exists'));
+			if (exists) {
+				return callback(new Error('[[error:group-already-exists]]'));
 			}
+
+			var groupData = {
+				name: name,
+				description: description,
+				deleted: '0',
+				hidden: '0',
+				system: system ? '1' : '0'
+			};
+
+			async.parallel([
+				function(next) {
+					db.setAdd('groups', name, next);
+				},
+				function(next) {
+					db.setObject('group:' + name, groupData, function(err) {
+						Groups.get(name, {}, next);
+					});
+				}
+			], callback);
 		});
 	};
 
@@ -184,12 +188,12 @@
 					db.setObject('group:' + groupName, values, callback);
 				} else {
 					if (callback) {
-						callback(new Error('name-change-not-allowed'));
+						callback(new Error('[[error:group-name-change-not-allowed]]'));
 					}
 				}
 			} else {
 				if (callback) {
-					callback(new Error('gid-not-found'));
+					callback(new Error('[[error:no-group]]'));
 				}
 			}
 		});
diff --git a/src/postTools.js b/src/postTools.js
index 7ecf7387ba..3cb73e2282 100644
--- a/src/postTools.js
+++ b/src/postTools.js
@@ -120,7 +120,7 @@ var winston = require('winston'),
 
 		PostTools.privileges(pid, uid, function(err, privileges) {
 			if (err || !privileges.editable) {
-				return callback(err || new Error('not-privileges-to-edit'));
+				return callback(err || new Error('[[error:no-privileges]]'));
 			}
 
 			posts.getPostData(pid, function(err, postData) {
@@ -155,15 +155,15 @@ var winston = require('winston'),
 			},
 			function(deleted, next) {
 				if(parseInt(deleted, 10) === 1 && isDelete) {
-					return next(new Error('Post already deleted'));
+					return next(new Error('[[error:post-already-deleted]]'));
 				} else if(parseInt(deleted, 10) !== 1 && !isDelete) {
-					return next(new Error('Post already restored'));
+					return next(new Error('[[error:post-already-restored]]'));
 				}
 				PostTools.privileges(pid, uid, next);
 			},
 			function(privileges, next) {
 				if (!privileges || !privileges.editable) {
-					return next(new Error('no privileges'));
+					return next(new Error('[[error:no-privileges]]'));
 				}
 				next();
 			}
diff --git a/src/posts.js b/src/posts.js
index 08603611ea..d35cd12e5d 100644
--- a/src/posts.js
+++ b/src/posts.js
@@ -30,7 +30,7 @@ var db = require('./database'),
 			toPid = data.toPid;
 
 		if (uid === null) {
-			return callback(new Error('invalid-user'));
+			return callback(new Error('[[error:invalid-uid]]'));
 		}
 
 		var timestamp = Date.now(),
@@ -210,7 +210,7 @@ var db = require('./database'),
 
 		var count = parseInt(stop, 10) === -1 ? stop : stop - start + 1;
 
-		db.getSortedSetRevRangeByScore(['posts:pid', '+inf', Date.now() - since, 'LIMIT', start, count], function(err, pids) {
+		db.getSortedSetRevRangeByScore('posts:pid', start, count, Infinity, Date.now() - since, function(err, pids) {
 			if(err) {
 				return callback(err);
 			}
@@ -420,7 +420,7 @@ var db = require('./database'),
 
 			topics.getTopicField(tid, 'cid', function(err, cid) {
 				if(err || !cid) {
-					return callback(err || new Error('invalid-category-id'));
+					return callback(err || new Error('[[error:invalid-cid]]'));
 				}
 				callback(null, cid);
 			});
@@ -458,7 +458,7 @@ var db = require('./database'),
 
 	Posts.getPidPage = function(pid, uid, callback) {
 		if(!pid) {
-			return callback(new Error('invalid-pid'));
+			return callback(new Error('[[error:invalid-pid]]'));
 		}
 
 		var index = 0;
diff --git a/src/routes/api.js b/src/routes/api.js
index 909f142863..54c72e3e45 100644
--- a/src/routes/api.js
+++ b/src/routes/api.js
@@ -63,14 +63,14 @@ function uploadPost(req, res, next) {
 function uploadThumb(req, res, next) {
 	if (!meta.config.allowTopicsThumbnail) {
 		deleteTempFiles(req.files.files);
-		return callback(new Error('Topic Thumbnails are disabled!'));
+		return callback(new Error('[[error:topic-thumbnails-are-disabled]]'));
 	}
 
 	upload(req, res, function(file, next) {
 		if(file.type.match(/image./)) {
 			uploadImage(file, next);
 		} else {
-			next(new Error('Invalid File'));
+			next(new Error('[[error:invalid-file]]'));
 		}
 	}, next);
 }
@@ -85,7 +85,7 @@ function uploadImage(image, callback) {
 		if (meta.config.allowFileUploads) {
 			uploadFile(image, callback);
 		} else {
-			callback(new Error('Uploads are disabled!'));
+			callback(new Error('[[error:uploads-are-disabled]]'));
 		}
 	}
 }
@@ -97,15 +97,15 @@ function uploadFile(file, callback) {
 	} else {
 
 		if(!meta.config.allowFileUploads) {
-			return callback(new Error('File uploads are not allowed'));
+			return callback(new Error('[[error:uploads-are-disabled]]'));
 		}
 
 		if(!file) {
-			return callback(new Error('invalid file'));
+			return callback(new Error('[[error:invalid-file]]'));
 		}
 
 		if(file.size > parseInt(meta.config.maximumFileSize, 10) * 1024) {
-			return callback(new Error('File too big'));
+			return callback(new Error('[[error:file-too-big, ' + meta.config.maximumFileSize + ']]'));
 		}
 
 		var filename = 'upload-' + utils.generateUUID() + path.extname(file.name);
diff --git a/src/routes/authentication.js b/src/routes/authentication.js
index a2d9f66e44..2728f58116 100644
--- a/src/routes/authentication.js
+++ b/src/routes/authentication.js
@@ -158,7 +158,7 @@
 
 	Auth.login = function(username, password, next) {
 		if (!username || !password) {
-			return next(new Error('invalid-user'));
+			return next(new Error('[[error:invalid-user-data]]'));
 		}
 
 		var userslug = utils.slugify(username);
@@ -170,7 +170,7 @@
 
 			if(!uid) {
 				// Even if a user doesn't exist, compare passwords anyway, so we don't immediately return
-				return next(null, false, 'user doesn\'t exist');
+				return next(null, false, '[[error:no-user]]');
 			}
 
 			user.getUserFields(uid, ['password', 'banned'], function(err, userData) {
@@ -179,11 +179,11 @@
 				}
 
 				if (!userData || !userData.password) {
-					return next(new Error('invalid userdata or password'));
+					return next(new Error('[[error:invalid-user-data]]'));
 				}
 
 				if (userData.banned && parseInt(userData.banned, 10) === 1) {
-					return next(null, false, 'User banned');
+					return next(null, false, '[[error:user-banned]]');
 				}
 
 				bcrypt.compare(password, userData.password, function(err, res) {
@@ -192,12 +192,12 @@
 					}
 
 					if (!res) {
-						return next(null, false, 'invalid-password');
+						return next(null, false, '[[error:invalid-password]]');
 					}
 
 					next(null, {
 						uid: uid
-					}, 'Authentication successful');
+					}, '[[success:authentication-successful]]');
 				});
 			});
 		});
diff --git a/src/socket.io/admin.js b/src/socket.io/admin.js
index 5888fd636d..dc0b248ae9 100644
--- a/src/socket.io/admin.js
+++ b/src/socket.io/admin.js
@@ -79,7 +79,7 @@ SocketAdmin.user.removeAdmin = function(socket, theirid, callback) {
 
 SocketAdmin.user.createUser = function(socket, userData, callback) {
 	if (!userData) {
-		return callback(new Error('invalid data'));
+		return callback(new Error('[[error:invalid-data]]'));
 	}
 	user.create(userData, callback);
 };
@@ -87,7 +87,7 @@ SocketAdmin.user.createUser = function(socket, userData, callback) {
 SocketAdmin.user.banUser = function(socket, theirid, callback) {
 	user.isAdministrator(theirid, function(err, isAdmin) {
 		if (err || isAdmin) {
-			return callback(err || new Error('You can\'t ban other admins!'));
+			return callback(err || new Error('[[error:cant-ban-other-admins]]'));
 		}
 
 		user.ban(theirid, function(err) {
@@ -137,6 +137,10 @@ SocketAdmin.user.search = function(socket, username, callback) {
 			});
 		}
 
+		if (err) {
+			return callback(err);
+		}
+
 		async.each(data.users, isAdmin, function(err) {
 			callback(err, data);
 		});
@@ -146,7 +150,7 @@ SocketAdmin.user.search = function(socket, username, callback) {
 /* Categories */
 SocketAdmin.categories.create = function(socket, data, callback) {
 	if(!data) {
-		return callback(new Error('invalid data'));
+		return callback(new Error('[[error:invalid-data]]'));
 	}
 
 	categories.create(data, callback);
@@ -154,7 +158,7 @@ SocketAdmin.categories.create = function(socket, data, callback) {
 
 SocketAdmin.categories.update = function(socket, data, callback) {
 	if(!data) {
-		return callback(new Error('invalid data'));
+		return callback(new Error('[[error:invalid-data]]'));
 	}
 
 	categories.update(data, callback);
@@ -162,7 +166,7 @@ SocketAdmin.categories.update = function(socket, data, callback) {
 
 SocketAdmin.categories.search = function(socket, data, callback) {
 	if(!data) {
-		return callback(new Error('invalid data'));
+		return callback(new Error('[[error:invalid-data]]'));
 	}
 
 	var	username = data.username,
@@ -184,7 +188,7 @@ SocketAdmin.categories.search = function(socket, data, callback) {
 
 SocketAdmin.categories.setPrivilege = function(socket, data, callback) {
 	if(!data) {
-		return callback(new Error('invalid data'));
+		return callback(new Error('[[error:invalid-data]]'));
 	}
 
 	var	cid = data.cid,
@@ -248,15 +252,14 @@ SocketAdmin.categories.getPrivilegeSettings = function(socket, cid, callback) {
 		callback(null, {
 			"+r": data['+r'].members,
 			"+w": data['+w'].members,
-			"mods": data['mods'].members
+			"mods": data.mods.members
 		});
 	});
 };
 
 SocketAdmin.categories.setGroupPrivilege = function(socket, data, callback) {
-
 	if(!data) {
-		return callback(new Error('invalid data'));
+		return callback(new Error('[[error:invalid-data]]'));
 	}
 
 	if (data.set) {
@@ -295,7 +298,7 @@ SocketAdmin.themes.getInstalled = function(socket, data, callback) {
 
 SocketAdmin.themes.set = function(socket, data, callback) {
 	if(!data) {
-		return callback(new Error('invalid data'));
+		return callback(new Error('[[error:invalid-data]]'));
 	}
 
 	widgets.reset(function(err) {
@@ -315,7 +318,7 @@ SocketAdmin.plugins.toggle = function(socket, plugin_id) {
 
 SocketAdmin.widgets.set = function(socket, data, callback) {
 	if(!data) {
-		return callback(new Error('invalid data'));
+		return callback(new Error('[[error:invalid-data]]'));
 	}
 
 	widgets.setArea(data, callback);
@@ -328,7 +331,7 @@ SocketAdmin.config.get = function(socket, data, callback) {
 
 SocketAdmin.config.set = function(socket, data, callback) {
 	if(!data) {
-		return callback(new Error('invalid data'));
+		return callback(new Error('[[error:invalid-data]]'));
 	}
 
 	meta.configs.set(data.key, data.value, function(err) {
@@ -354,7 +357,7 @@ SocketAdmin.config.remove = function(socket, key) {
 /* Groups */
 SocketAdmin.groups.create = function(socket, data, callback) {
 	if(!data) {
-		return callback(new Error('invalid data'));
+		return callback(new Error('[[error:invalid-data]]'));
 	}
 
 	groups.create(data.name, data.description, function(err, groupObj) {
@@ -376,7 +379,7 @@ SocketAdmin.groups.get = function(socket, groupName, callback) {
 
 SocketAdmin.groups.join = function(socket, data, callback) {
 	if(!data) {
-		return callback(new Error('invalid data'));
+		return callback(new Error('[[error:invalid-data]]'));
 	}
 
 	groups.join(data.groupName, data.uid, callback);
@@ -384,7 +387,7 @@ SocketAdmin.groups.join = function(socket, data, callback) {
 
 SocketAdmin.groups.leave = function(socket, data, callback) {
 	if(!data) {
-		return callback(new Error('invalid data'));
+		return callback(new Error('[[error:invalid-data]]'));
 	}
 
 	groups.leave(data.groupName, data.uid, callback);
@@ -392,7 +395,7 @@ SocketAdmin.groups.leave = function(socket, data, callback) {
 
 SocketAdmin.groups.update = function(socket, data, callback) {
 	if(!data) {
-		return callback(new Error('invalid data'));
+		return callback(new Error('[[error:invalid-data]]'));
 	}
 
 	groups.update(data.groupName, data.values, function(err) {
diff --git a/src/socket.io/categories.js b/src/socket.io/categories.js
index 0dd546ef16..eaa6eb0966 100644
--- a/src/socket.io/categories.js
+++ b/src/socket.io/categories.js
@@ -13,7 +13,7 @@ SocketCategories.getRecentReplies = function(socket, cid, callback) {
 			return callback(err);
 		}
 
-		if (privileges && !privileges.read) {
+		if (!privileges || !privileges.read) {
 			return callback(null, []);
 		}
 
@@ -27,7 +27,7 @@ SocketCategories.get = function(socket, data, callback) {
 
 SocketCategories.loadMore = function(socket, data, callback) {
 	if(!data) {
-		return callback(new Error('invalid data'));
+		return callback(new Error('[[error:invalid-data]]'));
 	}
 
 	user.getSettings(socket.uid, function(err, settings) {
diff --git a/src/socket.io/meta.js b/src/socket.io/meta.js
index 08415ab260..e2733e33a7 100644
--- a/src/socket.io/meta.js
+++ b/src/socket.io/meta.js
@@ -37,7 +37,7 @@ SocketMeta.buildTitle = function(socket, text, callback) {
 
 SocketMeta.updateHeader = function(socket, data, callback) {
 	if(!data) {
-		return callback(new Error('invalid data'));
+		return callback(new Error('[[error:invalid-data]]'));
 	}
 
 	if (socket.uid) {
@@ -76,7 +76,7 @@ SocketMeta.getUsageStats = function(socket, data, callback) {
 
 SocketMeta.rooms.enter = function(socket, data) {
 	if(!data) {
-		return callback(new Error('invalid data'));
+		return callback(new Error('[[error:invalid-data]]'));
 	}
 
 	if (data.leave !== null) {
diff --git a/src/socket.io/modules.js b/src/socket.io/modules.js
index 115bcf17b8..7dea871b32 100644
--- a/src/socket.io/modules.js
+++ b/src/socket.io/modules.js
@@ -49,35 +49,36 @@ var	stopTracking = function(replyObj) {
 	};
 
 SocketModules.composer.push = function(socket, pid, callback) {
-	if (socket.uid || parseInt(meta.config.allowGuestPosting, 10)) {
-		if (parseInt(pid, 10) > 0) {
-
-			async.parallel([
-				function(next) {
-					posts.getPostFields(pid, ['content'], next);
-				},
-				function(next) {
-					topics.getTopicDataByPid(pid, next);
-				},
-				function(next) {
-					posts.getPidIndex(pid, next);
-				}
-			], function(err, results) {
-				if(err) {
-					return callback(err);
-				}
-				callback(null, {
-					pid: pid,
-					body: results[0].content,
-					title: results[1].title,
-					topic_thumb: results[1].thumb,
-					index: results[2]
-				});
-			});
-		}
-	} else {
-		callback(new Error('no-uid'));
+	if(!socket.uid && parseInt(meta.config.allowGuestPosting, 10) !== 1) {
+		return callback(new Error('[[error:not-logged-in]]'));
 	}
+
+	posts.getPostFields(pid, ['content'], function(err, postData) {
+		if(err || (!postData && !postData.content)) {
+			return callback(err || new Error('[[error:invalid-pid]]'));
+		}
+
+		async.parallel({
+			topic: function(next) {
+				topics.getTopicDataByPid(pid, next);
+			},
+			index: function(next) {
+				posts.getPidIndex(pid, next);
+			}
+		}, function(err, results) {
+			if(err) {
+				return callback(err);
+			}
+
+			callback(null, {
+				pid: pid,
+				body: postData.content,
+				title: results.topic.title,
+				topic_thumb: results.topic.thumb,
+				index: results.index
+			});
+		});
+	});
 };
 
 SocketModules.composer.editCheck = function(socket, pid, callback) {
@@ -150,7 +151,7 @@ SocketModules.composer.getUsersByTid = function(socket, tid, callback) {
 
 SocketModules.chats.get = function(socket, data, callback) {
 	if(!data) {
-		return callback(new Error('invalid data'));
+		return callback(new Error('[[error:invalid-data]]'));
 	}
 
 	Messaging.getMessages(socket.uid, data.touid, false, callback);
@@ -158,7 +159,7 @@ SocketModules.chats.get = function(socket, data, callback) {
 
 SocketModules.chats.send = function(socket, data, callback) {
 	if(!data) {
-		return callback(new Error('invalid data'));
+		return callback(new Error('[[error:invalid-data]]'));
 	}
 
 	var touid = data.touid;
diff --git a/src/socket.io/posts.js b/src/socket.io/posts.js
index 71bc1ab030..8383f58f82 100644
--- a/src/socket.io/posts.js
+++ b/src/socket.io/posts.js
@@ -11,7 +11,7 @@ var	async = require('async'),
 	notifications = require('../notifications'),
 	groups = require('../groups'),
 	user = require('../user'),
-	index = require('./index'),
+	websockets = require('./index'),
 
 	SocketPosts = {};
 
@@ -40,38 +40,50 @@ SocketPosts.reply = function(socket, data, callback) {
 				posts: [postData]
 			};
 
-			index.server.sockets.emit('event:new_post', socketData);
+			websockets.server.sockets.emit('event:new_post', socketData);
 
 			callback();
 		}
 	});
 };
 
-SocketPosts.upvote = function(socket, data) {
-	favouriteCommand('upvote', socket, data);
+SocketPosts.upvote = function(socket, data, callback) {
+	favouriteCommand('upvote', 'voted', socket, data, callback);
 	sendNotificationToPostOwner(data, socket.uid, 'has upvoted your post');
 };
 
-SocketPosts.downvote = function(socket, data) {
-	favouriteCommand('downvote', socket, data);
+SocketPosts.downvote = function(socket, data, callback) {
+	favouriteCommand('downvote', 'voted', socket, data, callback);
 };
 
-SocketPosts.unvote = function(socket, data) {
-	favouriteCommand('unvote', socket, data);
+SocketPosts.unvote = function(socket, data, callback) {
+	favouriteCommand('unvote', 'voted', socket, data, callback);
 };
 
-SocketPosts.favourite = function(socket, data) {
-	favouriteCommand('favourite', socket, data);
+SocketPosts.favourite = function(socket, data, callback) {
+	favouriteCommand('favourite', 'favourited', socket, data, callback);
 	sendNotificationToPostOwner(data, socket.uid, 'has favourited your post');
 };
 
-SocketPosts.unfavourite = function(socket, data) {
-	favouriteCommand('unfavourite', socket, data);
+SocketPosts.unfavourite = function(socket, data, callback) {
+	favouriteCommand('unfavourite', 'favourited', socket, data, callback);
 };
 
-function favouriteCommand(command, socket, data) {
+function favouriteCommand(command, eventName, socket, data, callback) {
+
 	if(data && data.pid && data.room_id) {
-		favourites[command](data.pid, data.room_id, socket.uid, socket);
+		favourites[command](data.pid, socket.uid, function(err, result) {
+			if (err) {
+				return callback(err);
+			}
+
+			socket.emit('posts.' + command, data.pid);
+
+			if(data.room_id && result && eventName) {
+				websockets.in(data.room_id).emit('event:' + eventName, result);
+			}
+			callback();
+		});
 	}
 }
 
@@ -116,8 +128,8 @@ SocketPosts.getRawPost = function(socket, pid, callback) {
 			return callback(err);
 		}
 
-		if(data.deleted === '1') {
-			return callback(new Error('This post no longer exists'));
+		if(parseInt(data.deleted, 10) === 1) {
+			return callback(new Error('[[error:no-post]]'));
 		}
 
 		callback(null, data.content);
@@ -140,7 +152,7 @@ SocketPosts.edit = function(socket, data, callback) {
 			return callback(err);
 		}
 
-		index.server.sockets.in('topic_' + results.topic.tid).emit('event:post_edited', {
+		websockets.server.sockets.in('topic_' + results.topic.tid).emit('event:post_edited', {
 			pid: data.pid,
 			title: results.topic.title,
 			isMainPost: results.topic.isMainPost,
@@ -161,7 +173,7 @@ SocketPosts.restore = function(socket, data, callback) {
 
 function deleteOrRestore(command, socket, data, callback) {
 	if(!data) {
-		return callback(new Error('invalid data'));
+		return callback(new Error('[[error:invalid-data]]'));
 	}
 
 	postTools[command](socket.uid, data.pid, function(err) {
@@ -172,7 +184,7 @@ function deleteOrRestore(command, socket, data, callback) {
 		module.parent.exports.emitTopicPostStats();
 
 		var eventName = command === 'restore' ? 'event:post_restored' : 'event:post_deleted';
-		index.server.sockets.in('topic_' + data.tid).emit(eventName, {
+		websockets.server.sockets.in('topic_' + data.tid).emit(eventName, {
 			pid: data.pid
 		});
 
@@ -235,7 +247,7 @@ SocketPosts.getPidIndex = function(socket, pid, callback) {
 
 SocketPosts.flag = function(socket, pid, callback) {
 	if (!socket.uid) {
-		return callback(new Error('not-logged-in'));
+		return callback(new Error('[[error:not-logged-in]]'));
 	}
 
 	var message = '',
@@ -273,7 +285,7 @@ SocketPosts.flag = function(socket, pid, callback) {
 
 SocketPosts.loadMoreFavourites = function(socket, data, callback) {
 	if(!data || !data.after) {
-		return callback(new Error('invalid data'));
+		return callback(new Error('[[error:invalid-data]]'));
 	}
 
 	var start = parseInt(data.after, 10),
@@ -284,7 +296,7 @@ SocketPosts.loadMoreFavourites = function(socket, data, callback) {
 
 SocketPosts.loadMoreUserPosts = function(socket, data, callback) {
 	if(!data || !data.after || !data.uid) {
-		return callback(new Error('invalid data'));
+		return callback(new Error('[[error:invalid-data]]'));
 	}
 
 	var start = parseInt(data.after, 10),
@@ -296,7 +308,7 @@ SocketPosts.loadMoreUserPosts = function(socket, data, callback) {
 
 SocketPosts.getRecentPosts = function(socket, data, callback) {
 	if(!data || !data.count) {
-		return callback(new Error('invalid data'));
+		return callback(new Error('[[error:invalid-data]]'));
 	}
 
 	posts.getRecentPosts(socket.uid, 0, data.count - 1, data.term, callback);
diff --git a/src/socket.io/user.js b/src/socket.io/user.js
index 4dee39f4be..ce78fe9976 100644
--- a/src/socket.io/user.js
+++ b/src/socket.io/user.js
@@ -60,7 +60,7 @@ SocketUser.changePassword = function(socket, data, callback) {
 
 SocketUser.updateProfile = function(socket, data, callback) {
 	if(!data || !data.uid) {
-		return callback(new Error('invalid-data'));
+		return callback(new Error('[[error:invalid-data]]'));
 	}
 
 	if(socket.uid === parseInt(data.uid, 10)) {
@@ -73,7 +73,7 @@ SocketUser.updateProfile = function(socket, data, callback) {
 		}
 
 		if(!isAdmin) {
-			return callback(new Error('not allowed!'));
+			return callback(new Error('[[error:no-privileges]]'));
 		}
 
 		user.updateProfile(data.uid, data, callback);
@@ -82,7 +82,7 @@ SocketUser.updateProfile = function(socket, data, callback) {
 
 SocketUser.changePicture = function(socket, data, callback) {
 	if(!data) {
-		return callback(new Error('invalid-data'));
+		return callback(new Error('[[error:invalid-data]]'));
 	}
 
 	var type = data.type;
@@ -117,7 +117,7 @@ SocketUser.changePicture = function(socket, data, callback) {
 	} else if (type === 'uploaded') {
 		type = 'uploadedpicture';
 	} else {
-		return callback(new Error('invalid-image-type'));
+		return callback(new Error('[[error:invalid-image-type]]'));
 	}
 
 	if(socket.uid === parseInt(data.uid, 10)) {
@@ -136,7 +136,7 @@ SocketUser.changePicture = function(socket, data, callback) {
 		}
 
 		if(!isAdmin) {
-			return callback(new Error('not-allowed'));
+			return callback(new Error('[[error:no-privileges]]'));
 		}
 
 		changePicture(data.uid, callback);
@@ -170,7 +170,7 @@ SocketUser.saveSettings = function(socket, data, callback) {
 SocketUser.getOnlineUsers = function(socket, data, callback) {
 	var returnData = {};
 	if(!data) {
-		return callback(new Error('invalid-data'));
+		return callback(new Error('[[error:invalid-data]]'));
 	}
 
 	function getUserStatus(uid, next) {
@@ -202,11 +202,11 @@ SocketUser.getActiveUsers = function(socket, data, callback) {
 
 SocketUser.loadMore = function(socket, data, callback) {
 	if(!data || !data.set || parseInt(data.after, 10) < 0) {
-		return callback(new Error('invalid-data'));
+		return callback(new Error('[[error:invalid-data]]'));
 	}
 
 	if (!socket.uid && !!parseInt(meta.config.privateUserInfo, 10)) {
-		return callback(new Error('not-allowed'));
+		return callback(new Error('[[error:no-privileges]]'));
 	}
 
 	var start = data.after,
diff --git a/src/threadTools.js b/src/threadTools.js
index 27f2da6fe2..7e7c8e509b 100644
--- a/src/threadTools.js
+++ b/src/threadTools.js
@@ -69,9 +69,9 @@ var winston = require('winston'),
 			}
 
 			if (parseInt(deleted, 10) && isDelete) {
-				return callback(new Error('topic-already-deleted'));
+				return callback(new Error('[[error:topic-already-deleted]]'));
 			} else if (!parseInt(deleted, 10) && !isDelete) {
-				return callback(new Error('topic-already-restored'));
+				return callback(new Error('[[error:topic-already-restored]]'));
 			}
 
 			topics[isDelete ? 'delete' : 'restore'](tid, function(err) {
diff --git a/src/topics.js b/src/topics.js
index 2da58b7245..a5cbd0b69b 100644
--- a/src/topics.js
+++ b/src/topics.js
@@ -58,7 +58,7 @@ var async = require('async'),
 	Topics.getTopicDataWithUser = function(tid, callback) {
 		Topics.getTopicData(tid, function(err, topic) {
 			if (err || !topic) {
-				return callback(err || new Error('topic doesn\'t exist'));
+				return callback(err || new Error('[[error:no-topic]]'));
 			}
 
 			user.getUserFields(topic.uid, ['username', 'userslug', 'picture'] , function(err, userData) {
@@ -101,7 +101,7 @@ var async = require('async'),
 
 	Topics.getTidPage = function(tid, uid, callback) {
 		if(!tid) {
-			return callback(new Error('invalid-tid'));
+			return callback(new Error('[[error:invalid-tid]]'));
 		}
 
 		async.parallel({
@@ -270,7 +270,7 @@ var async = require('async'),
 	Topics.getTopicWithPosts = function(tid, uid, start, end, callback) {
 		Topics.getTopicData(tid, function(err, topicData) {
 			if (err || !topicData) {
-				return callback(err || new Error('Topic tid \'' + tid + '\' not found'));
+				return callback(err || new Error('[[error:no-topic]]'));
 			}
 
 			async.parallel({
@@ -328,8 +328,8 @@ var async = require('async'),
 			posts.getPostFields(pid, ['pid', 'uid', 'timestamp'], function(err, postData) {
 				if (err) {
 					return callback(err);
-				} else if(!postData) {
-					return callback(new Error('no-teaser-found'));
+				} else if(!postData || !postData.uid) {
+					return callback(new Error('[[error:no-teaser]]'));
 				}
 
 				user.getUserFields(postData.uid, ['username', 'userslug', 'picture'], function(err, userData) {
diff --git a/src/topics/fork.js b/src/topics/fork.js
index 6cda238973..005b137058 100644
--- a/src/topics/fork.js
+++ b/src/topics/fork.js
@@ -19,11 +19,11 @@ module.exports = function(Topics) {
 		}
 
 		if(!title) {
-			return callback(new Error('invalid-title'));
+			return callback(new Error('[[error:invalid-title]]'));
 		}
 
 		if(!pids || !pids.length) {
-			return callback(new Error('invalid-pids'));
+			return callback(new Error('[[error:invalid-pid]]'));
 		}
 
 		pids.sort();
@@ -71,7 +71,7 @@ module.exports = function(Topics) {
 	Topics.movePostToTopic = function(pid, tid, callback) {
 		threadTools.exists(tid, function(err, exists) {
 			if(err || !exists) {
-				return callback(err || new Error('Topic doesn\'t exist'));
+				return callback(err || new Error('[[error:no-topic]]'));
 			}
 
 			posts.getPostFields(pid, ['deleted', 'tid', 'timestamp'], function(err, postData) {
@@ -79,8 +79,8 @@ module.exports = function(Topics) {
 					return callback(err);
 				}
 
-				if(!postData) {
-					return callback(new Error('Post doesn\'t exist'));
+				if(!postData || !postData.tid) {
+					return callback(new Error('[[error:no-post]]'));
 				}
 
 				Topics.removePostFromTopic(postData.tid, pid, function(err) {
diff --git a/src/topics/recent.js b/src/topics/recent.js
index bb51800785..255c65a388 100644
--- a/src/topics/recent.js
+++ b/src/topics/recent.js
@@ -30,7 +30,7 @@ module.exports = function(Topics) {
 
 		var count = parseInt(end, 10) === -1 ? end : end - start + 1;
 
-		db.getSortedSetRevRangeByScore(['topics:recent', '+inf', Date.now() - since, 'LIMIT', start, count], callback);
+		db.getSortedSetRevRangeByScore('topics:recent', start, count, Infinity, Date.now() - since, callback);
 	};
 
 };
diff --git a/src/user.js b/src/user.js
index 93805a567b..a06219b957 100644
--- a/src/user.js
+++ b/src/user.js
@@ -126,11 +126,11 @@ var bcrypt = require('bcryptjs'),
 			}
 
 			if (parseInt(results.banned, 10) === 1) {
-				return callback(new Error('user-banned'));
+				return callback(new Error('[[error:user-banned]]'));
 			}
 
 			if (!results.exists) {
-				return callback(new Error('invalid-user'));
+				return callback(new Error('[[error:no-user]]'));
 			}
 
 			var lastposttime = results.lastposttime;
diff --git a/src/user/create.js b/src/user/create.js
index 33d7a5fd8c..f8f95b0878 100644
--- a/src/user/create.js
+++ b/src/user/create.js
@@ -25,17 +25,17 @@ module.exports = function(User) {
 		async.parallel([
 			function(next) {
 				if (userData.email) {
-					next(!utils.isEmailValid(userData.email) ? new Error('Invalid Email!') : null);
+					next(!utils.isEmailValid(userData.email) ? new Error('[[error:invalid-email]]') : null);
 				} else {
 					next();
 				}
 			},
 			function(next) {
-				next((!utils.isUserNameValid(userData.username) || !userData.userslug) ? new Error('Invalid Username!') : null);
+				next((!utils.isUserNameValid(userData.username) || !userData.userslug) ? new Error('[[error:invalid-username]]') : null);
 			},
 			function(next) {
 				if (userData.password) {
-					next(!utils.isPasswordValid(userData.password) ? new Error('Invalid Password!') : null);
+					next(!utils.isPasswordValid(userData.password) ? new Error('[[error:invalid-password]]') : null);
 				} else {
 					next();
 				}
@@ -45,7 +45,7 @@ module.exports = function(User) {
 					if (err) {
 						return next(err);
 					}
-					next(exists ? new Error('Username taken!') : null);
+					next(exists ? new Error('[[error:username-taken]]') : null);
 				});
 			},
 			function(next) {
@@ -54,7 +54,7 @@ module.exports = function(User) {
 						if (err) {
 							return next(err);
 						}
-						next(!available ? new Error('Email taken!') : null);
+						next(!available ? new Error('[[error:email-taken]]') : null);
 					});
 				} else {
 					next();
diff --git a/src/user/notifications.js b/src/user/notifications.js
index df3096b8e8..3b4542a787 100644
--- a/src/user/notifications.js
+++ b/src/user/notifications.js
@@ -114,7 +114,7 @@ var async = require('async'),
 	UserNotifications.getDailyUnread = function(uid, callback) {
 		var	now = Date.now(),
 			yesterday = now - (1000*60*60*24);	// Approximate, can be more or less depending on time changes, makes no difference really.
-		db.getSortedSetRangeByScore(['uid:' + uid + ':notifications:unread', yesterday, now], function(err, nids) {
+		db.getSortedSetRangeByScore('uid:' + uid + ':notifications:unread', 0, 20, yesterday, now, function(err, nids) {
 			async.map(nids, function(nid, next) {
 				notifications.get(nid, uid, function(notif_data) {
 					next(null, notif_data);
diff --git a/src/user/profile.js b/src/user/profile.js
index 50a33112e5..8620dde36e 100644
--- a/src/user/profile.js
+++ b/src/user/profile.js
@@ -18,7 +18,7 @@ module.exports = function(User) {
 
 		function isSignatureValid(next) {
 			if (data.signature !== undefined && data.signature.length > meta.config.maximumSignatureLength) {
-				next(new Error('Signature can\'t be longer than ' + meta.config.maximumSignatureLength + ' characters!'));
+				next(new Error('[[error:signature-too-long, ' + meta.config.maximumSignatureLength + ']]'));
 			} else {
 				next();
 			}
@@ -39,7 +39,7 @@ module.exports = function(User) {
 						return next(err);
 					}
 
-					next(!available ? new Error('Email not available!') : null);
+					next(!available ? new Error('[[error:email-taken]]') : null);
 
 				});
 			});
@@ -55,7 +55,7 @@ module.exports = function(User) {
 				}
 
 				if(!utils.isUserNameValid(data.username) || !userslug) {
-					return next(new Error('Invalid Username!'));
+					return next(new Error('[[error:invalid-username]]'));
 				}
 
 				User.exists(userslug, function(err, exists) {
@@ -63,7 +63,7 @@ module.exports = function(User) {
 						return next(err);
 					}
 
-					next(exists ? new Error('Username not available!') : null);
+					next(exists ? new Error('[[error:username-taken]]') : null);
 				});
 			});
 		}
@@ -196,7 +196,7 @@ module.exports = function(User) {
 
 	User.changePassword = function(uid, data, callback) {
 		if(!data || !data.uid) {
-			return callback(new Error('invalid-uid'));
+			return callback(new Error('[[error:invalid-uid]]'));
 		}
 
 		function hashAndSetPassword(callback) {
diff --git a/src/user/reset.js b/src/user/reset.js
index 330f49c843..7e3b2d75c8 100644
--- a/src/user/reset.js
+++ b/src/user/reset.js
@@ -40,7 +40,7 @@ var async = require('async'),
 	UserReset.send = function(socket, email, callback) {
 		user.getUidByEmail(email, function(err, uid) {
 			if(err || !uid) {
-				return callback(err || new Error('invalid-email'));
+				return callback(err || new Error('[[error:invalid-email]]'));
 			}
 
 			// Generate a new reset code
diff --git a/src/user/settings.js b/src/user/settings.js
index bc28622960..7e9665d9f0 100644
--- a/src/user/settings.js
+++ b/src/user/settings.js
@@ -37,7 +37,7 @@ module.exports = function(User) {
 	User.saveSettings = function(uid, data, callback) {
 
 		if(!data.topicsPerPage || !data.postsPerPage || parseInt(data.topicsPerPage, 10) <= 0 || parseInt(data.postsPerPage, 10) <= 0) {
-			return callback(new Error('Invalid pagination value!'));
+			return callback(new Error('[[error:invalid-pagination-value]]'));
 		}
 
 		plugins.fireHook('action:user.saveSettings', {uid: uid, settings: data});
diff --git a/tests/database.js b/tests/database.js
index 8ca9a8f390..eaa8bc667a 100644
--- a/tests/database.js
+++ b/tests/database.js
@@ -349,4 +349,4 @@ describe('Test database', function() {
 		});
 
 	});
-});
\ No newline at end of file
+});
diff --git a/tests/topics.js b/tests/topics.js
index a028c1b274..c28456fdbc 100644
--- a/tests/topics.js
+++ b/tests/topics.js
@@ -43,7 +43,7 @@ describe('Topic\'s', function() {
 
 		it('should fail to create new topic with wrong parameters', function(done) {
 			topics.post({uid: null, title: topic.title, content: topic.content, cid: topic.categoryId}, function(err, result) {
-				assert.equal(err.message, 'invalid-user');
+				assert.equal(err.message, '[[error:no-user]]');
 				done();
 			});
 		});