diff --git a/config.php.dist b/config.php.dist
index d50454db05822cbe2a39aff0c6f83637199eb72d..916a23ebfe27e93e45112ee8e7799680eb0ed698 100644
--- a/config.php.dist
+++ b/config.php.dist
@@ -5,18 +5,23 @@ return [
     'mode' => 'boxes',
     // 'mode' => 'wayf',
 
-    // -- language to fetch texts in discofeed
+    // --- language for ui
     'lang' => 'en',
 
     // 'text-info' => '+++ Hinweis +++ Hinweis +++ Hinweis +++ Hinweis +++',
 
-    'text-before' => '<p>Studierende und Dozenten nutzen in der Regel das nachfolgende "Anmelden":</p>',
+    'text-before' => '<h2>AAI Login</h2>
+        <p>
+            Studierende und Dozenten nutzen in der Regel das Anmelden &uuml;ber ihre Organisation:
+        </p>',
+
     'text-after' => '<br><br><h2>Anmeldung ohne AAI</h2>
         <p>
             Klicken Sie auf: <br />
             <a href="/login.php" onclick="">Gast-Zugang</a>
         </p>',
 
+    // --- list of IDPs to show
     'idps' => [
         "https://aai-idp.unibe.ch/idp/shibboleth",
         "https://aai.insel.ch/idp/shibboleth",
@@ -24,6 +29,7 @@ return [
         "https://aai-logon.unibas.ch/idp/shibboleth",
     ],
     
+    // -- return URL after AAI login
     'return-url' => '/shib_login.php'
 
     // -- cache for discofeed
diff --git a/functions.js b/functions.js
index 5740f00b44fbbce18b8208582da7efc9d55ec3f1..cbd1b98878214f3891f86d4d72ba3873f1bb48dd 100644
--- a/functions.js
+++ b/functions.js
@@ -13,10 +13,14 @@
 // VARS
 // ----------------------------------------------------------------------
 
+// for development without local shibboleth - set to true
+var bShowLoginOnError = true;
+
 var oFilter = document.getElementById('filterbox');
+var sLang =   document.getElementById('lang').value;
+var aLang={};
 var sLsvar = 'aailogin-q';
 
-
 // ----------------------------------------------------------------------
 // FUNCTIONS
 // ----------------------------------------------------------------------
@@ -54,7 +58,7 @@ function applyfilter() {
                 var qPart = aQ[iPart];
                 var id2search = 'filterbtn-dot-' + qPart.replace(/^./, '');
                 var qPart = aQ[iPart];
-                if(aBtns[i].id == id2search){
+                if (aBtns[i].id == id2search) {
                     bMarked = true;
                     break;
                 }
@@ -94,11 +98,12 @@ function showFilterBox() {
             q = '';
         }
 
-
         oFilter.style.display = 'block';
 
-        oFilter.innerHTML = '<input type="text" id="filter" placeholder="" onchange="applyfilter()" onkeydown="applyfilter()" onkeyup="applyfilter()" value=""/>'
-            + '<a id="resetfilter" onclick="resetFilter(); return false;"> X </a><br>'
+        oFilter.innerHTML = ''
+            // + t('filter-idps-label') + '<br>'
+            + '<input type="text" id="filter" placeholder="'+t('filter-idps-placeholder')+'" onchange="applyfilter()" onkeydown="applyfilter()" onkeyup="applyfilter()" value=""/>'
+            + '<a href="#" id="resetfilter" onclick="resetFilter(); return false;" title="'+t('resetfilter-title')+'"> X </a><br>'
             ;
         setFilter(q);
 
@@ -109,10 +114,93 @@ function showFilterBox() {
     };
 };
 
+// ----------------------------------------------------------------------
+
+/**
+ * Get translated text by a given keyword
+ * @param {string} sKeyword key of the translation text
+ * @returns 
+ */
+function t(sKeyword) {
+    if (aLang[sKeyword]) {
+        return aLang[sKeyword];
+    } else {
+        return "MISS: [" + sKeyword + "] in [" + sLang + '.json]';
+    }
+}
+
+// ----------------------------------------------------------------------
+
+/**
+ * Show error message
+ * @param {string} $sMessage message text
+ */
+function showError($sMessage){
+    document.getElementById('errorbox').innerHTML = $sMessage;
+    document.getElementById('errorbox').style.display = 'block';
+}
+
+/**
+ * Check if user is logged in in shibboleth
+ * It shows an error, ib shibboleth is not available.
+ * It detects if a user is logged in.
+ */
+async function checkSession() {
+    const url = "/Shibboleth.sso/Session";
+    if(!bShowLoginOnError) {
+        document.getElementById('shib-select-idp').style.display = 'none';
+    }
+    try {
+        const response = await fetch(url);
+        if (!response.ok) {
+            showError(
+                t('shib-check-failed') + " " + response.status + '<br>' 
+                + t('shib-hide-login')
+            );
+            if(bShowLoginOnError) {
+                showFilterBox();
+            }
+            throw new Error(`Response status: ${response.status}`);
+        }
+
+        const body = await response.text();
+        if (body.indexOf("was not found") > 0) {
+            // console.log("Not logged in");
+            // We show the login to idp selection
+            showFilterBox();
+        } else {
+            // console.log("Logged in already");
+            document.getElementById('sshib-check-logged-in').style.display = 'block';
+        }
+
+        // ...
+    } catch (error) {
+        console.error(error.message);
+    }
+}
+
+
+
+async function init(){
+    fetch('lang/' + sLang + '.json')
+    .then(response => response.json())
+    .then(aLang => {
+        initStep2(aLang)
+    })
+    .catch(error => console.log(error))
+    ;
+
+}
+
+async function initStep2(langArray) {
+    aLang = langArray;
+    checkSession();
+}
+
 // ----------------------------------------------------------------------
 // INIT
 // ----------------------------------------------------------------------
 
-showFilterBox();
+init();
 
 // ----------------------------------------------------------------------
diff --git a/inc_functions.php b/inc_functions.php
index 0152efb0c39abaf9897e48f590422237d520ab3f..0c261af42b4430550dd82f0b41abc4e49f76046c 100644
--- a/inc_functions.php
+++ b/inc_functions.php
@@ -15,6 +15,7 @@
 // ----------------------------------------------------------------------
 
 require 'classes/shibd_discofeed.class.php';
+require 'classes/lang.class.php';
 
 // get the user config
 if (!file_exists('config.php')) {
@@ -24,9 +25,11 @@ if (!file_exists('config.php')) {
 
 $aConfig = require 'config.php';
 $SELFURL = isset($_SERVER['SERVER_NAME']) ? "https://" . $_SERVER['SERVER_NAME'] : '';
+
 $oDiscofeed = new shibd_discofeed($aConfig, $SELFURL);
 $aIdplist = $oDiscofeed->getIdps();
 
+$L = new lang($aConfig['lang'] ?? 'en');
 
 // ----------------------------------------------------------------------
 // functions
diff --git a/inc_mode_boxes.php b/inc_mode_boxes.php
index 32ee556a2afad4e114590d1d29373b4eb2dfbab6..cb6e1a0dbe27ad360fe6d34d34d89569ab267427 100644
--- a/inc_mode_boxes.php
+++ b/inc_mode_boxes.php
@@ -23,7 +23,7 @@ if (is_array($aIdplist) && count($aIdplist)) {
                         title="' . strip_tags($aEntry['_description']) . '"
                         >
                         <span>' . htmlentities($aEntry['_label']) . '</span><br>
-                        <span class="hidden"> .'. $aEntry['_tld'] . '</span>
+                        <span class="hidden">#'. $aEntry['_tld'] . '</span>
                         <img src="' . $aEntry['_image'] . '"><br>
                     </a>
                 '
@@ -35,9 +35,9 @@ if (is_array($aIdplist) && count($aIdplist)) {
         ksort($aTld);
         foreach(array_keys($aTld) as $sTld) {
             $sId='filterbtn-dot-'.str_replace('.', '', $sTld);
-            $sDomainFilter.='<a href="#" id="'.$sId.'" class="filterbutton" onclick="setFilter(\'.'.$sTld.' \'); return false;">.'.$sTld.'</a> ';
+            $sDomainFilter.='<a href="#" id="'.$sId.'" class="filterbutton" onclick="setFilter(\'#'.$sTld.' \'); return false;">.'.$sTld.'</a> ';
         }
-        $sDomainFilter = '<div id="filterByDomain">' . $sDomainFilter . '</div>';
+        $sDomainFilter = '<div id="filterByDomain">'. $sDomainFilter . '</div>';
     }
 
     echo '<div id="filterbox"></div>'
diff --git a/index.php b/index.php
index ae5b490ab73697d220b536b9aa8b2c372f138b0c..0ec913347cd8a6a488d22df00b80e321cc2117ab 100644
--- a/index.php
+++ b/index.php
@@ -32,7 +32,6 @@
 
 <body>
 
-
     <div id="head">
         <h1><?php echo $aConfig['title'] ?? 'AAI Login'; ?></h1>
     </div>
@@ -41,19 +40,38 @@
 
         <?php
 
+            // --- set language for js script
+            echo '<input type="hdden" id="lang" value="'.($aConfig['lang'] ?? 'en').'">';
+            
             // --- messages
             echo $SELFURL ? '' : showMessage('error', 'SELFURL is not set. $_SERVER[\'SERVER_NAME\'] is not available.');
             echo $aConfig['text-info'] ? showMessage('info', $aConfig['text-info']) : ''; 
 
-            // --- Text before
-            echo $aConfig['text-before'] ?? '' ; 
+            // --- id "errorbox" is used for javascript
+            echo '<div id="errorbox" class="msg error hidden"></div>';
+
+            // --- text block if the user is logged in already
+            echo '<div id="shib-check-logged-in" class="hidden_">
+            <h2>'.$L->t('logged-in-already-title').'</h2>
+            <p>
+                '.$L->t('logged-in-already-text').'<br>
+                <a href="/">'.$L->t('home').'</a>
+            </p>
+            </div>';
+
+            echo '<div id="shib-select-idp">';
 
-            // --- show possible logins
+                // --- Text before
+                echo $aConfig['text-before'] ?? '' ; 
 
-            include "inc_mode_".$aConfig['mode'].".php";
+                // --- show possible logins
+                include "inc_mode_".$aConfig['mode'].".php";
+
+            echo '</div>';
 
             // --- finishing text
             echo $aConfig['text-after'] ?? '';
+
         ?>
 
     </div>
diff --git a/login_aai.css b/login_aai.css
index 101f392321569716b9069018791a78ac888ff0b5..6475efb77aa4dbfbdad485f78934786c3543ae40 100644
--- a/login_aai.css
+++ b/login_aai.css
@@ -11,7 +11,7 @@
     --content-shadow: 0.5em 0.5em 2em #fff;
 
     --error-bg: #faa;
-    --error-color: #400;
+    --error-color: #800;
 
     --info-bg: #fc3;
     --info-color: #650;
@@ -20,7 +20,7 @@
 
     /** ---------- mode boxes */
     --idp-border: 1px solid #cde;
-    --idp-color: #46c;
+    --idp-color: #45c;
     --idp-shadow: 0 0 2em #eee inset;
 
     --idp-hover-shadow: 0 0 2em #eee;
@@ -32,9 +32,13 @@
     --btn-bg:#f8f8f8;
     --btn-color: #888;
 
-    --resetfilter-border: 2px solid #dcc;
-    --resetfilter-bg:#edd;
-    --resetfilter-color: #800;
+    --filterbtn-ative-border: 2px solid #eee;
+    --filterbtn-ative-bg:#fff;
+    --filterbtn-ative-color: #45c;
+
+    --resetfilter-border: 2px solid #ddd;
+    --resetfilter-bg: #f8f8f8;
+    --resetfilter-color: #888;
     
 }
 
@@ -69,7 +73,7 @@ div.content {
 }
 div.msg{
     border-radius: 1em;
-    border: 1px solid;
+    border: 2px solid;
     padding: 1em;
     margin-bottom: 1em;
 }
@@ -89,6 +93,10 @@ footer{
     text-align: right; 
 }
 
+.hidden{
+    display: none;
+}
+
 /** ---------- mode boxes: filterbar on top */
 
 div#filterbox{
@@ -104,29 +112,29 @@ a.filterbutton, a#resetfilter{
     border: var(--btn-border);
     border-radius: 0.2em;
     color: var(--btn-color);
-    font-size: 130%;
+    font-size: 110%;
     margin: 0 0.3em 0 0;
     opacity: 1;
     padding: 0.5em;
     text-decoration: none;
 }
 a.filterbutton.active{
-    background: #fff;
-    border-top-color: #68a;
+    background: var(--filterbtn-ative-bg);
+    border-top-color: var(--filterbtn-ative-border);
+    color: var(--filterbtn-ative-color);
 }
 input#filter{
     border: var(--inputfilter-border);
     padding: 0.5em;
     color:var(--inputfilter-color);
-    font-size: 130%;
-    width: 70%;
+    font-size: 110%;
+    width: 20em;
 }
 a#resetfilter{
     background:var(--resetfilter-bg);
     border: var(--resetfilter-border);
     color: var(--resetfilter-color);
     font-weight: bold;
-    float: right;
     margin: 0 0 0 0.3em;
     padding: 0.5em 2em;
 }