diff --git a/data/languages.yaml b/data/languages.yaml
new file mode 100644
index 0000000..e4eab9f
--- /dev/null
+++ b/data/languages.yaml
@@ -0,0 +1,34 @@
+---
+af: Afrikaans
+ar: العربية
+ca: Català
+cs: Česky
+da: Dansk
+de: Deutsch
+el: Ελληνικά
+en: English
+eo: Esperanto
+es: Español
+fa: فارسی
+fi: Suomi
+fr: Français
+gl: Galego
+he: עברית
+hu: Magyar
+it: Italiano
+ja: 日本語
+ko: 한국어
+nl: Nederlands
+'no': Norsk (bokmål)
+pl: Polski
+pt: Português
+ro: Română
+ru: Русский
+sat: ᱥᱟᱱᱛᱟᱲᱤ
+si: සිංහල
+sr: Српски
+sv: Svenska
+tr: Türkçe
+uk: Українська
+vi: Việtnam
+zh: 中文
diff --git a/source/images/LanguageIcon.png b/source/images/LanguageIcon.png
new file mode 100755
index 0000000..aec3433
Binary files /dev/null and b/source/images/LanguageIcon.png differ
diff --git a/source/javascripts/site.js b/source/javascripts/site.js
index 3b7183b..cf2196a 100644
--- a/source/javascripts/site.js
+++ b/source/javascripts/site.js
@@ -1,3 +1,23 @@
document.getElementById('menu').addEventListener('click', evt =>
evt.target.parentElement.classList.toggle('show')
-)
\ No newline at end of file
+)
+
+document.addEventListener('DOMContentLoaded', () => {
+ for (const switcher of document.getElementsByClassName('language-switcher')) {
+ const control = switcher.querySelector('[aria-haspopup]');
+ control.addEventListener('click', event => {
+ const popupId = control.getAttribute('aria-controls');
+ if (! popupId) return;
+ const popup = document.getElementById(popupId);
+ if (! popup) return;
+ if (control.getAttribute('aria-expanded') === 'true') {
+ control.setAttribute('aria-expanded', 'false');
+ popup.setAttribute('aria-hidden', 'true');
+ } else {
+ control.setAttribute('aria-expanded', 'true');
+ popup.setAttribute('aria-hidden', 'false');
+
+ }
+ });
+ }
+});
diff --git a/source/layouts/layout.erb b/source/layouts/layout.erb
index 18ff900..534c6f8 100644
--- a/source/layouts/layout.erb
+++ b/source/layouts/layout.erb
@@ -28,6 +28,14 @@