Skip to content

Commit 883d2b5

Browse files
Translate to german (#64)
2 parents d3641e7 + 6293b0b commit 883d2b5

17 files changed

Lines changed: 110 additions & 123 deletions

RELEASE_NOTES.md

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,11 @@
22

33
## Summary
44

5-
This release overhauls the Reporting UI with a redesigned home/dashboard experience,
6-
improves page navigation and routing reliability, and standardizes CHP naming from
7-
`BHKW` to `KWK` across reporting labels.
8-
95
## Upgrading
106

11-
- No breaking API changes are introduced.
12-
- Package assets now explicitly include UI templates and styles (`templates/*.html`,
13-
`styles/*.css`) in distribution metadata.
147

158
## New Features
169

17-
- Redesign the home page with reusable HTML templates and dedicated CSS styling.
18-
- Introduce centralized UI resource helpers for loading/injecting templates and styles.
19-
- Add a global app theme and refreshed dashboard visual system (section dividers,
20-
KPI card styling, updated color palette).
21-
- Add section-aware routing from query params (including direct scroll to Data Export
22-
section when requested).
10+
- Translate the output texts to german.
2311

2412
## Bug Fixes
25-
26-
- Fix navigation state synchronization between sidebar selection and query-parameter
27-
routing.
28-
- Fix data-export navigation routing and page transitions.
29-
- Fix inconsistent terminology by renaming `BHKW` labels to `KWK` in reporting UI.
30-
- Fix formatting and quality issues reported by `nox` checks.

app.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -319,15 +319,18 @@ def sidebar(pages: list[PageSpec]) -> PageSpec:
319319
# --- Main entrypoint --------------------------------------------------------
320320
def main() -> None:
321321
st.set_page_config(
322-
page_title="Enterprise Reporting App",
322+
page_title="Reporting-Anwendung",
323323
page_icon="📊",
324324
layout="wide",
325325
)
326326
_inject_global_theme()
327327

328328
pages = discover_library_pages()
329329
if not pages:
330-
st.info(f"No pages found under `{LIB_PAGES_ROOT}` (or local app_pages).")
330+
st.info(
331+
f"Keine Seiten unter `{LIB_PAGES_ROOT}` gefunden "
332+
"(oder im lokalen Verzeichnis `app_pages`)."
333+
)
331334
return
332335

333336
selected = sidebar(pages)

src/frequenz/cs_reporting/app_pages/home.py

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -41,56 +41,56 @@ class _FeatureCard:
4141

4242

4343
_STATS: tuple[_Stat, ...] = (
44-
_Stat("Real-time", "Data Refresh"),
45-
_Stat("5+", "Component Types"),
46-
_Stat("Multi-site", "Portfolio View"),
47-
_Stat("CSV", "Export Ready"),
44+
_Stat("Echtzeit", "Datenaktualisierung"),
45+
_Stat("5+", "Komponententypen"),
46+
_Stat("Multi-Standort", "Portfolioansicht"),
47+
_Stat("CSV", "Exportbereit"),
4848
)
4949

5050
_FEATURE_CARDS: tuple[_FeatureCard, ...] = (
5151
_FeatureCard(
5252
color_class="home-card--blue",
5353
icon="⚡",
54-
title="Reporting Dashboard",
54+
title="Reporting-Dashboard",
5555
description=(
56-
"Portfolio-level power flows, consumption balances, "
57-
"and detailed KPI analytics across all microgrid components."
56+
"Leistungsflüsse auf Portfolioebene, Verbrauchsbilanzen "
57+
"und detaillierte KPI-Analysen über alle Microgrid-Komponenten."
5858
),
5959
features=(
60-
"Grid import / export KPIs",
61-
"PV, Battery, CHP & Wind analytics",
62-
"Self-sufficiency & self-consumption ratios",
63-
"Interactive time-series plots",
60+
"KPIs für Netzbezug und Einspeisung",
61+
"Analysen für PV, Batterie, KWK & Wind",
62+
"Autarkie- und Eigenverbrauchsquoten",
63+
"Interaktive Zeitreihen-Plots",
6464
),
6565
),
6666
_FeatureCard(
6767
color_class="home-card--green",
6868
icon="☀",
69-
title="Solar Monitoring",
69+
title="Solar-Monitoring",
7070
description=(
71-
"Maintenance-oriented solar workflow checks with baseline "
72-
"model comparison and rolling performance views."
71+
"Wartungsorientierte Solar-Workflow-Prüfungen mit "
72+
"Baseline-Modellvergleich und rollierenden Performance-Ansichten."
7373
),
7474
features=(
75-
"Inverter & meter diagnostics",
76-
"Baseline model benchmarking",
77-
"Rolling performance window (5-60 days)",
78-
"German & English report output",
75+
"Diagnose für Wechselrichter & Zähler",
76+
"Benchmarking von Baseline-Modellen",
77+
"Rollierendes Performance-Fenster (5-60 Tage)",
78+
"Berichtsausgabe auf Deutsch & Englisch",
7979
),
8080
),
8181
_FeatureCard(
8282
color_class="home-card--purple",
8383
icon="↓",
84-
title="Data Export",
84+
title="Datenexport",
8585
description=(
86-
"Download standardised tables from each reporting section "
87-
"for use in internal pipelines and client deliverables."
86+
"Standardisierte Tabellen aus allen Reporting-Bereichen "
87+
"für interne Pipelines und Kundenberichte herunterladen."
8888
),
8989
features=(
90-
"Per-component time series",
91-
"Aggregated energy summaries",
92-
"Configurable resolution & timezone",
93-
"CSV-compatible format",
90+
"Zeitreihen pro Komponente",
91+
"Aggregierte Energiezusammenfassungen",
92+
"Konfigurierbare Auflösung & Zeitzone",
93+
"CSV-kompatibles Format",
9494
),
9595
),
9696
)
@@ -203,21 +203,21 @@ def render() -> None:
203203

204204
col1, col2, col3 = st.columns(3)
205205
with col1:
206-
if st.button("Open Reporting Dashboard", use_container_width=True):
206+
if st.button("Reporting-Dashboard öffnen", use_container_width=True):
207207
_navigate_to("reporting_dashboard")
208208
with col2:
209-
if st.button("Open Solar Monitoring", use_container_width=True):
209+
if st.button("Solar-Monitoring öffnen", use_container_width=True):
210210
_navigate_to("solar")
211211
with col3:
212-
if st.button("Download Data Export", use_container_width=True):
212+
if st.button("Datenexport öffnen", use_container_width=True):
213213
_navigate_to("reporting_dashboard", section="data-export")
214214

215215
st.markdown(_footer_html(), unsafe_allow_html=True)
216216

217217

218218
PAGE = PageSpec(
219219
key="home",
220-
title="Overview",
220+
title="Übersicht",
221221
icon="",
222222
order=0,
223223
render=render,

src/frequenz/cs_reporting/app_pages/reporting.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ def _parse_resolution(resolution_str: str) -> timedelta:
6161
"""
6262
match = re.fullmatch(r"(\d+)\s*(min|hour|h)", resolution_str.strip().lower())
6363
if not match:
64-
raise ValueError(f"Unsupported resolution format: {resolution_str}")
64+
raise ValueError(f"Nicht unterstütztes Auflösungsformat: {resolution_str}")
6565

6666
value, unit = match.groups()
6767
minutes = int(value) * (60 if unit in ("hour", "h") else 1)
@@ -95,7 +95,7 @@ def render() -> None:
9595
Streamlit components are rendered directly.
9696
"""
9797
# Page header
98-
st.title("Reporting Dashboard")
98+
st.title("Reporting-Dashboard")
9999

100100
# Collect user inputs from sidebar
101101
today = date.today()
@@ -120,7 +120,8 @@ def render() -> None:
120120
# Validate date range
121121
if start_time > end_time:
122122
st.warning(
123-
"End date must be on or after the start date. Please adjust your selection."
123+
"Das Enddatum muss am oder nach dem Startdatum liegen. "
124+
"Bitte passen Sie Ihre Auswahl an."
124125
)
125126
st.stop()
126127

@@ -139,7 +140,7 @@ def render() -> None:
139140

140141
# Fetch data with error handling
141142
try:
142-
with st.spinner("Loading microgrid data..."):
143+
with st.spinner("Microgrid-Daten werden geladen..."):
143144
df = get_microgrid_data(
144145
microgrid_id=microgrid_id,
145146
start_date=start_time,
@@ -148,12 +149,12 @@ def render() -> None:
148149
resolution=resolution,
149150
)
150151
except (RuntimeError, ValueError, OSError) as e:
151-
st.error(f"Failed to fetch data: {e}")
152+
st.error(f"Daten konnten nicht geladen werden: {e}")
152153
st.stop()
153154

154155
# Check for empty results
155156
if df.empty:
156-
st.warning("No data for the selected filters.")
157+
st.warning("Keine Daten für die ausgewählten Filter vorhanden.")
157158
st.stop()
158159

159160
# Normalize the dataframe (Handle Index)

src/frequenz/cs_reporting/app_pages/solar.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def capture_figures() -> Iterator[list[Figure]]:
4949

5050
def render() -> None:
5151
"""Render the Solar Maintenance & Monitoring page."""
52-
st.title("Solar Maintenance & Monitoring")
52+
st.title("Solar-Wartung & Monitoring")
5353
st.divider()
5454

5555
inputs_data, submit_button = collect_solar_sidebar_inputs(
@@ -68,15 +68,16 @@ def render() -> None:
6868

6969
if not submit_button:
7070
st.info(
71-
"Select options in the sidebar and click Start to run the solar workflow."
71+
"Wählen Sie Optionen in der Sidebar und klicken Sie auf Starten, "
72+
"um den Solar-Workflow auszuführen."
7273
)
7374
return
7475

7576
try:
7677
microgrid_config = get_microgrid_config(inputs_data.microgrid_id)
7778
except (KeyError, RuntimeError) as exc:
7879
st.error(
79-
f"Configuration not found for microgrid {inputs_data.microgrid_id}: {exc}"
80+
f"Konfiguration für Microgrid {inputs_data.microgrid_id} nicht gefunden: {exc}"
8081
)
8182
return
8283

@@ -93,12 +94,12 @@ def render() -> None:
9394
baseline_models=inputs_data.baseline_models,
9495
)
9596
except Exception as exc: # pylint: disable=broad-except
96-
st.error(f"Failed to prepare request: {exc}")
97+
st.error(f"Anfrage konnte nicht vorbereitet werden: {exc}")
9798
# Improve logging (could use proper logger, simplistic print for now as in original)
9899
print(f"Error building workflow request:\n{traceback.format_exc()}")
99100
return
100101

101-
with st.spinner("Running workflow..."):
102+
with st.spinner("Workflow wird ausgeführt..."):
102103
with capture_figures() as new_figures:
103104
plot_data = asyncio.run(run_workflow(user_config_changes=user_request))
104105

@@ -107,7 +108,7 @@ def render() -> None:
107108

108109
PAGE = PageSpec(
109110
key="solar",
110-
title="Solar Monitoring",
111+
title="Solar-Monitoring",
111112
icon="",
112113
order=2,
113114
render=render,

src/frequenz/cs_reporting/components/inputs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def _resolve_container(container: Any | None) -> Any:
2323

2424

2525
def microgrid_selector(
26-
label: str = "Microgrid ID",
26+
label: str = "Microgrid-ID",
2727
ids: Iterable[int] = range(1, 2),
2828
key_prefix: str = "",
2929
container: Any | None = None,

src/frequenz/cs_reporting/components/plot_charts.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ def plot_percentage_bar(
9393
showlegend=False,
9494
annotations=[
9595
{
96-
"text": "No data to display",
96+
"text": "Keine Daten zur Anzeige",
9797
"x": 0.5,
9898
"xref": "paper",
9999
"y": 0.5,
@@ -114,18 +114,18 @@ def plot_percentage_bar(
114114
}
115115
residual = total - sum(segments.values())
116116
if abs(residual) > 1e-6:
117-
segments["Others"] = residual
117+
segments["Sonstige"] = residual
118118

119119
seg_with_pct = [
120120
(k, v, (v / total * 100.0 if total != 0 else 0.0)) for k, v in segments.items()
121121
]
122122
seg_with_pct.sort(key=lambda x: abs(x[1]), reverse=True)
123123

124-
# Assign palette colours in order; "Others" always gets slate
124+
# Assign palette colours in order; "Sonstige" always gets slate
125125
color_map: dict[str, str] = {}
126126
palette_idx = 0
127127
for label, _, _ in seg_with_pct:
128-
if label == "Others":
128+
if label == "Sonstige":
129129
color_map[label] = _OTHERS_COLOR
130130
else:
131131
color_map[label] = _PROFESSIONAL_PALETTE[
@@ -156,8 +156,8 @@ def plot_percentage_bar(
156156
customdata=[value],
157157
hovertemplate=(
158158
f"<b>{label}</b><br>"
159-
"Share: %{x:.1f}%<br>"
160-
"Energy: %{customdata[0]:,.0f} kWh"
159+
"Anteil: %{x:.1f}%<br>"
160+
"Energie: %{customdata[0]:,.0f} kWh"
161161
"<extra></extra>"
162162
),
163163
)

src/frequenz/cs_reporting/components/sidebar_inputs.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ def collect_sidebar_inputs(
6666
# Microgrid Section
6767
st.subheader("Microgrid")
6868
microgrid_id = inputs.microgrid_selector(
69-
label="Microgrid ID",
69+
label="Microgrid-ID",
7070
ids=get_microgrid_ids(),
7171
key_prefix=key_prefix,
7272
container=st,
@@ -89,7 +89,7 @@ def collect_sidebar_inputs(
8989
columns = st.columns(2)
9090
with columns[0]:
9191
start_date = st.date_input(
92-
"Start",
92+
"Von",
9393
value=start_initial,
9494
max_value=today,
9595
key=start_key,
@@ -211,11 +211,11 @@ def collect_solar_sidebar_inputs(
211211
try:
212212
available_microgrids = get_microgrid_ids()
213213
except RuntimeError as exc:
214-
st.error(f"Failed to load microgrid configurations: {exc}")
214+
st.error(f"Microgrid-Konfigurationen konnten nicht geladen werden: {exc}")
215215
return None, False
216216

217217
if not available_microgrids:
218-
st.error("No microgrid configurations found.")
218+
st.error("Keine Microgrid-Konfigurationen gefunden.")
219219
return None, False
220220

221221
timezone_options_list = list(timezone_options or TIMEZONE_OPTIONS)
@@ -248,7 +248,7 @@ def collect_solar_sidebar_inputs(
248248
st.subheader("Workflow")
249249
language = st.selectbox(
250250
"Sprache",
251-
options=["English", "Deutsch"],
251+
options=["Deutsch", "Englisch"],
252252
index=0,
253253
)
254254
resample_period = st.text_input(
@@ -280,7 +280,7 @@ def collect_solar_sidebar_inputs(
280280
index=timezone_default_index,
281281
)
282282

283-
submit_button = st.form_submit_button("Start", use_container_width=True)
283+
submit_button = st.form_submit_button("Starten", use_container_width=True)
284284

285285
inputs_data = SolarWorkflowInputs(
286286
microgrid_id=int(microgrid_id),

src/frequenz/cs_reporting/components/ui.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,6 @@ def render_plot_card(title: str, fig: object) -> None:
4545
elif isinstance(fig, Figure):
4646
st.pyplot(fig)
4747
else:
48-
st.warning("Unsupported figure type.")
48+
st.warning("Nicht unterstützter Figurtyp.")
4949

5050
st.markdown("</div></div>", unsafe_allow_html=True)

src/frequenz/cs_reporting/services/solar_workflow.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ def build_workflow_request(
102102

103103
days_since_start = max(0, (end_timestamp - start_dt).days)
104104
actual_rolling_duration = min(rolling_view_duration, days_since_start)
105-
lang_code = {"English": "en", "Deutsch": "de"}.get(language, "en")
105+
lang_code = {"English": "en", "Englisch": "en", "Deutsch": "de"}.get(language, "de")
106106

107107
weather_api = os.environ.get("WEATHER_API_URL")
108108
reporting_api = os.environ.get("REPORTING_API_URL")

0 commit comments

Comments
 (0)