Skip to content

Commit aef8f75

Browse files
committed
Refactor translation and component loading into separate utility modules
1 parent 2ccbf06 commit aef8f75

File tree

3 files changed

+162
-66
lines changed

3 files changed

+162
-66
lines changed

public/js/main.js

Lines changed: 12 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,10 @@ function initRouter() {
575575
};
576576
}
577577

578+
// Import utilities
579+
import { detectAndImportModules, filterScriptTags } from './utils/component-loader.js';
580+
import { translateContainer, applyStoredLanguage } from './utils/translation-helper.js';
581+
578582
/**
579583
* Load a page from the server
580584
* @param {string} url - Page URL
@@ -585,17 +589,7 @@ async function loadPage(url) {
585589
console.log(`Loading page: ${url}`);
586590

587591
// Apply stored language before loading the page
588-
const storedLang = localStorage.getItem('profullstack-language');
589-
if (storedLang && window.app && window.app.localizer) {
590-
console.log(`Pre-load: Applying stored language: ${storedLang}`);
591-
window.app.localizer.setLanguage(storedLang);
592-
593-
// Force language application
594-
if (window.app.localizer.getLanguage() !== storedLang) {
595-
console.log(`Language mismatch in loadPage, forcing to: ${storedLang}`);
596-
window.app.localizer.setLanguage(storedLang);
597-
}
598-
}
592+
const storedLang = applyStoredLanguage();
599593

600594
// Add cache-busting parameter to prevent caching
601595
const cacheBuster = `?_=${Date.now()}`;
@@ -630,61 +624,21 @@ async function loadPage(url) {
630624
// Create a temporary div to hold the content
631625
const tempDiv = document.createElement('div');
632626

633-
// Clone all child nodes except script tags
634-
Array.from(doc.body.children).forEach(child => {
635-
if (child.tagName !== 'SCRIPT') {
636-
tempDiv.appendChild(child.cloneNode(true));
637-
}
638-
});
627+
// Automatically detect and import module scripts
628+
await detectAndImportModules(doc);
639629

640-
content = tempDiv.innerHTML;
630+
// Filter out script tags (they'll be imported dynamically)
631+
const contentWithoutScripts = filterScriptTags(doc.body);
632+
content = contentWithoutScripts.innerHTML;
641633
}
642634

643635
// Add the content to the wrapper
644636
wrapper.innerHTML = content;
645637

646638
// Pre-translate the content before it's returned to the router
647-
if (storedLang && window.app && window.app._t) {
639+
if (storedLang) {
648640
console.log(`Pre-translating content to ${storedLang} before DOM insertion`);
649-
650-
// Force language application
651-
window.app.localizer.setLanguage(storedLang);
652-
653-
// Translate elements with data-i18n attribute
654-
wrapper.querySelectorAll('[data-i18n]').forEach(element => {
655-
const key = element.getAttribute('data-i18n');
656-
element.textContent = window.app._t(key);
657-
});
658-
659-
// Translate other i18n attributes
660-
wrapper.querySelectorAll('[data-i18n-placeholder]').forEach(element => {
661-
const key = element.getAttribute('data-i18n-placeholder');
662-
element.placeholder = window.app._t(key);
663-
});
664-
665-
wrapper.querySelectorAll('[data-i18n-title]').forEach(element => {
666-
const key = element.getAttribute('data-i18n-title');
667-
element.title = window.app._t(key);
668-
});
669-
670-
wrapper.querySelectorAll('[data-i18n-html]').forEach(element => {
671-
const key = element.getAttribute('data-i18n-html');
672-
element.innerHTML = window.app._t(key);
673-
});
674-
675-
// Handle elements with data-i18n-params attribute (for interpolation)
676-
wrapper.querySelectorAll('[data-i18n-params]').forEach(element => {
677-
const key = element.getAttribute('data-i18n');
678-
if (!key) return;
679-
680-
try {
681-
const paramsAttr = element.getAttribute('data-i18n-params');
682-
const params = JSON.parse(paramsAttr);
683-
element.textContent = window.app._t(key, params);
684-
} catch (error) {
685-
console.error(`Error parsing data-i18n-params for key ${key}:`, error);
686-
}
687-
});
641+
translateContainer(wrapper, storedLang);
688642
}
689643

690644
// Get the translated content
@@ -693,14 +647,6 @@ async function loadPage(url) {
693647
// Remove the wrapper
694648
document.body.removeChild(wrapper);
695649

696-
// Special handling for state-demo.html
697-
if (url.includes('state-demo.html')) {
698-
// Import the components directly in the HTML
699-
await import('/js/components/state-example.js');
700-
await import('/js/components/test-component.js');
701-
console.log('Imported state-example.js and test-component.js directly');
702-
}
703-
704650
// Create DOM elements instead of using template strings
705651
const container = document.createElement('div');
706652

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/**
2+
* Component Loader Utility
3+
*
4+
* Handles automatic detection and loading of web components from HTML content
5+
*/
6+
7+
/**
8+
* Detects and imports module scripts from HTML content
9+
* @param {Document} doc - Parsed HTML document
10+
* @returns {Promise<string[]>} - Array of imported script paths
11+
*/
12+
export async function detectAndImportModules(doc) {
13+
// Extract script tags for automatic importing
14+
const scriptTags = Array.from(doc.body.querySelectorAll('script[type="module"]'));
15+
const scriptSources = scriptTags.map(script => script.getAttribute('src')).filter(src => src);
16+
17+
if (scriptSources.length > 0) {
18+
console.log(`Found ${scriptSources.length} module scripts to import automatically:`, scriptSources);
19+
20+
// Import all scripts in parallel
21+
const importPromises = scriptSources.map(src => {
22+
// Convert relative paths if needed
23+
const scriptPath = src.startsWith('/') ? src : `/${src}`;
24+
25+
// Dynamically import the script
26+
return import(scriptPath)
27+
.catch(error => {
28+
console.error(`Error automatically importing script ${scriptPath}:`, error);
29+
return null; // Return null for failed imports
30+
});
31+
});
32+
33+
// Wait for all imports to complete
34+
await Promise.all(importPromises);
35+
36+
return scriptSources;
37+
}
38+
39+
return [];
40+
}
41+
42+
/**
43+
* Filters out script tags from HTML content
44+
* @param {HTMLElement} element - Element to filter scripts from
45+
* @returns {DocumentFragment} - Document fragment with scripts removed
46+
*/
47+
export function filterScriptTags(element) {
48+
const tempDiv = document.createElement('div');
49+
50+
// Clone all child nodes except script tags
51+
Array.from(element.children).forEach(child => {
52+
if (child.tagName !== 'SCRIPT') {
53+
tempDiv.appendChild(child.cloneNode(true));
54+
}
55+
});
56+
57+
return tempDiv;
58+
}
59+
60+
export default {
61+
detectAndImportModules,
62+
filterScriptTags
63+
};
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/**
2+
* Translation Helper Utility
3+
*
4+
* Handles translation of HTML content using the i18n system
5+
*/
6+
7+
/**
8+
* Translates all i18n elements in the provided container
9+
* @param {HTMLElement} container - The container element with i18n attributes
10+
* @param {string} language - The language code to translate to
11+
* @returns {void}
12+
*/
13+
export function translateContainer(container, language) {
14+
if (!window.app || !window.app._t || !window.app.localizer) {
15+
console.warn('Translation system not available');
16+
return;
17+
}
18+
19+
console.log(`Translating container to ${language}`);
20+
21+
// Force language application
22+
window.app.localizer.setLanguage(language);
23+
24+
// Translate elements with data-i18n attribute
25+
container.querySelectorAll('[data-i18n]').forEach(element => {
26+
const key = element.getAttribute('data-i18n');
27+
element.textContent = window.app._t(key);
28+
});
29+
30+
// Translate other i18n attributes
31+
container.querySelectorAll('[data-i18n-placeholder]').forEach(element => {
32+
const key = element.getAttribute('data-i18n-placeholder');
33+
element.placeholder = window.app._t(key);
34+
});
35+
36+
container.querySelectorAll('[data-i18n-title]').forEach(element => {
37+
const key = element.getAttribute('data-i18n-title');
38+
element.title = window.app._t(key);
39+
});
40+
41+
container.querySelectorAll('[data-i18n-html]').forEach(element => {
42+
const key = element.getAttribute('data-i18n-html');
43+
element.innerHTML = window.app._t(key);
44+
});
45+
46+
// Handle elements with data-i18n-params attribute (for interpolation)
47+
container.querySelectorAll('[data-i18n-params]').forEach(element => {
48+
const key = element.getAttribute('data-i18n');
49+
if (!key) return;
50+
51+
try {
52+
const paramsAttr = element.getAttribute('data-i18n-params');
53+
const params = JSON.parse(paramsAttr);
54+
element.textContent = window.app._t(key, params);
55+
} catch (error) {
56+
console.error(`Error parsing data-i18n-params for key ${key}:`, error);
57+
}
58+
});
59+
}
60+
61+
/**
62+
* Applies the stored language to the document
63+
* @returns {string|null} The applied language code or null if not available
64+
*/
65+
export function applyStoredLanguage() {
66+
const storedLang = localStorage.getItem('profullstack-language');
67+
68+
if (storedLang && window.app && window.app.localizer) {
69+
console.log(`Applying stored language: ${storedLang}`);
70+
window.app.localizer.setLanguage(storedLang);
71+
72+
// Force language application
73+
if (window.app.localizer.getLanguage() !== storedLang) {
74+
console.log(`Language mismatch, forcing to: ${storedLang}`);
75+
window.app.localizer.setLanguage(storedLang);
76+
}
77+
78+
return storedLang;
79+
}
80+
81+
return null;
82+
}
83+
84+
export default {
85+
translateContainer,
86+
applyStoredLanguage
87+
};

0 commit comments

Comments
 (0)