diff --git a/src/language.h b/src/language.h
index fed138e635..ea30697561 100644
--- a/src/language.h
+++ b/src/language.h
@@ -21,10 +21,8 @@ static const uint8 CASE_GENDER_LEN = 16; ///< The (maximum) length of a case/gen
 static const uint8 MAX_NUM_GENDERS =  8; ///< Maximum number of supported genders.
 static const uint8 MAX_NUM_CASES   = 16; ///< Maximum number of supported cases.
 
-static const uint TAB_SIZE_OFFSET  = 0;                   ///< The offset for the tab size.
 static const uint TAB_SIZE_BITS    = 11;                  ///< The number of bits used for the tab size.
 static const uint TAB_SIZE         = 1 << TAB_SIZE_BITS;  ///< The number of values in a tab.
-static const uint TAB_COUNT_OFFSET = TAB_SIZE_BITS;       ///< The offset for the tab count.
 static const uint TAB_COUNT_BITS   = 5;                   ///< The number of bits used for the amount of tabs.
 static const uint TAB_COUNT        = 1 << TAB_COUNT_BITS; ///< The amount of tabs.
 
diff --git a/src/saveload/company_sl.cpp b/src/saveload/company_sl.cpp
index 6ac22c2685..ff02167d3e 100644
--- a/src/saveload/company_sl.cpp
+++ b/src/saveload/company_sl.cpp
@@ -16,6 +16,7 @@
 #include "../tunnelbridge_map.h"
 #include "../tunnelbridge.h"
 #include "../station_base.h"
+#include "../strings_func.h"
 
 #include "saveload.h"
 
