diff --git a/packages/app-shell/src/app-shell.js b/packages/app-shell/src/app-shell.js
index 95ea104586969c1fdc91950bb936d3141132227d..53397c4e4edd3ae6a8c8d12aaa31d53ecf9276f0 100644
--- a/packages/app-shell/src/app-shell.js
+++ b/packages/app-shell/src/app-shell.js
@@ -74,6 +74,7 @@ export class AppShell extends ScopedElementsMixin(DBPLitElement) {
         this.initateOpenMenu = false;
 
         this.auth = {};
+        this.langFile = '';
     }
 
     static get scopedElements() {
@@ -271,6 +272,7 @@ export class AppShell extends ScopedElementsMixin(DBPLitElement) {
             buildTime: {type: String, attribute: 'build-time'},
             env: {type: String},
             auth: {type: Object},
+            langFile: {type: String, attribute: 'lang-file'},
         };
     }
 
@@ -701,7 +703,7 @@ export class AppShell extends ScopedElementsMixin(DBPLitElement) {
                 background-position:center center;
                 margin: 0 0.5% 0 1.5%;
                 font-size:94%;
-            } 
+            }
             */
 
             .menu a {
diff --git a/packages/common/components.js b/packages/common/components.js
index 6a42c6afc7809394b9e260abd2f512b61ddec743..ac6917b0663c2a19190e69bb5b7653a8a3f681e6 100644
--- a/packages/common/components.js
+++ b/packages/common/components.js
@@ -7,6 +7,7 @@ import {
     MiniSpinner,
     Spinner,
     Translated,
+    Translation
 } from './index';
 
 commonUtils.defineCustomElement('dbp-mini-spinner', MiniSpinner);
@@ -16,3 +17,4 @@ commonUtils.defineCustomElement('dbp-button', Button);
 commonUtils.defineCustomElement('dbp-loading-button', LoadingButton);
 commonUtils.defineCustomElement('dbp-inline-notification', InlineNotification);
 commonUtils.defineCustomElement('dbp-translated', Translated);
+commonUtils.defineCustomElement('dbp-translation', Translation);
diff --git a/packages/common/dbp-common-demo.js b/packages/common/dbp-common-demo.js
index fad1b686ab060edc66778e4bd13431b2c895c616..5f5b9bc0f94bd8b218ca979e82d531b869e46193 100644
--- a/packages/common/dbp-common-demo.js
+++ b/packages/common/dbp-common-demo.js
@@ -12,6 +12,7 @@ import {
     Spinner,
     InlineNotification,
     Translated,
+    Translation,
 } from './index.js';
 
 export class DbpCommonDemo extends ScopedElementsMixin(LitElement) {
@@ -20,6 +21,7 @@ export class DbpCommonDemo extends ScopedElementsMixin(LitElement) {
         this._i18n = createInstance();
         this.lang = this._i18n.language;
         this.noAuth = false;
+        this.langFile = '';
     }
 
     static get scopedElements() {
@@ -31,6 +33,7 @@ export class DbpCommonDemo extends ScopedElementsMixin(LitElement) {
             'dbp-loading-button': LoadingButton,
             'dbp-inline-notification': InlineNotification,
             'dbp-translated': Translated,
+            'dbp-translation': Translation
         };
 
         if (customElements.get('dbp-auth')) {
@@ -44,6 +47,7 @@ export class DbpCommonDemo extends ScopedElementsMixin(LitElement) {
         return {
             lang: {type: String},
             noAuth: {type: Boolean, attribute: 'no-auth'},
+            langFile: {type: String, attribute: 'lang-file'},
         };
     }
 
@@ -297,6 +301,9 @@ html {
                             </div>
                         </dbp-translated>
                     </div>
+                    <div class="control" id="dbp-translation-demo">
+                        <dbp-translation key="toolkit-showcase" subscribe="lang, lang-file"></dbp-translation>
+                    </div>
                 </div>
             </section>
         `;
diff --git a/packages/common/index.js b/packages/common/index.js
index 0009a2f09e6baac0872c90addb531afd3ed31203..3d7467fc55c3290890d31f4c2164949c6f726323 100644
--- a/packages/common/index.js
+++ b/packages/common/index.js
@@ -6,6 +6,7 @@ import {Button, LoadingButton} from './src/button.js';
 import {Spinner} from './src/spinner.js';
 import {InlineNotification} from './src/inline-notification.js';
 import {Translated} from './src/translated';
+import {Translation} from './src/translation';
 import {AdapterLitElement} from './src/adapter-lit-element.js';
 
 export {EventBus, createLinkedAbortController, createTimeoutAbortSignal};
@@ -14,7 +15,7 @@ export {MiniSpinner};
 export {Button, LoadingButton};
 export {Spinner};
 export {InlineNotification};
-export {Translated};
+export {Translated, Translation};
 export * from './src/logger.js';
 export * from './src/utils.js';
 export {AdapterLitElement};
diff --git a/packages/common/jsonld.js b/packages/common/jsonld.js
index 021a62dbe3bb734db0b62eeb4d6d2222bfd38351..525e432b3f4a17424f2cb9453ebedacdc9ad7a67 100644
--- a/packages/common/jsonld.js
+++ b/packages/common/jsonld.js
@@ -297,7 +297,7 @@ export default class JSONLD {
     }
 }
 
-JSONLD._i18n = createInstance();
+JSONLD._i18n = await createInstance();
 JSONLD.instances = {};
 JSONLD.successFunctions = {};
 JSONLD.failureFunctions = {};
diff --git a/packages/common/src/i18n.js b/packages/common/src/i18n.js
index 6623ebdc2d5144e117f9df22d98a8b866ae3c1ba..0374a825419d2f2dea5fd68c5b9b0f76c019b9bb 100644
--- a/packages/common/src/i18n.js
+++ b/packages/common/src/i18n.js
@@ -6,3 +6,26 @@ import en from './i18n/en/translation.json';
 export function createInstance() {
     return _createInstance({en: en, de: de}, 'de', 'en');
 }
+
+export async function createInstanceAsync(langFile) {
+    // check if a path to language files is given
+    if(langFile) {
+      // request german lang file asynchronously
+      let result = await
+          fetch(langFile + 'de/translation.json', {
+              headers: {'Content-Type': 'application/json'},
+          });
+      const dynDe = await result.json();
+
+      // request english lang file asynchronously
+      result = await
+          fetch(langFile + 'en/translation.json', {
+              headers: {'Content-Type': 'application/json'},
+          });
+      const dynEn = await result.json();
+
+      return _createInstance({en: dynEn, de: dynDe}, 'de', 'en');
+    }
+
+    return _createInstance({en: en, de: de}, 'de', 'en');
+}
diff --git a/packages/common/src/i18n/de/translation.json b/packages/common/src/i18n/de/translation.json
index 51cf793c6cb4108b18d8cab47b63959663ec8590..b9daed00bc4e840bf1e885e4cff6f5a5e9c4d229 100644
--- a/packages/common/src/i18n/de/translation.json
+++ b/packages/common/src/i18n/de/translation.json
@@ -7,5 +7,6 @@
         "api-documentation-server": "Verbindung zum apiDocumentation API Server {{apiDocUrl}} fehlgeschlagen!",
         "error-api-server": "Verbindung zum API Server {{apiUrl}} fehlgeschlagen!",
         "error-hydra-documentation-url-not-set": "Hydra apiDocumentation URL wurden für server {{apiUrl}} nicht gesetzt!"
-    }
+    },
+    "toolkit-showcase": "Dieser Text wird mithilfe von i18n Englisch wenn man die Sprache auf Englisch stellt."
 }
diff --git a/packages/common/src/i18n/en/translation.json b/packages/common/src/i18n/en/translation.json
index e1036a6580fd180af131a4c2744c8c4fef2745fb..1e17839ab74d17ebb29bd8bf73c721a13533280a 100644
--- a/packages/common/src/i18n/en/translation.json
+++ b/packages/common/src/i18n/en/translation.json
@@ -7,5 +7,6 @@
         "api-documentation-server": "Connection to apiDocumentation server {{apiDocUrl}} failed!",
         "error-api-server": "Connection to api server {{apiUrl}} failed!",
         "error-hydra-documentation-url-not-set": "Hydra apiDocumentation url was not set for server {{apiUrl}}!"
-    }
+    },
+    "toolkit-showcase": "This text will be translated to german using i18n when the user changes the language to german."
 }
diff --git a/packages/common/src/translation.js b/packages/common/src/translation.js
new file mode 100644
index 0000000000000000000000000000000000000000..782dd3e278f423ed284a6063423ae1ba35eaf6f2
--- /dev/null
+++ b/packages/common/src/translation.js
@@ -0,0 +1,67 @@
+import {css, html} from 'lit';
+import {classMap} from 'lit/directives/class-map.js';
+import {until} from 'lit/directives/until.js';
+import DBPLitElement from '../dbp-lit-element';
+import {createInstanceAsync} from './i18n.js';
+
+export class Translation extends DBPLitElement {
+    constructor() {
+        super();
+        this.key = '';
+        this.lang = '';
+        this.langFile = '';
+    }
+
+    static get properties() {
+        return {
+            ...super.properties,
+            key: {type: String},
+            lang: {type: String},
+            langFile: {type: String, attribute: 'lang-file'},
+        };
+    }
+
+    static get styles() {
+        // language=css
+        return css`
+            .hidden {
+                display: none;
+            }
+        `;
+    }
+
+    connectedCallback() {
+      super.connectedCallback();
+      this._i18n = createInstanceAsync(this.langFile);
+    }
+
+    update(changedProperties) {
+        changedProperties.forEach((oldValue, propName) => {
+            switch (propName) {
+                case 'lang':
+                    let lang = this.lang;
+                    this._i18n.then(function(response) {
+                      response.changeLanguage(lang);
+                    });
+                    break;
+            }
+        });
+
+        super.update(changedProperties);
+    }
+
+    render() {
+        // save global key in local variable for async use
+        let key = this.key;
+
+        // async request to i18n translation
+        const translation = this._i18n.then(function(response){
+          return response.t(key);
+        });
+
+        // load translation text when available, otherweise display "Loading.."
+        return html`
+            ${until(translation, html`<span>Loading..</span>`)}
+        `;
+    }
+}
diff --git a/toolkit-showcase/assets/common.metadata.json b/toolkit-showcase/assets/common.metadata.json
index de1f70c7ede8728cc3cd6b0b8922da8137310763..51e4892cb53180deb82a6224e9dd0c836c9f6452 100644
--- a/toolkit-showcase/assets/common.metadata.json
+++ b/toolkit-showcase/assets/common.metadata.json
@@ -14,5 +14,5 @@
         "de": "Gemeinsame Web Components",
         "en": "Common web components"
     },
-    "subscribe": "lang,entry-point-url"
+    "subscribe": "lang,entry-point-url,lang-file"
 }
diff --git a/toolkit-showcase/assets/dbp-toolkit-showcase.html.ejs b/toolkit-showcase/assets/dbp-toolkit-showcase.html.ejs
index ef38009f126c9bdba6e6f9009e443eb59867f93d..1cf3784a8bcfde73d276e6951838c2b011cbf373 100644
--- a/toolkit-showcase/assets/dbp-toolkit-showcase.html.ejs
+++ b/toolkit-showcase/assets/dbp-toolkit-showcase.html.ejs
@@ -11,7 +11,7 @@
 
     <!-- PWA manifest file -->
     <link rel="manifest" href="<%= getUrl(name + '.manifest.json') %>">
-    
+
     <!-- PWA iphone -->
     <link rel="apple-touch-icon" sizes="180x180" href="<%= getPrivateUrl('apple-touch-icon.png') %>">
     <link rel="icon" type="image/png" sizes="32x32" href="<%= getPrivateUrl('icon-32x32.png') %>">
@@ -113,6 +113,7 @@
 <<%= name %>
     provider-root
     lang="de"
+    lang-file="<%= getUrl('/src/i18n/') %>"
     entry-point-url="<%= entryPointURL %>"
     nextcloud-auth-url="<%= nextcloudWebAppPasswordURL %>"
     nextcloud-web-dav-url="<%= nextcloudWebDavURL %>"
diff --git a/toolkit-showcase/src/dbp-common-demo-activity.js b/toolkit-showcase/src/dbp-common-demo-activity.js
index fc633b9d37f5ba3362f6bbe99f6b75f8ab107010..6735845541387b0098d6f61bf329470508bf84c3 100644
--- a/toolkit-showcase/src/dbp-common-demo-activity.js
+++ b/toolkit-showcase/src/dbp-common-demo-activity.js
@@ -13,6 +13,7 @@ class DbpCommonDemoActivity extends ScopedElementsMixin(AdapterLitElement) {
         super();
         this.lang = 'en';
         this.entryPointUrl = '';
+        this.langFile = '';
     }
 
     static get scopedElements() {
@@ -25,6 +26,7 @@ class DbpCommonDemoActivity extends ScopedElementsMixin(AdapterLitElement) {
         return {
             ...super.properties,
             lang: {type: String},
+            langFile: {type: String, attribute: 'lang-file'},
             entryPointUrl: {type: String, attribute: 'entry-point-url'},
         };
     }
@@ -63,6 +65,7 @@ class DbpCommonDemoActivity extends ScopedElementsMixin(AdapterLitElement) {
             <dbp-common-demo
                 id="demo"
                 lang="${this.lang}"
+                lang-file="${this.langFile}"
                 entry-point-url="${this.entryPointUrl}"></dbp-common-demo>
         `;
     }
diff --git a/toolkit-showcase/src/i18n/de/translation.json b/toolkit-showcase/src/i18n/de/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..d51b20bd684577823c83c44264231d626cbcfdbf
--- /dev/null
+++ b/toolkit-showcase/src/i18n/de/translation.json
@@ -0,0 +1,3 @@
+{
+    "toolkit-showcase": "Dieser Text wird mithilfe von i18n aus einer benutzerdefinierten Sprachdatei gelesen und ins Englische übersetzt wenn man die Sprache auf Englisch stellt."
+}
diff --git a/toolkit-showcase/src/i18n/en/translation.json b/toolkit-showcase/src/i18n/en/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..789bde6fc2f8a1ae8df720d3f736c19b5bb2315e
--- /dev/null
+++ b/toolkit-showcase/src/i18n/en/translation.json
@@ -0,0 +1,12 @@
+{
+    "error": {
+        "connection-to-server-refused": "Connection to server refused!",
+        "summary": "An error occurred"
+    },
+    "jsonld": {
+        "api-documentation-server": "Connection to apiDocumentation server {{apiDocUrl}} failed!",
+        "error-api-server": "Connection to api server {{apiUrl}} failed!",
+        "error-hydra-documentation-url-not-set": "Hydra apiDocumentation url was not set for server {{apiUrl}}!"
+    },
+    "toolkit-showcase": "This text will be translated to german using i18n with a user defined language file when the language is changed to german."
+}