diff --git a/lib/SimpleSAML/Error/Error.php b/lib/SimpleSAML/Error/Error.php
index 542c35be43124a6e335092e5d5b3473c3f873359..0e26bf552705eed76e957294939ab4b6238cfa9b 100644
--- a/lib/SimpleSAML/Error/Error.php
+++ b/lib/SimpleSAML/Error/Error.php
@@ -100,8 +100,8 @@ class SimpleSAML_Error_Error extends SimpleSAML_Error_Exception
             $this->dictTitle = '{'.$this->module.':errors:title_'.$moduleCode[1].'}';
             $this->dictDescr = '{'.$this->module.':errors:descr_'.$moduleCode[1].'}';
         } else {
-            $this->dictTitle = '{errors:title_'.$this->errorCode.'}';
-            $this->dictDescr = '{errors:descr_'.$this->errorCode.'}';
+            $this->dictTitle = SimpleSAML\Error\ErrorCodes::getErrorCodeTitle($this->errorCode);
+            $this->dictDescr = SimpleSAML\Error\ErrorCodes::getErrorCodeDescription($this->errorCode);
         }
 
         if (!empty($this->parameters)) {
diff --git a/lib/SimpleSAML/Error/ErrorCodes.php b/lib/SimpleSAML/Error/ErrorCodes.php
new file mode 100644
index 0000000000000000000000000000000000000000..e4d08195094936c457cf92727cfdc6120760f553
--- /dev/null
+++ b/lib/SimpleSAML/Error/ErrorCodes.php
@@ -0,0 +1,188 @@
+<?php
+/**
+ * Class that maps SimpleSAMLphp error codes to translateable strings.
+ *
+ * @author Hanne Moa, UNINETT AS. <hanne.moa@uninett.no>
+ * @package SimpleSAMLphp
+ */
+
+namespace SimpleSAML\Error;
+
+class ErrorCodes
+{
+    /**
+     * Fetch all default translation strings for error code titles.
+     *
+     * @return array A map from error code to error code title
+     */
+    final public static function defaultGetAllErrorCodeTitles()
+    {
+        return array(
+            'ACSPARAMS' => \SimpleSAML\Locale\Translate::noop('{error:title_ACSPARAMS}'),
+            'ARSPARAMS' => \SimpleSAML\Locale\Translate::noop('{error:title_ARSPARAMS}'),
+            'AUTHSOURCEERROR' => \SimpleSAML\Locale\Translate::noop('{error:title_AUTHSOURCEERROR}'),
+            'BADREQUEST' => \SimpleSAML\Locale\Translate::noop('{error:title_BADREQUEST}'),
+            'CASERROR' => \SimpleSAML\Locale\Translate::noop('{error:title_CASERROR}'),
+            'CONFIG' => \SimpleSAML\Locale\Translate::noop('{error:title_CONFIG}'),
+            'CREATEREQUEST' => \SimpleSAML\Locale\Translate::noop('{error:title_CREATEREQUEST}'),
+            'DISCOPARAMS' => \SimpleSAML\Locale\Translate::noop('{error:title_DISCOPARAMS}'),
+            'GENERATEAUTHNRESPONSE' => \SimpleSAML\Locale\Translate::noop('{error:title_GENERATEAUTHNRESPONSE}'),
+            'INVALIDCERT' => \SimpleSAML\Locale\Translate::noop('{error:title_INVALIDCERT}'),
+            'LDAPERROR' => \SimpleSAML\Locale\Translate::noop('{error:title_LDAPERROR}'),
+            'LOGOUTINFOLOST' => \SimpleSAML\Locale\Translate::noop('{error:title_LOGOUTINFOLOST}'),
+            'LOGOUTREQUEST' => \SimpleSAML\Locale\Translate::noop('{error:title_LOGOUTREQUEST}'),
+            'MEMCACHEDOWN' => \SimpleSAML\Locale\Translate::noop('{error:title_MEMCACHEDOWN}'),
+            'METADATA' => \SimpleSAML\Locale\Translate::noop('{error:title_METADATA}'),
+            'METADATANOTFOUND' => \SimpleSAML\Locale\Translate::noop('{error:title_METADATANOTFOUND}'),
+            'NOACCESS' => \SimpleSAML\Locale\Translate::noop('{error:title_NOACCESS}'),
+            'NOCERT' => \SimpleSAML\Locale\Translate::noop('{error:title_NOCERT}'),
+            'NORELAYSTATE' => \SimpleSAML\Locale\Translate::noop('{error:title_NORELAYSTATE}'),
+            'NOSTATE' => \SimpleSAML\Locale\Translate::noop('{error:title_NOSTATE}'),
+            'NOTFOUND' => \SimpleSAML\Locale\Translate::noop('{error:title_NOTFOUND}'),
+            'NOTFOUNDREASON' => \SimpleSAML\Locale\Translate::noop('{error:title_NOTFOUNDREASON}'),
+            'NOTSET' => \SimpleSAML\Locale\Translate::noop('{error:title_NOTSET}'),
+            'NOTVALIDCERT' => \SimpleSAML\Locale\Translate::noop('{error:title_NOTVALIDCERT}'),
+            'PROCESSASSERTION' => \SimpleSAML\Locale\Translate::noop('{error:title_PROCESSASSERTION}'),
+            'PROCESSAUTHNREQUEST' => \SimpleSAML\Locale\Translate::noop('{error:title_PROCESSAUTHNREQUEST}'),
+            'RESPONSESTATUSNOSUCCESS' => \SimpleSAML\Locale\Translate::noop('{error:title_RESPONSESTATUSNOSUCCESS}'),
+            'SLOSERVICEPARAMS' => \SimpleSAML\Locale\Translate::noop('{error:title_SLOSERVICEPARAMS}'),
+            'SSOPARAMS' => \SimpleSAML\Locale\Translate::noop('{error:title_SSOPARAMS}'),
+            'UNHANDLEDEXCEPTION' => \SimpleSAML\Locale\Translate::noop('{error:title_UNHANDLEDEXCEPTION}'),
+            'UNKNOWNCERT' => \SimpleSAML\Locale\Translate::noop('{error:title_UNKNOWNCERT}'),
+            'USERABORTED' => \SimpleSAML\Locale\Translate::noop('{error:title_USERABORTED}'),
+            'WRONGUSERPASS' => \SimpleSAML\Locale\Translate::noop('{error:title_WRONGUSERPASS}'),
+        );
+    }
+
+
+    /**
+     * Fetch all translation strings for error code titles.
+     *
+     * Extend this to add error codes.
+     *
+     * @return array A map from error code to error code title
+     */
+    public static function getAllErrorCodeTitles()
+    {
+        return self::defaultGetAllErrorCodeTitles();
+    }
+
+
+    /**
+     * Fetch all default translation strings for error code descriptions.
+     *
+     * @return string A map from error code to error code description
+     */
+    final public static function defaultGetAllErrorCodeDescriptions()
+    {
+        return array(
+            'ACSPARAMS' => \SimpleSAML\Locale\Translate::noop('{error:descr_ACSPARAMS}'),
+            'ARSPARAMS' => \SimpleSAML\Locale\Translate::noop('{error:descr_ARSPARAMS}'),
+            'AUTHSOURCEERROR' => \SimpleSAML\Locale\Translate::noop('{error:descr_AUTHSOURCEERROR}'),
+            'BADREQUEST' => \SimpleSAML\Locale\Translate::noop('{error:descr_BADREQUEST}'),
+            'CASERROR' => \SimpleSAML\Locale\Translate::noop('{error:descr_CASERROR}'),
+            'CONFIG' => \SimpleSAML\Locale\Translate::noop('{error:descr_CONFIG}'),
+            'CREATEREQUEST' => \SimpleSAML\Locale\Translate::noop('{error:descr_CREATEREQUEST}'),
+            'DISCOPARAMS' => \SimpleSAML\Locale\Translate::noop('{error:descr_DISCOPARAMS}'),
+            'GENERATEAUTHNRESPONSE' => \SimpleSAML\Locale\Translate::noop('{error:descr_GENERATEAUTHNRESPONSE}'),
+            'INVALIDCERT' => \SimpleSAML\Locale\Translate::noop('{error:descr_INVALIDCERT}'),
+            'LDAPERROR' => \SimpleSAML\Locale\Translate::noop('{error:descr_LDAPERROR}'),
+            'LOGOUTINFOLOST' => \SimpleSAML\Locale\Translate::noop('{error:descr_LOGOUTINFOLOST}'),
+            'LOGOUTREQUEST' => \SimpleSAML\Locale\Translate::noop('{error:descr_LOGOUTREQUEST}'),
+            'MEMCACHEDOWN' => \SimpleSAML\Locale\Translate::noop('{error:descr_MEMCACHEDOWN}'),
+            'METADATA' => \SimpleSAML\Locale\Translate::noop('{error:descr_METADATA}'),
+            'METADATANOTFOUND' => \SimpleSAML\Locale\Translate::noop('{error:descr_METADATANOTFOUND}'),
+            'NOACCESS' => \SimpleSAML\Locale\Translate::noop('{error:descr_NOACCESS}'),
+            'NOCERT' => \SimpleSAML\Locale\Translate::noop('{error:descr_NOCERT}'),
+            'NORELAYSTATE' => \SimpleSAML\Locale\Translate::noop('{error:descr_NORELAYSTATE}'),
+            'NOSTATE' => \SimpleSAML\Locale\Translate::noop('{error:descr_NOSTATE}'),
+            'NOTFOUND' => \SimpleSAML\Locale\Translate::noop('{error:descr_NOTFOUND}'),
+            'NOTFOUNDREASON' => \SimpleSAML\Locale\Translate::noop('{error:descr_NOTFOUNDREASON}'),
+            'NOTSET' => \SimpleSAML\Locale\Translate::noop('{error:descr_NOTSET}'),
+            'NOTVALIDCERT' => \SimpleSAML\Locale\Translate::noop('{error:descr_NOTVALIDCERT}'),
+            'PROCESSASSERTION' => \SimpleSAML\Locale\Translate::noop('{error:descr_PROCESSASSERTION}'),
+            'PROCESSAUTHNREQUEST' => \SimpleSAML\Locale\Translate::noop('{error:descr_PROCESSAUTHNREQUEST}'),
+            'RESPONSESTATUSNOSUCCESS' => \SimpleSAML\Locale\Translate::noop('{error:descr_RESPONSESTATUSNOSUCCESS}'),
+            'SLOSERVICEPARAMS' => \SimpleSAML\Locale\Translate::noop('{error:descr_SLOSERVICEPARAMS}'),
+            'SSOPARAMS' => \SimpleSAML\Locale\Translate::noop('{error:descr_SSOPARAMS}'),
+            'UNHANDLEDEXCEPTION' => \SimpleSAML\Locale\Translate::noop('{error:descr_UNHANDLEDEXCEPTION}'),
+            'UNKNOWNCERT' => \SimpleSAML\Locale\Translate::noop('{error:descr_UNKNOWNCERT}'),
+            'USERABORTED' => \SimpleSAML\Locale\Translate::noop('{error:descr_USERABORTED}'),
+            'WRONGUSERPASS' => \SimpleSAML\Locale\Translate::noop('{error:descr_WRONGUSERPASS}'),
+        );
+    }
+
+    /**
+     * Fetch all translation strings for error code descriptions.
+     *
+     * Extend this to add error codes.
+     *
+     * @return string A map from error code to error code description
+     */
+    public static function getAllErrorCodeDescriptions()
+    {
+        return self::defaultGetAllErrorCodeDescriptions();
+    }
+
+
+    /**
+     * Get a map of both errorcode titles and descriptions
+     *
+     * Convenience-method for template-callers
+     *
+     * @return array An array containing both errorcode maps.
+     */
+    public static function getAllErrorCodeMessages()
+    {
+        return array(
+            'title' => self::getAllErrorCodeTitles(),
+            'descr' => self::getAllErrorCodeDescriptions(),
+        );
+    }
+
+
+    /**
+     * Fetch a translation string for a title for a given error code.
+     *
+     * @param string $errorCode The error code to look up
+     *
+     * @return string A string to translate
+     */
+    public static function getErrorCodeTitle($errorCode)
+    {
+        $errorCodeTitles = self::getAllErrorCodeTitles();
+        return $errorCodeTitles[$errorCode];
+    }
+
+
+    /**
+     * Fetch a translation string for a description for a given error code.
+     *
+     * @param string $errorCode The error code to look up
+     *
+     * @return string A string to translate
+     */
+    public static function getErrorCodeDescription($errorCode)
+    {
+        $errorCodeDescriptions = self::getAllErrorCodeDescriptions();
+        return $errorCodeDescriptions[$errorCode];
+    }
+
+
+    /**
+     * Get both title and description for a specific error code
+     *
+     * Convenience-method for template-callers
+     *
+     * @param string $errorCode The error code to look up
+     *
+     * @return array An array containing both errorcode strings.
+     */
+    public static function getErrorCodeMessage($errorCode)
+    {
+        return array(
+            'title' => self::getErrorCodeTitle($errorCode),
+            'descr' => self::getErrorCodeDescription($errorCode),
+        );
+    }
+}
diff --git a/modules/authX509/templates/X509error.php b/modules/authX509/templates/X509error.php
index 1f272301cedaaa72fef55c95994b11b012d3d8d6..459bbf2c51cc1537c8f1b5c600f651550c0e2d46 100644
--- a/modules/authX509/templates/X509error.php
+++ b/modules/authX509/templates/X509error.php
@@ -11,8 +11,8 @@ if ($this->data['errorcode'] !== NULL) {
 	<div style="border-left: 1px solid #e8e8e8; border-bottom: 1px solid #e8e8e8; background: #f5f5f5">
 		<img src="/<?php echo $this->data['baseurlpath']; ?>resources/icons/experience/gtk-dialog-error.48x48.png" class="float-l" style="margin: 15px" alt="" />
 		<h2><?php echo $this->t('{login:error_header}'); ?></h2>
-		<p><b><?php echo $this->t('{errors:title_' . $this->data['errorcode'] . '}'); ?></b></p>
-		<p><?php echo $this->t('{errors:descr_' . $this->data['errorcode'] . '}'); ?></p>
+		<p><b><?php echo $this->t($this->data['errorcodes']['title'][$this->data['errorcode']]); ?></b></p>
+		<p><?php echo $this->t($this->data['errorcodes']['descr'][$this->data['errorcode']]); ?></p>
 	</div>
 <?php
 }
diff --git a/modules/authX509/www/expirywarning.php b/modules/authX509/www/expirywarning.php
index b5faa22a24d9ac1fac47efcd89c345a5f7c77794..02b2b221e1684d400d838ddd3dffa949a6c490a4 100644
--- a/modules/authX509/www/expirywarning.php
+++ b/modules/authX509/www/expirywarning.php
@@ -27,4 +27,5 @@ $t->data['target'] = SimpleSAML\Module::getModuleURL('authX509/expirywarning.php
 $t->data['data'] = array('StateId' => $id);
 $t->data['daysleft'] = $state['daysleft'];
 $t->data['renewurl'] = $state['renewurl'];
+$t->data['errorcodes'] = SimpleSAML\Error\Errorcodes::getAllErrorCodeMessages();
 $t->show();
diff --git a/modules/authYubiKey/templates/yubikeylogin.php b/modules/authYubiKey/templates/yubikeylogin.php
index 732904decfe2eb202f1a2583d292c3b6070ace7b..0c9f3e48e31475a9cd202a1bb7973d0695f99d73 100644
--- a/modules/authYubiKey/templates/yubikeylogin.php
+++ b/modules/authYubiKey/templates/yubikeylogin.php
@@ -12,8 +12,8 @@ if ($this->data['errorcode'] !== NULL) {
 	<div style="border-left: 1px solid #e8e8e8; border-bottom: 1px solid #e8e8e8; background: #f5f5f5">
 		<img src="/<?php echo $this->data['baseurlpath']; ?>resources/icons/experience/gtk-dialog-error.48x48.png" class="float-l" style="margin: 15px" alt="" />
 		<h2><?php echo $this->t('{login:error_header}'); ?></h2>
-		<p><b><?php echo $this->t('{errors:title_' . $this->data['errorcode'] . '}'); ?></b></p>
-		<p><?php echo $this->t('{errors:descr_' . $this->data['errorcode'] . '}'); ?></p>
+		<p><b><?php echo $this->t($this->data['errorcodes']['title'][$this->data['errorcode']]); ?></b></p>
+		<p><?php echo $this->t($this->data['errorcodes']['descr'][$this->data['errorcode']]); ?></p>
 	</div>
 <?php
 }
diff --git a/modules/authYubiKey/www/yubikeylogin.php b/modules/authYubiKey/www/yubikeylogin.php
index 7873828e6cd2612d2da36b1bde677f8140db578e..52311b5bfe0e0d30d1352a69c5922a1f848fc50f 100644
--- a/modules/authYubiKey/www/yubikeylogin.php
+++ b/modules/authYubiKey/www/yubikeylogin.php
@@ -31,6 +31,7 @@ $globalConfig = SimpleSAML_Configuration::getInstance();
 $t = new SimpleSAML_XHTML_Template($globalConfig, 'authYubiKey:yubikeylogin.php');
 $t->data['stateparams'] = array('AuthState' => $authStateId);
 $t->data['errorcode'] = $errorCode;
+$t->data['errorcodes'] = SimpleSAML\Error\Errorcodes::getAllErrorCodeMessages();
 $t->data['logo_url'] = SimpleSAML\Module::getModuleURL('authYubiKey/resources/logo.jpg');
 $t->data['devicepic_url'] = SimpleSAML\Module::getModuleURL('authYubiKey/resources/yubikey.jpg');
 $t->show();
diff --git a/modules/core/templates/loginuserpass.php b/modules/core/templates/loginuserpass.php
index f9d739c6205f33ceb4636a7196d8c77d519e5c87..5baaec7aef0018fc65ff4d63cd0d17a2c1861cd7 100644
--- a/modules/core/templates/loginuserpass.php
+++ b/modules/core/templates/loginuserpass.php
@@ -20,16 +20,10 @@ if ($this->data['errorcode'] !== null) {
         <h2><?php echo $this->t('{login:error_header}'); ?></h2>
 
         <p><strong><?php
-                echo htmlspecialchars($this->t(
-                    '{errors:title_'.$this->data['errorcode'].'}',
-                    $this->data['errorparams']
-                )); ?></strong></p>
+            echo htmlspecialchars($this->t($this->data['errorcodes']['title'][$this->data['errorcode']], $this->data['errorparams'])); ?></strong></p>
 
         <p><?php
-            echo htmlspecialchars($this->t(
-                '{errors:descr_'.$this->data['errorcode'].'}',
-                $this->data['errorparams']
-            )); ?></p>
+            echo htmlspecialchars($this->t($this->data['errorcodes']['descr'][$this->data['errorcode']], $this->data['errorparams'])); ?></p>
     </div>
 <?php
 }
diff --git a/modules/core/www/loginuserpass.php b/modules/core/www/loginuserpass.php
index 545032027b9d4ce5898e87b384713de5e1ab9ba6..9a2958b08ca510a00da71e6d8f1e847715e0485c 100644
--- a/modules/core/www/loginuserpass.php
+++ b/modules/core/www/loginuserpass.php
@@ -93,6 +93,7 @@ if (array_key_exists('forcedUsername', $state)) {
 }
 $t->data['links'] = $source->getLoginLinks();
 $t->data['errorcode'] = $errorCode;
+$t->data['errorcodes'] = SimpleSAML\Error\Errorcodes::getAllErrorCodeMessages();
 $t->data['errorparams'] = $errorParams;
 
 if (isset($state['SPMetadata'])) {
diff --git a/modules/core/www/loginuserpassorg.php b/modules/core/www/loginuserpassorg.php
index 23db59831799e5836d4ded930a136be00894c596..4f33b36f186b1b790e8e6fa8777b592a9cbe9399 100644
--- a/modules/core/www/loginuserpassorg.php
+++ b/modules/core/www/loginuserpassorg.php
@@ -81,6 +81,7 @@ $t->data['rememberMeEnabled'] = false;
 $t->data['rememberMeChecked'] = false;
 if (isset($_COOKIE[$source->getAuthId() . '-username'])) $t->data['rememberUsernameChecked'] = TRUE;
 $t->data['errorcode'] = $errorCode;
+$t->data['errorcodes'] = SimpleSAML\Error\Errorcodes::getAllErrorCodeMessages();
 $t->data['errorparams'] = $errorParams;
 
 if ($organizations !== NULL) {