@jjlmoya/utils-home 1.34.0 → 1.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/package.json +1 -1
  2. package/src/entries.ts +4 -1
  3. package/src/index.ts +1 -0
  4. package/src/tests/locale_completeness.test.ts +2 -2
  5. package/src/tests/tool_validation.test.ts +2 -2
  6. package/src/tool/humidityCalculator/bibliography.astro +14 -0
  7. package/src/tool/humidityCalculator/bibliography.ts +14 -0
  8. package/src/tool/humidityCalculator/component.astro +296 -0
  9. package/src/tool/humidityCalculator/entry.ts +29 -0
  10. package/src/tool/humidityCalculator/humidity-calculator.css +469 -0
  11. package/src/tool/humidityCalculator/i18n/de.ts +217 -0
  12. package/src/tool/humidityCalculator/i18n/en.ts +217 -0
  13. package/src/tool/humidityCalculator/i18n/es.ts +217 -0
  14. package/src/tool/humidityCalculator/i18n/fr.ts +217 -0
  15. package/src/tool/humidityCalculator/i18n/id.ts +217 -0
  16. package/src/tool/humidityCalculator/i18n/it.ts +217 -0
  17. package/src/tool/humidityCalculator/i18n/ja.ts +217 -0
  18. package/src/tool/humidityCalculator/i18n/ko.ts +217 -0
  19. package/src/tool/humidityCalculator/i18n/nl.ts +217 -0
  20. package/src/tool/humidityCalculator/i18n/pl.ts +217 -0
  21. package/src/tool/humidityCalculator/i18n/pt.ts +217 -0
  22. package/src/tool/humidityCalculator/i18n/ru.ts +217 -0
  23. package/src/tool/humidityCalculator/i18n/sv.ts +217 -0
  24. package/src/tool/humidityCalculator/i18n/tr.ts +217 -0
  25. package/src/tool/humidityCalculator/i18n/zh.ts +217 -0
  26. package/src/tool/humidityCalculator/index.ts +9 -0
  27. package/src/tool/humidityCalculator/logic.ts +60 -0
  28. package/src/tool/humidityCalculator/seo.astro +15 -0
  29. package/src/tool/humidityCalculator/ui.ts +29 -0
  30. package/src/tools.ts +2 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jjlmoya/utils-home",
3
- "version": "1.34.0",
3
+ "version": "1.35.0",
4
4
  "type": "module",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
package/src/entries.ts CHANGED
@@ -28,6 +28,8 @@ export { tileLayoutCalculator } from './tool/tileLayoutCalculator/entry';
28
28
  export type { TileLayoutCalculatorLocaleContent } from './tool/tileLayoutCalculator/entry';
29
29
  export { lightingCalculator } from './tool/lightingCalculator/entry';
30
30
  export type { LightingCalculatorLocaleContent } from './tool/lightingCalculator/entry';
31
+ export { humidityCalculator } from './tool/humidityCalculator/entry';
32
+ export type { HumidityCalculatorLocaleContent } from './tool/humidityCalculator/entry';
31
33
  export { homeCategory } from './category';
32
34
  import { dewPointCalculator } from './tool/dewPointCalculator/entry';
33
35
  import { heatingComparator } from './tool/heatingComparator/entry';
@@ -44,4 +46,5 @@ import { deskErgonomics } from './tool/deskErgonomics/entry';
44
46
  import { applianceCostCalculator } from './tool/applianceCostCalculator/entry';
45
47
  import { tileLayoutCalculator } from './tool/tileLayoutCalculator/entry';
46
48
  import { lightingCalculator } from './tool/lightingCalculator/entry';
47
- export const ALL_ENTRIES = [dewPointCalculator, heatingComparator, ledSavingCalculator, projectorCalculator, qrGenerator, solarCalculator, tariffComparator, wifiRangeSimulator, acTonnageCalculator, wallPaintingCalculator, vampireDrawSimulator, deskErgonomics, applianceCostCalculator, tileLayoutCalculator, lightingCalculator];
49
+ import { humidityCalculator } from './tool/humidityCalculator/entry';
50
+ export const ALL_ENTRIES = [dewPointCalculator, heatingComparator, ledSavingCalculator, projectorCalculator, qrGenerator, solarCalculator, tariffComparator, wifiRangeSimulator, acTonnageCalculator, wallPaintingCalculator, vampireDrawSimulator, deskErgonomics, applianceCostCalculator, tileLayoutCalculator, lightingCalculator, humidityCalculator];
package/src/index.ts CHANGED
@@ -29,4 +29,5 @@ export { VAMPIRE_DRAW_SIMULATOR_TOOL } from './tool/vampireDrawSimulator';
29
29
  export { DESK_ERGONOMICS_TOOL } from './tool/deskErgonomics';