@@ -501,11 +502,11 @@ static void Check_PLYR()
 
 		/* We do not load old custom names */
 		if (IsSavegameVersionBefore(84)) {
-			if (GB(cprops->name_1, 11, 5) == 15) {
+			if (GetStringTab(cprops->name_1) == 15) {
 				cprops->name_1 = STR_GAME_SAVELOAD_NOT_AVAILABLE;
 			}
 
-			if (GB(cprops->president_name_1, 11, 5) == 15) {
+			if (GetStringTab(cprops->president_name_1) == 15) {
 				cprops->president_name_1 = STR_GAME_SAVELOAD_NOT_AVAILABLE;
 			}
 		}
diff --git a/src/saveload/strings_sl.cpp b/src/saveload/strings_sl.cpp
index 73f2a1e2e1..c9fb91546a 100644
--- a/src/saveload/strings_sl.cpp
+++ b/src/saveload/strings_sl.cpp
@@ -11,6 +11,7 @@
 
 #include "../stdafx.h"
 #include "../string_func.h"
+#include "../strings_func.h"
 #include "saveload_internal.h"
 
 #include "table/strings.h"
@@ -60,7 +61,7 @@ char *_old_name_array = NULL;
 char *CopyFromOldName(StringID id)
 {
 	/* Is this name an (old) custom name? */
-	if (GB(id, 11, 5) != 15) return NULL;
+	if (GetStringTab(id) != 15) return NULL;
 
 	if (IsSavegameVersionBefore(37)) {
 		/* Allow for expansion when converted to UTF-8. */
diff --git a/src/saveload/town_sl.cpp b/src/saveload/town_sl.cpp
index 7db6a7aa6f..8a9f879bba 100644
--- a/src/saveload/town_sl.cpp
+++ b/src/saveload/town_sl.cpp
@@ -14,6 +14,7 @@
 #include "../town.h"
 #include "../landscape.h"
 #include "../subsidy_func.h"
+#include "../strings_func.h"
 
 #include "saveload.h"
 #include "newgrf_sl.h"
@@ -285,7 +286,7 @@ static void Load_TOWN()
 			SlObject(&t->received[i], _town_received_desc);
 		}
 
-		if (t->townnamegrfid == 0 && !IsInsideMM(t->townnametype, SPECSTR_TOWNNAME_START, SPECSTR_TOWNNAME_LAST + 1) && GB(t->townnametype, 11, 5) != 15) {
+		if (t->townnamegrfid == 0 && !IsInsideMM(t->townnametype, SPECSTR_TOWNNAME_START, SPECSTR_TOWNNAME_LAST + 1) && GetStringTab(t->townnametype) != 15) {
 			SlErrorCorrupt("Invalid town name generator");
 		}
 
diff --git a/src/script/api/script_error.cpp b/src/script/api/script_error.cpp
index d30ad9c66d..87647cfe82 100644
--- a/src/script/api/script_error.cpp
+++ b/src/script/api/script_error.cpp
@@ -13,6 +13,7 @@
 #include "script_error.hpp"
 #include "../../core/bitmath_func.hpp"
 #include "../../string_func.h"
+#include "../../strings_func.h"
 
 #include "../../safeguards.h"
 
@@ -31,8 +32,8 @@ ScriptError::ScriptErrorMapString ScriptError::error_map_string = ScriptError::S
 
 /* static */ ScriptErrorType ScriptError::StringToError(StringID internal_string_id)
 {
-	uint index = GB(internal_string_id, 11, 5);
-	switch (GB(internal_string_id, 11, 5)) {
+	uint index = GetStringIndex(internal_string_id);
+	switch (GetStringTab(internal_string_id)) {
 		case 26: case 28: case 29: case 30: // NewGRF strings.
 			return ERR_NEWGRF_SUPPLIED_ERROR;
 
diff --git a/src/strings.cpp b/src/strings.cpp
index 8cd071d0ba..704ed3c72e 100644
--- a/src/strings.cpp
+++ b/src/strings.cpp
@@ -194,14 +194,14 @@ static bool _scan_for_gender_data = false;  ///< Are we scanning for the gender
 
 const char *GetStringPtr(StringID string)
 {
-	switch (GB(string, TAB_COUNT_OFFSET, TAB_COUNT_BITS)) {
-		case GAME_TEXT_TAB: return GetGameStringPtr(GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS));
+	switch (GetStringTab(string)) {
+		case GAME_TEXT_TAB: return GetGameStringPtr(GetStringIndex(string));
 		/* 0xD0xx and 0xD4xx IDs have been converted earlier. */
 		case 26: NOT_REACHED();
-		case 28: return GetGRFStringPtr(GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS));
-		case 29: return GetGRFStringPtr(GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS) + 0x0800);
-		case 30: return GetGRFStringPtr(GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS) + 0x1000);
-		default: return _langpack_offs[_langtab_start[GB(string, TAB_COUNT_OFFSET, TAB_COUNT_BITS)] + GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS)];
+		case 28: return GetGRFStringPtr(GetStringIndex(string));
+		case 29: return GetGRFStringPtr(GetStringIndex(string) + 0x0800);
+		case 30: return GetGRFStringPtr(GetStringIndex(string) + 0x1000);
+		default: return _langpack_offs[_langtab_start[GetStringTab(string)] + GetStringIndex(string)];
 	}
 }
 
@@ -219,8 +219,8 @@ char *GetStringWithArgs(char *buffr, StringID string, StringParameters *args, co
 {
 	if (string == 0) return GetStringWithArgs(buffr, STR_UNDEFINED, args, last);
 
-	uint index = GB(string, TAB_SIZE_OFFSET,  TAB_SIZE_BITS);
-	uint tab   = GB(string, TAB_COUNT_OFFSET, TAB_COUNT_BITS);
+	uint index = GetStringIndex(string);
+	uint tab = GetStringTab(string);
 
 	switch (tab) {
 		case 4:
@@ -886,7 +886,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
 								buff = strecat(buff, "(invalid sub-StringID)", last);
 								break;
 							}
-							param = (GAME_TEXT_TAB << TAB_COUNT_OFFSET) + param;
+							param = MakeStringID(GAME_TEXT_TAB, param);
 						}
 
 						sub_args.SetParam(i++, param);
@@ -901,7 +901,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
 				/* If we didn't error out, we can actually print the string. */
 				if (*str != '\0') {
 					str = p;
-					buff = GetStringWithArgs(buff, (GAME_TEXT_TAB << TAB_COUNT_OFFSET) + stringid, &sub_args, last, true);
+					buff = GetStringWithArgs(buff, MakeStringID(GAME_TEXT_TAB, stringid), &sub_args, last, true);
 				}
 
 				for (int i = 0; i < 20; i++) {
@@ -1017,7 +1017,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
 
 			case SCC_STRING: {// {STRING}
 				StringID str = args->GetInt32(SCC_STRING);
-				if (game_script && GB(str, TAB_COUNT_OFFSET, TAB_COUNT_BITS) != GAME_TEXT_TAB) break;
+				if (game_script && GetStringTab(str) != GAME_TEXT_TAB) break;
 				/* WARNING. It's prohibited for the included string to consume any arguments.
 				 * For included strings that consume argument, you should use STRING1, STRING2 etc.
 				 * To debug stuff you can set argv to NULL and it will tell you */
@@ -1036,7 +1036,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
 			case SCC_STRING7: { // {STRING1..7}
 				/* Strings that consume arguments */
 				StringID str = args->GetInt32(b);
-				if (game_script && GB(str, TAB_COUNT_OFFSET, TAB_COUNT_BITS) != GAME_TEXT_TAB) break;
+				if (game_script && GetStringTab(str) != GAME_TEXT_TAB) break;
 				uint size = b - SCC_STRING1 + 1;
 				if (game_script && size > args->GetDataLeft()) {
 					buff = strecat(buff, "(too many parameters)", last);
diff --git a/src/strings_func.h b/src/strings_func.h
index 2c7809d020..5d69121e39 100644
--- a/src/strings_func.h
+++ b/src/strings_func.h
@@ -15,6 +15,38 @@
 #include "strings_type.h"
 #include "string_type.h"
 #include "gfx_type.h"
+#include "core/bitmath_func.hpp"
+
+/**
+ * Extract the StringTab from a StringID.
+ * @param str String identifier
+ * @return StringTab from \a str
+ */
+static inline uint GetStringTab(StringID str)
+{
+	return GB(str, 11, 5);
+}
+
+/**
+ * Extract the StringIndex from a StringID.
+ * @param str String identifier
+ * @return StringIndex from \a str
+ */
+static inline uint GetStringIndex(StringID str)
+{
+	return GB(str, 0, 11);
+}
+
+/**
+ * Create a StringID
+ * @param tab StringTab
+ * @param index StringIndex
+ * @return StringID composed from \a tab and \a index
+ */
+static inline StringID MakeStringID(uint tab, uint index)
+{
+	return tab << 11 | index;
+}
 
 class StringParameters {
 	StringParameters *parent; ///< If not NULL, this instance references data from this parent instance.