@@ -17,26 +17,30 @@ Could've used i18next, but that's 50KB+ for something I can do in ~100 lines.
1717### How It Works
1818
1919** Language detection:**
20+
20211 . localStorage (` language ` key) - remembers user choice
21222 . Browser language (` navigator.language ` )
22233 . Fallback to English
2324
2425** Translation keys:**
26+
2527- Flat JSON: ` {"title": "...", "role": "..."} `
2628- No nesting (keeps it simple)
2729- Same keys across all languages (en, es, fr, pt)
2830
2931** HTML usage:**
32+
3033``` html
3134<p data-translate =" role" >Default text</p >
32- <img data-translate-alt =" imageAlt" alt =" Default" >
35+ <img data-translate-alt =" imageAlt" alt =" Default" / >
3336```
3437
3538** SEO:** Updates ` <title> ` , meta description, and Open Graph tags on language change.
3639
3740### Module Pattern
3841
3942Supports both browser and Jest:
43+
4044``` javascript
4145// Browser: global functions
4246if (typeof window !== " undefined" ) {
@@ -58,6 +62,7 @@ No build step needed. Works everywhere.
5862Standard ` <select> ` can't do glassmorphism without hacky CSS. Custom dropdown gives full control.
5963
6064** Features:**
65+
6166- Click outside to close
6267- Keyboard accessible
6368- Pure JavaScript (no libs)
@@ -69,11 +74,16 @@ Standard `<select>` can't do glassmorphism without hacky CSS. Custom dropdown gi
6974### Mobile-First
7075
7176Base styles = mobile. Enhance for desktop:
77+
7278``` css
73- .custom-select { width : 110px ; } /* mobile */
79+ .custom-select {
80+ width : 110px ;
81+ } /* mobile */
7482
7583@media (min-width : 992px ) {
76- .custom-select { width : 150px ; } /* desktop */
84+ .custom-select {
85+ width : 150px ;
86+ } /* desktop */
7787}
7888```
7989
@@ -88,12 +98,14 @@ Base styles = mobile. Enhance for desktop:
8898```
8999
90100** Glassmorphism:**
101+
91102``` css
92103background: rgba(255, 255, 255, 0.1 );
93104backdrop-filter : blur(10px);
94105```
95106
96107** SVG colors:** Use filters instead of multiple files:
108+
97109``` css
98110filter : brightness(0) invert(1); /* white */
99111filter : brightness(0) invert(0); /* dark */
@@ -106,12 +118,14 @@ filter: brightness(0) invert(0); /* dark */
106118### Coverage: ~ 90%
107119
108120** What's tested:**
121+
109122- Translation loading (success + errors)
110123- DOM updates (textContent, alt, meta tags)
111124- localStorage integration
112125- Dropdown behavior
113126
114127** What's not:**
128+
115129- Browser APIs (tested by browsers)
116130- Bootstrap (tested by Bootstrap)
117131- DOMContentLoaded listener (manual test)
@@ -123,6 +137,7 @@ filter: brightness(0) invert(0); /* dark */
123137### ESLint Flat Config
124138
125139ESLint 9+ requires it. Multiple environments need different globals:
140+
126141- Browser JS: ` setLanguage ` , ` getCurrentLanguage `
127142- Test files: ` describe ` , ` test ` , ` expect `
128143- Node scripts: ` module ` , ` require `
@@ -151,6 +166,7 @@ Need DOM APIs without a browser. Jest runs tests in Node.js with simulated DOM.
151166### GitHub Actions Workflow
152167
153168** On Pull Request:**
169+
154170``` yaml
155171- Format check (Prettier)
156172- Lint (ESLint)
@@ -159,6 +175,7 @@ Need DOM APIs without a browser. Jest runs tests in Node.js with simulated DOM.
159175` ` `
160176
161177**On Push to Main:**
178+
162179` ` ` yaml
163180- All PR checks
164181- Build (prepare src/ folder)
@@ -170,6 +187,7 @@ Need DOM APIs without a browser. Jest runs tests in Node.js with simulated DOM.
170187### GitHub Pages
171188
172189**Configuration:**
190+
173191- Source: GitHub Actions (not branch-based)
174192- Deploys: ` ./src` directory
175193- URL : https://fernandotonacoder.github.io
0 commit comments