30
30
  export { APPLIANCE_COST_CALCULATOR_TOOL } from './tool/applianceCostCalculator';
31
31
  export { TILE_LAYOUT_CALCULATOR_TOOL } from './tool/tileLayoutCalculator';
32
+ export { HUMIDITY_CALCULATOR_TOOL } from './tool/humidityCalculator';
32
33
 
@@ -17,8 +17,8 @@ describe('Locale Completeness Validation', () => {
17
17
  });
18
18
  });
19
19
 
20
- it('should have 15 tools registered', () => {
21
- expect(ALL_TOOLS.length).toBe(15);
20
+ it('should have 16 tools registered', () => {
21
+ expect(ALL_TOOLS.length).toBe(16);
22
22
  });
23
23
  });
24
24
 
@@ -4,8 +4,8 @@ import { homeCategory } from '../data';
4
4
 
5
5
  describe('Tool Validation Suite', () => {
6
6
  describe('Library Registration', () => {
7
- it('should have 15 tools in ALL_TOOLS', () => {
8
- expect(ALL_TOOLS.length).toBe(15);
7
+ it('should have 16 tools in ALL_TOOLS', () => {
8
+ expect(ALL_TOOLS.length).toBe(16);
9
9
  });
10
10
 
11
11
  it('homeCategory should be defined', () => {
@@ -0,0 +1,14 @@
1
+ ---
2
+ import { Bibliography as SharedBibliography } from '@jjlmoya/utils-shared';
3
+ import { humidityCalculator } from './index';
4
+ import type { KnownLocale } from '../../types';
5
+
6
+ interface Props {
7
+ locale?: KnownLocale;
8
+ }
9
+
10
+ const { locale = 'es' } = Astro.props;
11
+ const content = await humidityCalculator.i18n[locale]?.();
12
+ ---
13
+
14
+ {content && <SharedBibliography links={content.bibliography} />}
@@ -0,0 +1,14 @@
1
+ export const bibliography = [
2
+ {
3
+ name: 'EPA: Mold Course Chapter 2: Mold and Moisture Dynamics',
4
+ url: 'https://www.epa.gov/mold/mold-course-introduction',
5
+ },
6
+ {
7
+ name: 'ASHRAE Standard 55: Thermal Environmental Conditions for Human Occupancy',
8
+ url: 'https://www.ashrae.org/technical-resources/standards-and-guidelines/standards-and-guidelines-detail?id=17110',
9
+ },
10
+ {
11
+ name: 'Energy Star: Choosing a Dehumidifier',
12
+ url: 'https://www.energystar.gov/products/dehumidifiers',
13
+ },
14
+ ];
@@ -0,0 +1,296 @@
1
+ ---
2
+ import type { HumidityCalculatorUI } from './ui';
3
+ import { calculateHumidity, fmt2 } from './logic';
4
+
5
+ interface Props {
6
+ ui?: Record<string, unknown>;
7
+ }
8
+
9
+ const { ui = {} } = Astro.props;
10
+ const hUI = ui as HumidityCalculatorUI;
11
+
12
+ const DEF_ROOM = 25;
13
+ const DEF_TEMP = 22;
14
+ const DEF_CUR = 70;
15
+ const DEF_TAR = 50;
16
+ const DEF_CAP = 12;
17
+
18
+ const initial = calculateHumidity({ roomM2: DEF_ROOM, tempC: DEF_TEMP, currentRH: DEF_CUR, targetRH: DEF_TAR, capacityLPerDay: DEF_CAP });
19
+ ---
20
+
21
+ <div class="hc-root">
22
+ <div class="hc-card"
23
+ data-mh={hUI.moldRiskHigh}
24
+ data-mm={hUI.moldRiskMedium}
25
+ data-ml={hUI.moldRiskLow}
26
+ data-bh={hUI.badgeHigh}
27
+ data-bm={hUI.badgeMedium}
28
+ data-bl={hUI.badgeLow}
29
+ data-uh={hUI.runtimeUnitH}
30
+ data-um={hUI.runtimeUnitM}
31
+ >
32
+ <div class="hc-stage">
33
+ <div class="hc-stage-glow"></div>
34
+ <p class="hc-section-label">{hUI.comfortDialTitle}</p>
35
+ <svg class="hc-dial" viewBox="0 0 200 160" id="hc-dial-svg">
36
+ <defs>
37
+ <linearGradient id="hc-grad-low" x1="0%" y1="0%" x2="100%" y2="0%">
38
+ <stop offset="0%" stop-color="#3b82f6" />
39
+ <stop offset="100%" stop-color="#22c55e" />
40
+ </linearGradient>
41
+ <linearGradient id="hc-grad-medium" x1="0%" y1="0%" x2="100%" y2="0%">
42
+ <stop offset="0%" stop-color="#f59e0b" />
43
+ <stop offset="100%" stop-color="#eab308" />
44
+ </linearGradient>
45
+ <linearGradient id="hc-grad-high" x1="0%" y1="0%" x2="100%" y2="0%">
46
+ <stop offset="0%" stop-color="#ef4444" />
47
+ <stop offset="100%" stop-color="#f97316" />
48
+ </linearGradient>
49
+ </defs>
50
+ <path id="hc-track" d="" fill="none" stroke="var(--hc-track)" stroke-width="10" stroke-linecap="round" />
51
+ <path id="hc-fill" d="" fill="none" stroke="url(#hc-grad-high)" stroke-width="10" stroke-linecap="round" />
52
+ <circle id="hc-handle-c" r="6" fill="#fff" stroke="#ef4444" stroke-width="3" />
53
+ <circle id="hc-handle-t" r="6" fill="#fff" stroke="#f97316" stroke-width="3" />
54
+ <text x="100" y="118" text-anchor="middle" class="hc-dial-center">
55
+ <tspan id="hc-dial-num" x="100">{DEF_CUR}{hUI.unitPercent}</tspan>
56
+ <tspan id="hc-dial-sub" x="100" dy="22" class="hc-dial-sub">{hUI.comfortCurrent}</tspan>
57
+ </text>
58
+ </svg>
59
+ <div class="hc-dial-labels">
60
+ <span>{hUI.comfortCurrent}: <b id="hc-lbl-c">{DEF_CUR}{hUI.unitPercent}</b></span>
61
+ <span>{hUI.comfortTarget}: <b id="hc-lbl-t">{DEF_TAR}{hUI.unitPercent}</b></span>
62
+ </div>
63
+ </div>
64
+
65
+ <div class="hc-body">
66
+ <div class="hc-fields hc-fields-3">
67
+ <div class="hc-field">
68
+ <label class="hc-field-label" for="hc-room">{hUI.labelRoomSize}</label>
69
+ <div class="hc-field-row">
70
+ <input type="number" id="hc-room" class="hc-field-input" value={DEF_ROOM} min="1" max="500" step="1" />
71
+ <span class="hc-field-unit">{hUI.unitM2}</span>
72
+ </div>
73
+ </div>
74
+ <div class="hc-field">
75
+ <label class="hc-field-label" for="hc-temp">{hUI.labelTemperature}</label>
76
+ <div class="hc-field-row">
77
+ <input type="number" id="hc-temp" class="hc-field-input" value={DEF_TEMP} min="5" max="35" step="1" />
78
+ <span class="hc-field-unit">{hUI.unitCelsius}</span>
79
+ </div>
80
+ </div>
81
+ <div class="hc-field">
82
+ <label class="hc-field-label" for="hc-rh-c">{hUI.labelCurrentHumidity}</label>
83
+ <div class="hc-field-row">
84
+ <input type="number" id="hc-rh-c" class="hc-field-input" value={DEF_CUR} min="0" max="100" step="1" />
85
+ <span class="hc-field-unit">{hUI.unitPercent}</span>
86
+ </div>
87
+ </div>
88
+ </div>
89
+
90
+ <div class="hc-sliders">
91
+ <div class="hc-slider-group">
92
+ <label class="hc-slider-label">{hUI.labelTargetHumidity}</label>
93
+ <input type="range" id="hc-rh-t" class="hc-slider" min="0" max="100" value={DEF_TAR} />
94
+ <span class="hc-slider-val" id="hc-rh-t-val">{DEF_TAR}{hUI.unitPercent}</span>
95
+ </div>
96
+ <div class="hc-slider-group">
97
+ <label class="hc-slider-label">{hUI.labelCapacity}</label>
98
+ <input type="range" id="hc-cap" class="hc-slider" min="0" max="100" value={DEF_CAP} />
99
+ <span class="hc-slider-val" id="hc-cap-val">{DEF_CAP}{hUI.unitLitersDay}</span>
100
+ </div>
101
+ </div>
102
+
103
+ <div class="hc-tank">
104
+ <p class="hc-section-label">{hUI.tankTitle}</p>
105
+ <div class="hc-tank-visual">
106
+ <div class="hc-tank-body">
107
+ <div class="hc-tank-water" id="hc-water"></div>
108
+ <div class="hc-tank-marks">
109
+ <span></span><span></span><span></span><span></span>
110
+ </div>
111
+ </div>
112
+ <div class="hc-tank-info">
113
+ <span class="hc-tank-num" id="hc-tank-num">{fmt2(initial.litersToExtract)}</span>
114
+ <span class="hc-tank-unit">{hUI.tankLiters}</span>
115
+ <span class="hc-tank-sub" id="hc-tank-sub">{hUI.extractionLabel}</span>
116
+ </div>
117
+ </div>
118
+ </div>
119
+
120
+ <div class="hc-runtime">
121
+ <p class="hc-runtime-label">{hUI.runtimeTitle}</p>
122
+ <div class="hc-runtime-ring" id="hc-runtime-ring">
123
+ <span class="hc-runtime-value" id="hc-runtime-val">{initial.runtimeHours}{hUI.runtimeUnitH} {initial.runtimeMinutes}{hUI.runtimeUnitM}</span>
124
+ </div>
125
+ </div>
126
+
127
+ <div class="hc-mold hc-mold-high" id="hc-mold">
128
+ <span class="hc-mold-icon">
129
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>
130
+ </span>
131
+ <span class="hc-mold-text" id="hc-mold-text">{hUI.moldRiskHigh}</span>
132
+ <span class="hc-mold-badge" id="hc-mold-badge">{hUI.badgeHigh}</span>
133
+ </div>
134
+ </div>
135
+ </div>
136
+ </div>
137
+
138
+ <script>
139
+ import { calculateHumidity, fmt2 } from './logic';
140
+
141
+ function el(id: string) { return document.getElementById(id); }
142
+ function setTxt(id: string, val: string) { const e = el(id); if (e) e.textContent = val; }
143
+ function getNum(id: string): number { const e = el(id) as HTMLInputElement | null; return e ? parseFloat(e.value) || 0 : 0; }
144
+
145
+ function clamp(n: number, min: number, max: number) { return Math.max(min, Math.min(max, n)); }
146
+
147
+ function polar(cx: number, cy: number, r: number, aDeg: number) {
148
+ const a = (aDeg * Math.PI) / 180;
149
+ return { x: cx + r * Math.cos(a), y: cy + r * Math.sin(a) };
150
+ }
151
+
152
+ interface ArcArgs { cx: number; cy: number; r: number; a1: number; a2: number }
153
+
154
+ function arcPath(a: ArcArgs) {
155
+ const p1 = polar(a.cx, a.cy, a.r, a.a1);
156
+ const p2 = polar(a.cx, a.cy, a.r, a.a2);
157
+ const large = Math.abs(a.a2 - a.a1) > 180 ? 1 : 0;
158
+ const sweep = a.a2 > a.a1 ? 1 : 0;
159
+ return `M ${fmt2(p1.x)} ${fmt2(p1.y)} A ${a.r} ${a.r} 0 ${large} ${sweep} ${fmt2(p2.x)} ${fmt2(p2.y)}`;
160
+ }
161
+
162
+ function angleFor(val: number) {
163
+ return 210 + (val / 100) * 120;
164
+ }
165
+
166
+ const RISK_COLORS: Record<string, { fill: string; c: string; t: string }> = {
167
+ low: { fill: 'url(#hc-grad-low)', c: '#3b82f6', t: '#22c55e' },
168
+ medium: { fill: 'url(#hc-grad-medium)', c: '#f59e0b', t: '#eab308' },
169
+ high: { fill: 'url(#hc-grad-high)', c: '#ef4444', t: '#f97316' },
170
+ };
171
+
172
+ function setTrack() {
173
+ const track = el('hc-track') as SVGPathElement | null;
174
+ if (track) track.setAttribute('d', arcPath({ cx: 100, cy: 132, r: 86, a1: 210, a2: 330 }));
175
+ }
176
+
177
+ function setGauge(c: number, t: number, risk: 'high' | 'medium' | 'low') {
178
+ const ac = angleFor(c);
179
+ const at = angleFor(t);
180
+ const aStart = Math.min(ac, at);
181
+ const aEnd = Math.max(ac, at);
182
+ const fill = el('hc-fill') as SVGPathElement | null;
183
+ if (fill) {
184
+ fill.setAttribute('d', arcPath({ cx: 100, cy: 132, r: 86, a1: aStart, a2: aEnd }));
185
+ fill.setAttribute('stroke', RISK_COLORS[risk].fill);
186
+ }
187
+ const hc = polar(100, 132, 86, ac);
188
+ const ht = polar(100, 132, 86, at);
189
+ const handleC = el('hc-handle-c') as SVGCircleElement | null;
190
+ const handleT = el('hc-handle-t') as SVGCircleElement | null;
191
+ if (handleC) { handleC.setAttribute('cx', String(hc.x)); handleC.setAttribute('cy', String(hc.y)); handleC.setAttribute('stroke', RISK_COLORS[risk].c); }
192
+ if (handleT) { handleT.setAttribute('cx', String(ht.x)); handleT.setAttribute('cy', String(ht.y)); handleT.setAttribute('stroke', RISK_COLORS[risk].t); }
193
+ }
194
+
195
+ function setTank(liters: number) {
196
+ const maxL = 10;
197
+ const pct = Math.min(100, (liters / maxL) * 100);
198
+ const water = el('hc-water') as HTMLElement | null;
199
+ if (water) water.style.height = `${pct}%`;
200
+ setTxt('hc-tank-num', fmt2(liters));
201
+ }
202
+
203
+ function setRuntime(h: number, m: number, uh: string, um: string) {
204
+ setTxt('hc-runtime-val', `${h}${uh} ${m}${um}`);
205
+ const ring = el('hc-runtime-ring') as HTMLElement | null;
206
+ if (ring) {
207
+ ring.classList.remove('hc-pulse');
208
+ void ring.offsetWidth;
209
+ ring.classList.add('hc-pulse');
210
+ }
211
+ }
212
+
213
+ interface MoldLabels { highText: string; medText: string; lowText: string; highBadge: string; medBadge: string; lowBadge: string }
214
+
215
+ function setMold(risk: 'high' | 'medium' | 'low', labels: MoldLabels) {
216
+ const mold = el('hc-mold') as HTMLElement | null;
217
+ if (!mold) return;
218
+ mold.classList.remove('hc-mold-high', 'hc-mold-medium', 'hc-mold-low');
219
+ if (risk === 'high') {
220
+ mold.classList.add('hc-mold-high');
221
+ setTxt('hc-mold-text', labels.highText);
222
+ setTxt('hc-mold-badge', labels.highBadge);
223
+ } else if (risk === 'medium') {
224
+ mold.classList.add('hc-mold-medium');
225
+ setTxt('hc-mold-text', labels.medText);
226
+ setTxt('hc-mold-badge', labels.medBadge);
227
+ } else {
228
+ mold.classList.add('hc-mold-low');
229
+ setTxt('hc-mold-text', labels.lowText);
230
+ setTxt('hc-mold-badge', labels.lowBadge);
231
+ }
232
+ }
233
+
234
+ function readAndSync() {
235
+ const room = clamp(getNum('hc-room'), 1, 500);
236
+ const temp = clamp(getNum('hc-temp'), 5, 35);
237
+ const currentRH = clamp(getNum('hc-rh-c'), 20, 95);
238
+ const targetRH = clamp(getNum('hc-rh-t'), 30, 60);
239
+ const cap = clamp(getNum('hc-cap'), 1, 100);
240
+
241
+ const tarSlider = el('hc-rh-t') as HTMLInputElement | null;
242
+ if (tarSlider && parseFloat(tarSlider.value) !== targetRH) tarSlider.value = String(targetRH);
243
+ const capSlider = el('hc-cap') as HTMLInputElement | null;
244
+ if (capSlider && parseFloat(capSlider.value) !== cap) capSlider.value = String(cap);
245
+
246
+ const roomIn = el('hc-room') as HTMLInputElement | null;
247
+ if (roomIn && parseFloat(roomIn.value) !== room) roomIn.value = String(room);
248
+ const tempIn = el('hc-temp') as HTMLInputElement | null;
249
+ if (tempIn && parseFloat(tempIn.value) !== temp) tempIn.value = String(temp);
250
+ const curIn = el('hc-rh-c') as HTMLInputElement | null;
251
+ if (curIn && parseFloat(curIn.value) !== currentRH) curIn.value = String(currentRH);
252
+
253
+ return { roomM2: room, tempC: temp, currentRH, targetRH, capacityLPerDay: cap };
254
+ }
255
+
256
+ function update() {
257
+ const s = readAndSync();
258
+ const r = calculateHumidity(s);
259
+ setGauge(s.currentRH, s.targetRH, r.moldRisk);
260
+ setTank(r.litersToExtract);
261
+ const card = document.querySelector('.hc-card') as HTMLElement | null;
262
+ const uh = card?.dataset.uh || 'h';
263
+ const um = card?.dataset.um || 'm';
264
+ setRuntime(r.runtimeHours, r.runtimeMinutes, uh, um);
265
+ if (card) {
266
+ const highTxt = card.dataset.mh || 'High Risk';
267
+ const medTxt = card.dataset.mm || 'Moderate Risk';
268
+ const lowTxt = card.dataset.ml || 'Low Risk';
269
+ const highBadge = card.dataset.bh || 'High';
270
+ const medBadge = card.dataset.bm || 'Medium';
271
+ const lowBadge = card.dataset.bl || 'Low';
272
+ setMold(r.moldRisk, { highText: highTxt, medText: medTxt, lowText: lowTxt, highBadge, medBadge, lowBadge });
273
+ }
274
+ setTxt('hc-rh-t-val', `${s.targetRH}%`);
275
+ setTxt('hc-cap-val', `${s.capacityLPerDay} L/day`);
276
+ setTxt('hc-lbl-c', `${s.currentRH}%`);
277
+ setTxt('hc-lbl-t', `${s.targetRH}%`);
278
+ setTxt('hc-dial-num', `${s.currentRH}%`);
279
+ }
280
+
281
+ function bindInputs() {
282
+ ['hc-room', 'hc-temp', 'hc-rh-c', 'hc-rh-t', 'hc-cap'].forEach((id) => {
283
+ const i = el(id) as HTMLInputElement | null;
284
+ if (i) i.addEventListener('input', update);
285
+ });
286
+ }
287
+
288
+ function init() {
289
+ setTrack();
290
+ bindInputs();
291
+ update();
292
+ }
293
+
294
+ document.addEventListener('astro:page-load', init);
295
+ init();
296
+ </script>
@@ -0,0 +1,29 @@
1
+ import type { HomeToolEntry, ToolLocaleContent } from '../../types';
2
+ import type { HumidityCalculatorUI } from './ui';
3
+
4
+ export type HumidityCalculatorLocaleContent = ToolLocaleContent<HumidityCalculatorUI>;
5
+
6
+ export const humidityCalculator: HomeToolEntry<HumidityCalculatorUI> = {
7
+ id: 'humidity-calculator',
8
+ icons: {
9
+ bg: 'mdi:water-percent',
10
+ fg: 'mdi:air-filter',
11
+ },
12
+ i18n: {
13
+ en: async () => (await import('./i18n/en')).content,
14
+ de: async () => (await import('./i18n/de')).content,
15
+ es: async () => (await import('./i18n/es')).content,
16
+ fr: async () => (await import('./i18n/fr')).content,
17
+ id: async () => (await import('./i18n/id')).content,
18
+ it: async () => (await import('./i18n/it')).content,
19
+ ja: async () => (await import('./i18n/ja')).content,
20
+ ko: async () => (await import('./i18n/ko')).content,
21
+ nl: async () => (await import('./i18n/nl')).content,
22
+ pl: async () => (await import('./i18n/pl')).content,
23
+ pt: async () => (await import('./i18n/pt')).content,
24
+ ru: async () => (await import('./i18n/ru')).content,
25
+ sv: async () => (await import('./i18n/sv')).content,
26
+ tr: async () => (await import('./i18n/tr')).content,
27
+ zh: async () => (await import('./i18n/zh')).content,
28
+ },
29
+ };