diff --git a/static/admin/js/init-alpine.js b/static/admin/js/init-alpine.js
index 5c75e35b..80af7f4d 100644
--- a/static/admin/js/init-alpine.js
+++ b/static/admin/js/init-alpine.js
@@ -163,4 +163,57 @@ function data() {
// ─────────────────────────────────────────────────────────────────
currentPage: ''
}
-}
\ No newline at end of file
+}
+
+/**
+ * Language selector component for i18n support
+ * Used by language_selector macros in templates
+ *
+ * @param {string} currentLang - Current language code (e.g., 'fr')
+ * @param {Array} enabledLanguages - Array of enabled language codes
+ * @returns {Object} Alpine.js component data
+ */
+function languageSelector(currentLang, enabledLanguages) {
+ return {
+ isLangOpen: false,
+ currentLang: currentLang || 'fr',
+ languages: enabledLanguages || ['fr', 'de', 'en'],
+ languageNames: {
+ 'en': 'English',
+ 'fr': 'Français',
+ 'de': 'Deutsch',
+ 'lb': 'Lëtzebuergesch'
+ },
+ languageFlags: {
+ 'en': 'gb',
+ 'fr': 'fr',
+ 'de': 'de',
+ 'lb': 'lu'
+ },
+ async setLanguage(lang) {
+ if (lang === this.currentLang) {
+ this.isLangOpen = false;
+ return;
+ }
+ try {
+ const response = await fetch('/api/v1/language/set', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({ language: lang })
+ });
+ if (response.ok) {
+ this.currentLang = lang;
+ window.location.reload();
+ }
+ } catch (error) {
+ console.error('Failed to set language:', error);
+ }
+ this.isLangOpen = false;
+ }
+ };
+}
+
+// Export to window for use in templates
+window.languageSelector = languageSelector;
\ No newline at end of file
diff --git a/static/shared/js/icons.js b/static/shared/js/icons.js
index 076e585a..c8094100 100644
--- a/static/shared/js/icons.js
+++ b/static/shared/js/icons.js
@@ -152,6 +152,7 @@ const Icons = {
'view-grid-add': ``,
'code': ``,
'template': ``,
+ 'translate': ``,
'view-boards': ``,
'menu-alt-2': ``,