diff --git a/src/m_misc.c b/src/m_misc.c
index 5815d17c54619f46afe6fc29c1277a3621ecc77b..1b6a90c50acd6230cb9d8c56f99df7178926c777 100644
--- a/src/m_misc.c
+++ b/src/m_misc.c
@@ -2259,6 +2259,44 @@ boolean M_IsStringEmpty(const char *s)
 	return true;
 }
 
+// Converts a string containing a whole number into an int. Returns false if the conversion failed.
+boolean M_StringToNumber(const char *input, int *out)
+{
+	char *end_position = NULL;
+
+	errno = 0;
+
+	int result = strtol(input, &end_position, 10);
+	if (end_position == input || *end_position != '\0')
+		return false;
+
+	if (errno == ERANGE)
+		return false;
+
+	*out = result;
+
+	return true;
+}
+
+// Converts a string containing a number into a double. Returns false if the conversion failed.
+boolean M_StringToDecimal(const char *input, double *out)
+{
+	char *end_position = NULL;
+
+	errno = 0;
+
+	double result = strtod(input, &end_position);
+	if (end_position == input || *end_position != '\0')
+		return false;
+
+	if (errno == ERANGE)
+		return false;
+
+	*out = result;
+
+	return true;
+}
+
 // Rounds off floating numbers and checks for 0 - 255 bounds
 int M_RoundUp(double number)
 {
diff --git a/src/m_misc.h b/src/m_misc.h
index 753991e70465c075fa874ce7662d6aa6603e7b6a..04ac66ca65e1ff92d44172b12e1186cdaa04b648 100644
--- a/src/m_misc.h
+++ b/src/m_misc.h
@@ -109,6 +109,12 @@ const char * M_Ftrim (double);
 // Returns true if the string is empty.
 boolean M_IsStringEmpty(const char *s);
 
+// Converts a string containing a whole number into an int. Returns false if the conversion failed.
+boolean M_StringToNumber(const char *input, int *out);
+
+// Converts a string containing a number into a double. Returns false if the conversion failed.
+boolean M_StringToDecimal(const char *input, double *out);
+
 // counting bits, for weapon ammo code, usually
 FUNCMATH UINT8 M_CountBits(UINT32 num, UINT8 size);
 
diff --git a/src/r_translation.c b/src/r_translation.c
index a4df3cde0c9bdc04702f963e62b3703ca6f42c58..d86f3ad9598f258c782a0d050c89dbead64213b6 100644
--- a/src/r_translation.c
+++ b/src/r_translation.c
@@ -17,6 +17,7 @@
 #include "z_zone.h"
 #include "w_wad.h"
 #include "m_tokenizer.h"
+#include "m_misc.h"
 
 #include <errno.h>
 
@@ -528,50 +529,26 @@ void R_LoadParsedTranslations(void)
 
 static boolean ExpectToken(tokenizer_t *sc, const char *expect)
 {
-	return strcmp(sc->get(sc, 0), expect) == 0;
-}
-
-static boolean StringToNumber(const char *tkn, int *out)
-{
-	char *endPos = NULL;
-
-	errno = 0;
-
-	int result = strtol(tkn, &endPos, 10);
-	if (endPos == tkn || *endPos != '\0')
-		return false;
-
-	if (errno == ERANGE)
+	const char *tkn = sc->get(sc, 0);
+	if (!tkn)
 		return false;
-
-	*out = result;
-
-	return true;
+	return strcmp(tkn, expect) == 0;
 }
 
 static boolean ParseNumber(tokenizer_t *sc, int *out)
 {
-	return StringToNumber(sc->get(sc, 0), out);
+	const char *tkn = sc->get(sc, 0);
+	if (!tkn)
+		return false;
+	return M_StringToNumber(tkn, out);
 }
 
 static boolean ParseDecimal(tokenizer_t *sc, double *out)
 {
 	const char *tkn = sc->get(sc, 0);
-
-	char *endPos = NULL;
-
-	errno = 0;
-
-	double result = strtod(tkn, &endPos);
-	if (endPos == tkn || *endPos != '\0')
-		return false;
-
-	if (errno == ERANGE)
+	if (!tkn)
 		return false;
-
-	*out = result;
-
-	return true;
+	return M_StringToDecimal(tkn, out);
 }
 
 static struct PaletteRemapParseResult *ThrowError(const char *format, ...)
@@ -615,6 +592,9 @@ static struct PaletteRemapParseResult *PaletteRemap_ParseString(tokenizer_t *sc)
 		return ThrowError("expected '='");
 
 	const char *tkn = sc->get(sc, 0);
+	if (tkn == NULL)
+		return ThrowError("unexpected EOF");
+
 	if (strcmp(tkn, "[") == 0)
 	{
 		// translation using RGB values
@@ -785,7 +765,7 @@ static struct PaletteRemapParseResult *PaletteRemap_ParseString(tokenizer_t *sc)
 	{
 		int pal1, pal2;
 
-		if (!StringToNumber(tkn, &pal1))
+		if (!M_StringToNumber(tkn, &pal1))
 			return ThrowError("expected a number for starting index");
 		if (!ExpectToken(sc, ":"))
 			return ThrowError("expected ':'");
@@ -809,6 +789,13 @@ static struct PaletteRemapParseResult *PaletteRemap_ParseTranslation(const char
 	return result;
 }
 
+#define CHECK_EOF() \
+	if (!tkn) \
+	{ \
+		CONS_Alert(CONS_ERROR, "Error parsing translation '%s': Unexpected EOF\n", name); \
+		goto fail; \
+	}
+
 void R_ParseTrnslate(INT32 wadNum, UINT16 lumpnum)
 {
 	char *lumpData = (char *)W_CacheLumpNumPwad(wadNum, lumpnum, PU_STATIC);
@@ -827,9 +814,11 @@ void R_ParseTrnslate(INT32 wadNum, UINT16 lumpnum)
 		char *name = Z_StrDup(tkn);
 
 		tkn = sc->get(sc, 0);
+		CHECK_EOF();
 		if (strcmp(tkn, ":") == 0)
 		{
 			tkn = sc->get(sc, 0);
+			CHECK_EOF();
 
 			base_translation = R_FindCustomTranslation(tkn);
 			if (base_translation == -1)
@@ -847,6 +836,15 @@ void R_ParseTrnslate(INT32 wadNum, UINT16 lumpnum)
 			goto fail;
 		}
 		tkn = sc->get(sc, 0);
+		CHECK_EOF();
+
+		if (strcmp(tkn, "\"") != 0)
+		{
+			CONS_Alert(CONS_ERROR, "Error parsing translation '%s': Expected '=', got '%s'\n", name, tkn);
+			goto fail;
+		}
+		tkn = sc->get(sc, 0);
+		CHECK_EOF();
 
 		struct PaletteRemapParseResult *result = NULL;
 		do {
@@ -862,10 +860,15 @@ void R_ParseTrnslate(INT32 wadNum, UINT16 lumpnum)
 			if (!tkn)
 				break;
 
-			if (strcmp(tkn, ",") != 0)
-				break;
+			if (strcmp(tkn, "\"") != 0)
+			{
+				CONS_Alert(CONS_ERROR, "Error parsing translation '%s': Expected '=', got '%s'\n", name, tkn);
+				goto fail;
+			}
 
 			tkn = sc->get(sc, 0);
+			if (!tkn || strcmp(tkn, ",") != 0)
+				break;
 		} while (true);
 
 		// Allocate it and register it
@@ -886,6 +889,8 @@ fail:
 	Z_Free(text);
 }
 
+#undef CHECK_EOF
+
 typedef struct CustomTranslation
 {
 	char *name;