From ab8f13a586299d958ae2831bb841af049520df5c Mon Sep 17 00:00:00 2001
From: Volker Schukai <volker.schukai@schukai.com>
Date: Wed, 4 Jun 2025 18:55:58 +0200
Subject: [PATCH] feat: Add i18n support in CustomElement template formatting

Summary of changes
- Enhanced `CustomElement` class with i18n property for template formatting. The property is initialized to false and can be enabled for internationalization support.
- Implemented `substituteI18n` function to handle i18n formatting for HTML content using a `Formatter` instance, which applies labels to the templates.
- Updated the `initHtmlContent` and `initShadowRoot` methods to call `substituteI18n`, thereby applying i18n formatting when rendering HTML.

- Updated the import sequence in the JSDOM tests to ensure consistent element polyfills are loaded before tests run.
- Added debug logging for key setting in the JSDOM initialization to assist in debugging any issues with key assignments from the `window` object.

These changes allow developers to specify an i18n option when using the `CustomElement`, enhancing localization capabilities for template rendering. The adjustments in the tests enhance reliability and traceability during the testing process.
---
 source/dom/customelement.mjs          | 27 +++++++++++++++++++++++++--
 test/cases/components/form/button.mjs | 13 ++++++-------
 test/util/jsdom.mjs                   |  6 ++++--
 3 files changed, 35 insertions(+), 11 deletions(-)

diff --git a/source/dom/customelement.mjs b/source/dom/customelement.mjs
index c4e2fcc4c..69b728787 100644
--- a/source/dom/customelement.mjs
+++ b/source/dom/customelement.mjs
@@ -297,6 +297,7 @@ class CustomElement extends HTMLElement {
 	 * @property {Object} templateFormatter.marker Specifies the marker for the templates.
 	 * @property {Function} templateFormatter.marker.open=null Specifies the opening marker for the templates.
 	 * @property {Function} templateFormatter.marker.close=null Specifies the closing marker for the templates.
+	 * @property {Boolean} templateFormatter.i18n=false Specifies whether the templates should be formatted with i18n.
 	 * @property {Boolean} eventProcessing=false Specifies whether the control processes events.
 	 * @since 1.8.0
 	 */
@@ -314,6 +315,7 @@ class CustomElement extends HTMLElement {
 					open: null,
 					close: null,
 				},
+				i18n : false,
 			},
 
 			eventProcessing: false,
@@ -1075,6 +1077,27 @@ function parseOptionsJSON(data) {
 	return validateObject(obj);
 }
 
+/**
+ * @private
+ * @param html
+ * @returns {*|string}
+ */
+function substituteI18n(html) {
+
+	if(!this.getOption("templateFormatter.i18n", false)) {
+		return html;
+	}
+
+	const labels = this.getOption("labels", {});
+	if (!(isObject(labels) || isIterable(labels))) {
+		return html;
+	}
+
+	const formatter = new Formatter(labels, {});
+	formatter.setMarker("i18n{", '}')
+	return formatter.format(html);
+}
+
 /**
  * @private
  * @return {initHtmlContent}
@@ -1090,7 +1113,7 @@ function initHtmlContent() {
 			if (isObject(mapping)) {
 				html = new Formatter(mapping, {}).format(html);
 			}
-			this.innerHTML = html;
+			this.innerHTML = substituteI18n.call(this, html);
 		}
 	}
 
@@ -1194,7 +1217,7 @@ function initShadowRoot() {
 		html = formatter.format(html);
 	}
 
-	this.shadowRoot.innerHTML = html;
+	this.shadowRoot.innerHTML = substituteI18n.call(this, html);
 	return this;
 }
 
diff --git a/test/cases/components/form/button.mjs b/test/cases/components/form/button.mjs
index 07292a08d..652e3edc3 100644
--- a/test/cases/components/form/button.mjs
+++ b/test/cases/components/form/button.mjs
@@ -38,13 +38,12 @@ describe('Button', function () {
     before(function (done) {
         initJSDOM().then(() => {
 
-            import("element-internals-polyfill").catch(e => done(e));
-
-            import("../../../../source/components/form/button.mjs").then((m) => {
-                Button = m['Button'];
-                done()
-            }).catch(e => done(e))
-
+            import("element-internals-polyfill").then(()=>{
+                import("../../../../source/components/form/button.mjs").then((m) => {
+                    Button = m['Button'];
+                    done()
+                }).catch(e => done(e))
+            }).catch(e => done(e));
 
         });
     })
diff --git a/test/util/jsdom.mjs b/test/util/jsdom.mjs
index 747f8618c..0153d3ce5 100644
--- a/test/util/jsdom.mjs
+++ b/test/util/jsdom.mjs
@@ -67,7 +67,7 @@ function initJSDOM(options) {
                     'InputEvent',
                     'KeyboardEvent',
                     'MutationObserver',
-                    'navigator',
+                //    'navigator',
                     'Node',
                     'NodeFilter',
                     'NodeList',
@@ -76,7 +76,9 @@ function initJSDOM(options) {
                     'XMLSerializer',
                 ].forEach(key => {
                     try {
-                        g[key] = window[key]    
+                        console.log("setting key", key);
+
+                        g[key] = window[key]
                     } catch(e) {
                         console.error("Error setting key", key, e);
                     }
-- 
GitLab