Skip to content

Commit 6ed72ed

Browse files
belinea4071claude
andcommitted
Localize all SEM custom cards to 6 languages (#60)
Add _t() translation helper to all 6 remaining SEM cards: - sem-flow-card: node labels (Solar/Batterie/Netz/...), status text (Import/Export/Laden/Entladen), daily energy "Heute" labels - sem-battery-card: metric labels (Leistung/Zustand/Zyklen/Temperatur), status values (Laden/Entladen/Leerlauf), bottom chips - sem-ev-status-card: status (Verbunden/Getrennt/Laden), metric labels, session cost label - sem-schedule-card: row labels (Tarif/Nacht/Überschuss/EV), HT/NT - sem-load-priority-card: mode dropdown (Aus/Nur Spitze/Überschuss), critical toggle - sem-system-diagram-card: node labels, status text (legacy card) All cards read hass.language and use semLocalize() from sem-localize.js. Supports: English, German, French, Spanish, Italian, Dutch. Refs #60 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 41a2b5a commit 6ed72ed

6 files changed

Lines changed: 109 additions & 52 deletions

File tree

dashboard/card/sem-battery-card.js

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ class SEMBatteryCard extends HTMLElement {
5757
return Math.round(w) + ' W';
5858
}
5959

60+
_t(key) {
61+
const lang = this._hass?.language;
62+
return (typeof semLocalize === 'function') ? semLocalize(key, lang) : key;
63+
}
64+
6065
_update() {
6166
if (!this._hass) return;
6267

@@ -74,7 +79,7 @@ class SEMBatteryCard extends HTMLElement {
7479
// Determine status
7580
const isCharging = statusRaw === 'charging' || chargePower > 10;
7681
const isDischarging = statusRaw === 'discharging' || dischargePower > 10;
77-
const status = isCharging ? 'Charging' : isDischarging ? 'Discharging' : 'Idle';
82+
const status = isCharging ? this._t('charging') : isDischarging ? this._t('discharging') : this._t('idle');
7883

7984
// Temperature: try battery_temperature sensor
8085
const tempEntity = this._hass?.states[`${this._prefix}battery_temperature`];
@@ -131,6 +136,17 @@ class SEMBatteryCard extends HTMLElement {
131136
statusEl.style.color = isCharging ? '#f06292' : isDischarging ? '#4db6ac' : '#888';
132137
}
133138

139+
// Translate labels
140+
setVal('.lbl-soc', this._t('soc'));
141+
setVal('.lbl-power', this._t('power'));
142+
setVal('.lbl-status', this._t('status'));
143+
setVal('.lbl-health', this._t('health'));
144+
setVal('.lbl-cycles', this._t('cycles'));
145+
setVal('.lbl-temp', this._t('temperature'));
146+
setVal('.lbl-charge-today', this._t('charge_today'));
147+
setVal('.lbl-discharge-today', this._t('discharge_today'));
148+
setVal('.lbl-savings-today', this._t('savings_today'));
149+
134150
// Bottom chips
135151
setVal('.chip-charge', this._fmt(dailyCharge, 2) + ' kWh');
136152
setVal('.chip-discharge', this._fmt(dailyDischarge, 2) + ' kWh');
@@ -330,43 +346,43 @@ class SEMBatteryCard extends HTMLElement {
330346
</div>
331347
<div class="metrics-col">
332348
<div class="metric-row">
333-
<span class="metric-label">SOC</span>
349+
<span class="metric-label lbl-soc">SOC</span>
334350
<span class="metric-val m-soc">—</span>
335351
</div>
336352
<div class="metric-row">
337-
<span class="metric-label">Power</span>
353+
<span class="metric-label lbl-power">Power</span>
338354
<span class="metric-val m-power">—</span>
339355
</div>
340356
<div class="metric-row">
341-
<span class="metric-label">Status</span>
357+
<span class="metric-label lbl-status">Status</span>
342358
<span class="metric-val m-status" style="color:#888">—</span>
343359
</div>
344360
<div class="metric-row">
345-
<span class="metric-label">Health</span>
361+
<span class="metric-label lbl-health">Health</span>
346362
<span class="metric-val m-health">—</span>
347363
</div>
348364
<div class="metric-row">
349-
<span class="metric-label">Cycles</span>
365+
<span class="metric-label lbl-cycles">Cycles</span>
350366
<span class="metric-val m-cycles">—</span>
351367
</div>
352368
<div class="metric-row">
353-
<span class="metric-label">Temperature</span>
369+
<span class="metric-label lbl-temp">Temperature</span>
354370
<span class="metric-val m-temp">—</span>
355371
</div>
356372
</div>
357373
</div>
358374
359375
<div class="chips">
360376
<div class="chip">
361-
<div class="chip-label">Charge today</div>
377+
<div class="chip-label lbl-charge-today">Charge today</div>
362378
<div class="chip-value c-charge chip-charge">—</div>
363379
</div>
364380
<div class="chip">
365-
<div class="chip-label">Discharge today</div>
381+
<div class="chip-label lbl-discharge-today">Discharge today</div>
366382
<div class="chip-value c-discharge chip-discharge">—</div>
367383
</div>
368384
<div class="chip">
369-
<div class="chip-label">Savings today</div>
385+
<div class="chip-label lbl-savings-today">Savings today</div>
370386
<div class="chip-value c-savings chip-savings">—</div>
371387
</div>
372388
</div>

dashboard/card/sem-ev-status-card.js

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ class SEMEVStatusCard extends HTMLElement {
6464
return Math.round(w) + ' W';
6565
}
6666

67+
_t(key) {
68+
const lang = this._hass?.language;
69+
return (typeof semLocalize === 'function') ? semLocalize(key, lang) : key;
70+
}
71+
6772
_update() {
6873
if (!this._hass) return;
6974

@@ -97,13 +102,13 @@ class SEMEVStatusCard extends HTMLElement {
97102
const statusEl = $('.status-value');
98103
if (statusEl) {
99104
if (charging) {
100-
statusEl.textContent = 'Charging';
105+
statusEl.textContent = this._t('charging');
101106
statusEl.className = 'status-value charging';
102107
} else if (connected) {
103-
statusEl.textContent = 'Connected';
108+
statusEl.textContent = this._t('connected');
104109
statusEl.className = 'status-value connected';
105110
} else {
106-
statusEl.textContent = 'Disconnected';
111+
statusEl.textContent = this._t('disconnected');
107112
statusEl.className = 'status-value disconnected';
108113
}
109114
}
@@ -135,6 +140,17 @@ class SEMEVStatusCard extends HTMLElement {
135140
// Bottom chips
136141
setVal('.cost-chip-value', this._fmt(sessionCost, 2) + ' CHF');
137142

143+
// Translate labels
144+
const setLabel = (sel, text) => { const el = $(sel); if (el) el.textContent = text; };
145+
setLabel('.lbl-status', this._t('status'));
146+
setLabel('.lbl-power', this._t('power'));
147+
setLabel('.lbl-current', this._t('current'));
148+
setLabel('.lbl-session', this._t('session'));
149+
setLabel('.lbl-today', this._t('today'));
150+
setLabel('.lbl-solar-share', this._t('solar_share'));
151+
setLabel('.lbl-strategy', this._t('strategy'));
152+
setLabel('.lbl-session-cost', this._t('session_cost'));
153+
138154
// Glow ring animation state
139155
const ring = $('.glow-ring');
140156
if (ring) {
@@ -413,39 +429,39 @@ class SEMEVStatusCard extends HTMLElement {
413429
414430
<div class="metrics-col">
415431
<div class="metric-row">
416-
<span class="metric-label">Status</span>
432+
<span class="metric-label lbl-status">Status</span>
417433
<span class="status-value disconnected">Disconnected</span>
418434
</div>
419435
<div class="metric-row power-row" style="display:none">
420-
<span class="metric-label">Power</span>
436+
<span class="metric-label lbl-power">Power</span>
421437
<span class="metric-value power-value">\u2014 W</span>
422438
</div>
423439
<div class="metric-row">
424-
<span class="metric-label">Current</span>
440+
<span class="metric-label lbl-current">Current</span>
425441
<span class="metric-value current-value">\u2014 A</span>
426442
</div>
427443
<div class="metric-row">
428-
<span class="metric-label">Session</span>
444+
<span class="metric-label lbl-session">Session</span>
429445
<span class="metric-value session-value">\u2014 kWh</span>
430446
</div>
431447
<div class="metric-row">
432-
<span class="metric-label">Today</span>
448+
<span class="metric-label lbl-today">Today</span>
433449
<span class="metric-value daily-value">\u2014 kWh</span>
434450
</div>
435451
<div class="metric-row">
436-
<span class="metric-label">Solar share</span>
452+
<span class="metric-label lbl-solar-share">Solar share</span>
437453
<span class="metric-value solar-share-value">\u2014%</span>
438454
</div>
439455
<div class="metric-row">
440-
<span class="metric-label">Strategy</span>
456+
<span class="metric-label lbl-strategy">Strategy</span>
441457
<span class="strategy-value">\u2014</span>
442458
</div>
443459
</div>
444460
</div>
445461
446462
<div class="bottom-bar">
447463
<div class="chip">
448-
<span class="chip-label">Session cost</span>
464+
<span class="chip-label lbl-session-cost">Session cost</span>
449465
<span class="cost-chip-value">\u2014 CHF</span>
450466
</div>
451467
</div>

dashboard/card/sem-flow-card.js

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -234,15 +234,23 @@ class SEMFlowCard extends HTMLElement {
234234
}
235235
}
236236

237+
_t(key) {
238+
const lang = this._hass?.language;
239+
return (typeof semLocalize === 'function') ? semLocalize(key, lang) : key;
240+
}
241+
237242
_getNodeName(node) {
238243
const e = this._entities;
239-
if (!e) return SFC_DEFAULTS[node]?.name || node;
244+
// Translation keys for default names
245+
const translationKeys = { solar: 'solar', battery: 'battery', grid: 'grid', home: 'home', ev: 'ev_charging' };
246+
const defaultTranslated = translationKeys[node] ? this._t(translationKeys[node]) : (SFC_DEFAULTS[node]?.name || node);
247+
if (!e) return defaultTranslated;
240248
switch (node) {
241-
case 'solar': return e.solar?.name || SFC_DEFAULTS.solar.name;
242-
case 'battery': return e.battery?.name || SFC_DEFAULTS.battery.name;
243-
case 'grid': return e.grid?.name || SFC_DEFAULTS.grid.name;
244-
case 'home': return e.home?.name || SFC_DEFAULTS.home.name;
245-
case 'ev': return e.ev?.name || e.individual?.[0]?.name || SFC_DEFAULTS.ev.name;
249+
case 'solar': return e.solar?.name || defaultTranslated;
250+
case 'battery': return e.battery?.name || defaultTranslated;
251+
case 'grid': return e.grid?.name || defaultTranslated;
252+
case 'home': return e.home?.name || defaultTranslated;
253+
case 'ev': return e.ev?.name || e.individual?.[0]?.name || defaultTranslated;
246254
default: return node;
247255
}
248256
}
@@ -545,10 +553,11 @@ class SEMFlowCard extends HTMLElement {
545553
this._setText('val-inverter-status', this._getStateStr('charging_state'));
546554

547555
// Daily energy texts
548-
this._setText('val-today-solar', dailySolar ? `Today ${dailySolar} kWh` : '');
549-
this._setText('val-today-ev', dailyEv ? `Today ${dailyEv} kWh` : '');
550-
this._setText('val-today-battery', dailyBattery ? `Today ${dailyBattery} kWh` : '');
551-
this._setText('val-today-home', dailyHome ? `Today ${dailyHome} kWh` : '');
556+
const _today = this._t('today');
557+
this._setText('val-today-solar', dailySolar ? `${_today} ${dailySolar} kWh` : '');
558+
this._setText('val-today-ev', dailyEv ? `${_today} ${dailyEv} kWh` : '');
559+
this._setText('val-today-battery', dailyBattery ? `${_today} ${dailyBattery} kWh` : '');
560+
this._setText('val-today-home', dailyHome ? `${_today} ${dailyHome} kWh` : '');
552561

553562
// Grid daily: show import, export, and net on one line
554563
const gridDailyParts = [];
@@ -597,13 +606,13 @@ class SEMFlowCard extends HTMLElement {
597606
// Grid label
598607
const gridLabel = this.shadowRoot.getElementById('label-grid');
599608
if (gridLabel) {
600-
gridLabel.textContent = isImport ? 'IMPORT' : (gridExport > 10 ? 'EXPORT' : 'GRID');
609+
gridLabel.textContent = isImport ? this._t('importing') : (gridExport > 10 ? this._t('exporting') : this._t('grid'));
601610
}
602611

603612
// Battery label + dynamic color (pink=#f06292 charge, teal=#4db6ac discharge)
604613
const battLabel = this.shadowRoot.getElementById('label-battery-state');
605614
if (battLabel) {
606-
battLabel.textContent = battCharge > 10 ? 'CHARGING' : (battDischarge > 10 ? 'DISCHARGE' : '');
615+
battLabel.textContent = battCharge > 10 ? this._t('charging') : (battDischarge > 10 ? this._t('discharging') : '');
607616
}
608617

609618
// Dynamic battery color based on direction
@@ -862,7 +871,7 @@ class SEMFlowCard extends HTMLElement {
862871
// Daily energy text
863872
if (info.daily_energy_entity) {
864873
const de = this._hass.states[info.daily_energy_entity];
865-
this._setText(`dev-daily-${idx}`, de ? `Today ${de.state} kWh` : '');
874+
this._setText(`dev-daily-${idx}`, de ? `${this._t('today')} ${de.state} kWh` : '');
866875
}
867876

868877
// Update connection line opacity

dashboard/card/sem-load-priority-card.js

Lines changed: 11 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dashboard/card/sem-schedule-card.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,11 @@ class SEMScheduleCard extends HTMLElement {
139139
return SEMScheduleCard.MARGIN_LEFT + frac * SEMScheduleCard.BAR_WIDTH;
140140
}
141141

142+
_t(key) {
143+
const lang = this._hass?.language;
144+
return (typeof semLocalize === 'function') ? semLocalize(key, lang) : key;
145+
}
146+
142147
_update() {
143148
if (!this._hass) return;
144149

@@ -171,7 +176,7 @@ class SEMScheduleCard extends HTMLElement {
171176
}
172177

173178
// Row labels (left side)
174-
const rowLabels = ['Tariff', 'Night', 'Surplus', 'EV'];
179+
const rowLabels = [this._t('tariff'), this._t('night'), this._t('surplus'), this._t('ev')];
175180
rowLabels.forEach((label, i) => {
176181
const y = FRY + i * (RH + RG) + RH / 2 + 3.5;
177182
svgContent += `<text x="${ML - 4}" y="${y}" text-anchor="end"
@@ -197,9 +202,10 @@ class SEMScheduleCard extends HTMLElement {
197202
rx="3" fill="${fill}" opacity="${opacity}"/>`;
198203
// Label inside block if wide enough
199204
if (w > 30) {
205+
const tariffLabel = this._t(block.type.toLowerCase());
200206
svgContent += `<text x="${x + w / 2}" y="${tariffY + RH / 2 + 3.5}"
201207
text-anchor="middle" fill="rgba(255,255,255,0.85)" font-size="8"
202-
font-weight="600" font-family="'Segoe UI','Roboto',sans-serif">${block.type}</text>`;
208+
font-weight="600" font-family="'Segoe UI','Roboto',sans-serif">${tariffLabel}</text>`;
203209
}
204210
});
205211

0 commit comments

Comments
 (0)