Erfahrungen · Lessons Learned
Darstellung ist kein Detail
Dieser Erfahrungsbericht ist anonymisiert. Er beschreibt keine fertige Lösung, sondern einen Weg: Wir haben eine Web-Oberfläche gebaut, in der Darstellung keine Kosmetik ist, sondern Architektur. Dabei sind wir genau über die drei Themen gestolpert, die in Entwickler-Communities immer wieder aufkommen — Theming, Sprachen (i18n und .resx) und die Framework-Frage. Die Lehren daraus sind weniger technisch als methodisch.
Drei Themen, ein Muster
„Präsentation“ klingt nach Farbe und Schriftart. Tatsächlich stecken darin drei Querschnitts-Zustände, die jede Oberfläche von Anfang an begleiten: das Theme (hell/dunkel), die Sprache und der Zustand von Formularen (Beschriftungen, Fehlermeldungen). Alle drei teilen dasselbe Problem — sie müssen bekannt sein, bevor das erste Pixel entsteht.
(hell / dunkel)"] --> UI["Darstellung
der Oberfläche"] L["Sprache
(Texte, Formate)"] --> UI F["Formular-Zustand
(Labels, Fehler)"] --> UI S["Server: erster Rendervorgang"] --> T S --> L S --> F
Wer das übersieht, baut drei Mal dieselbe Lösung — oder drei Mal keine.
Lektion 1 — „Theme“ ist ein Zustand, keine Farbwahl
Ein Theme ist kein Stylesheet-Umschalter, sondern eine Zustandskette. In unserem Fall hat sich diese Aufteilung bewährt:
- Token: Farben und Maße liegen als semantische Variablen vor (nicht als konkrete Farbwerte in Regeln).
- Umschaltung: ein Attribut auf dem Wurzelelement (
data-theme) schaltet die Variablen um — nicht jede einzelne Regel. - Persistenz: der gewählte Wert überlebt den Neuladen-Vorgang (lokaler Speicher genügt).
- Flackern vermeiden (FOUC): eine einzige Zeile im
<head>setzt das Attribut, bevor das erste Stylesheet greift.
(gespeicherte Wahl)"] --> A["Wurzel-Attribut
data-theme"] A --> V["Semantische Variablen"] V --> C["Alle Komponenten"] P --> I["Inline-Zeile im head
(kein Flackern)"] I --> A
Lektion 1: Ein Theme ist ein Zustand mit vier Ebenen — Token, Umschaltung, Persistenz, Flackervermeidung. Wer nur die Umschaltung baut, hat ein Theme, das beim Neuladen verschwindet oder kurz aufblitzt.
Und eine harte Grenze: Komponenten, die ihren Inhalt in einem isolierten Baum (Shadow DOM) rendern, sind von außen nicht themebar — kein CSS-Selektor der Seite erreicht sie. Wir haben das an einer Framework-eigenen Meldung erlebt: Sie war mit Bordmitteln überhaupt nicht gestaltbar. Solche Stellen muss man entweder ersetzen oder in Ruhe lassen — Styling-Versuche von außen sind verlorene Zeit.
Lektion 2 — Sprache wird pro Anfrage entschieden, nicht pro Client
Der Sprachumschalter ist ein Knopf. Die Sprache ist ein Zustand — und zwar ein zweigeteilter. Bei serverseitig gerenderten Oberflächen entscheidet die Anfrage über die Kultur (über eine Abfrage, ein Cookie oder die Sprachwünsche des Browsers). Wird die Seite danach interaktiv (dauerhafte Verbindung zum Server), greift diese Entscheidung nicht mehr automatisch: Die interaktive Sitzung ist eine eigene Ausführung.
folgen erst mit der nächsten Anfrage
Daraus folgt eine praktische Konsequenz: Der Schalter muss zwei Dinge setzen — den Zustand der interaktiven Sitzung (sofort sichtbar) und den Wert, den die nächste Anfrage liest. Sonst wechseln manche Texte sofort und andere gar nicht. Am deutlichsten wird das bei Meldungen, die vor jeder Interaktivität erscheinen (Fehlerseiten, Verbindungsmeldungen): Sie haben keinen Zugriff auf den Sitzungszustand und brauchen die Sprache aus der Anfrage.
Lektion 2: Die Kultur gehört an zwei Stellen geführt — an der Anfrage und in der Sitzung. Wer nur eine Stelle pflegt, produziert eine Oberfläche, die „halb“ übersetzt ist.
Lektion 3 — Der .resx-Diskurs: was offiziell gilt
Kaum ein Thema wird in Communities so uneinheitlich beantwortet wie Lokalisierung von Komponenten. Der Blick in die offizielle Dokumentation räumt auf:
| Baustein | Status | Rolle |
|---|---|---|
IStringLocalizer / IStringLocalizer<T> | unterstützt | Texte für Komponenten und Bibliotheken |
IHtmlLocalizer / IViewLocalizer | nicht unterstützt | gehören zu einer anderen Render-Technik (MVC) |
Anker-Typ T | entscheidend | bestimmt Assembly, Namensraum und Ressourcen-Basisname |
Die eigentliche Stolperstelle liegt in Bibliotheken (wiederverwendbare Komponenten-Pakete). Dort gilt: Ressourcen werden nur gefunden, wenn drei Dinge stimmen —
- die Ressourcen sind als eingebettete Ressource eingebunden (sonst „findet“ der Lokalisierer sie still nicht),
- der Namensraum der Bibliothek entspricht der Erwartung (weicht er ab, muss er zur Laufzeit mitgegeben werden — er ist ein reiner Build-Wert),
- die Dateinamen folgen der Namenskonvention (Typname bzw. Anker-Klasse).
Lektion 3: Lokalisierung scheitert selten am Übersetzen — sie scheitert am Finden. Drei Prüfpunkte (Build-Action, Namensraum, Dateiname) klären fast jeden „es funktioniert einfach nicht“-Fall.
Lektion 4 — Formulare übersetzt man zweimal
Ein Formular hat zwei Textsorten: die Beschriftungen und die Fehlermeldungen. Beide werden getrennt verdrahtet — die einen über die Anzeige-Metadaten, die anderen über die Validierungs-Metadaten, jeweils mit Verweis auf dieselbe Ressource. In neueren Versionen gibt es zusätzlich eine Validierungs-Pipeline, die sich automatisch lokalisiert, sobald ein Lokalisierer registriert ist.
Lektion 4: Ein Formular ist erst fertig, wenn seine Fehlermeldungen übersetzt sind. Beschriftungen zu übersetzen und Meldungen zu vergessen, ist der häufigste Halb-Zustand.
Lektion 5 — Community-Muster gelten oft nur für einen Render-Modus
Der Standardratschlag im Netz lautet: „Nimm den lokalen Speicher des Browsers.“ Das ist ein Muster aus dem client-zentrierten Denken. Für Werte, die eine serverseitig gerenderte Antwort beeinflussen, ist es unbrauchbar — der Server sieht den lokalen Speicher nicht. Genau dort braucht es ein Cookie (oder eine serverseitige Ablage).
Dieselbe Verwechslung passiert beim Theme, bei der Sprache und bei Berechtigungen. Der Filter, der uns seither hilft:
serverseitige Antwort?"} F1 -->|"ja"| C["Cookie / serverseitige Ablage"] F1 -->|"nein"| L["Browser-Speicher genügt"] Q --> F2{"Ist es PII
(personenbezogen)?"} F2 -->|"ja"| A["Ablage nach IdP-/Aufbewahrungs-
Vorgaben, gerätebezogen"] F2 -->|"nein"| C
Lektion 5: Bevor man ein Muster übernimmt, prüft man, in welchem Render-Modus es gilt. Viele „Best Practices“ sind Antworten auf Fragen, die man selbst nicht gestellt hat.
Lektion 6 — Die Dokumentation schlägt das Forum
Zwei Beispiele aus derselben Arbeitswoche, die diesen Punkt am besten belegen:
- Die Framework-interne Meldung. Naheliegend war, das isolierte Overlay nachträglich „umzuschreiben“ — beobachtet, patched, hoffentlich stabil. Die Dokumentation beschrieb jedoch einen unterstützten Weg: Wer ein eigenes Element mit der erwarteten Kennung bereitstellt, bekommt die Kontrolle über Texte und Styling. Der Patch war nicht nur unnötig, er war die schlechtere Lösung.
- Der Kultur-Ablauf. Der naheliegende Weg (Wert beim Start setzen) ist client-zentriert gedacht. Die Dokumentation trennt sauber zwischen „Kultur je Anfrage“ und „Kultur je interaktiver Sitzung“ — und erklärt damit genau das Verhalten, das uns irritiert hatte.
Lektion 6: Die offizielle Dokumentation ist oft die kürzere Antwort. Wo sie schweigt, schlägt sie der Quelltext des Frameworks — aber nicht das Forum.
Und die Framework-Frage? — offen
Zur Vollständigkeit: Zu diesem Diskurs gehört auch die Frage, ob eine Oberfläche komponentenbasiert (mit serverseitigem Zustand und wiederverwendbaren Komponenten-Paketen) oder als minimaler HTML-Austausch („HTMX-artig“: der Server liefert HTML-Fragmente, der Browser tauscht sie aus) gebaut werden sollte. Beide Ansätze sind legitim — sie passen zu verschiedenen Aufgaben:
| Kriterium | Komponentenbasiert (serverseitiger Zustand) | Minimaler HTML-Austausch |
|---|---|---|
| Art der Oberfläche | zustandsreiche Arbeitsfläche, viele parallele Kontexte | dokumentenzentrierte Seiten, Formulare, Listen |
| Interaktionsdichte | hoch (Fenster, Karten, Live-Ströme) | niedrig bis mittel (Klick → Seite aktualisieren) |
| Host-Komplexität | höher (Sitzungen, verbundene Clients) | niedrig (ein Webserver, Antworten) |
| Wiederverwendung | Komponenten-Pakete über Projekte hinweg | Fragmente und Konventionen |
| Offline / mobiler Einsatz | anspruchsvoller | einfacher, aber ohne echten Offline-Modus |
Diese Entscheidung war zum Zeitpunkt dieses Beitrags offen. Sie wird an den eigenen Vorgaben gemessen — nicht an der Beliebtheit eines Werkzeugs. Für die dokumentenzentrierten Auftritte (wie diese Website) ist der minimale Weg naheliegend; für eine zustandsreiche Arbeitsfläche sprechen andere Gründe. Beides nebeneinander ist kein Widerspruch, sondern eine Arbeitsteilung.
Nachtrag (13.10.2026): Der Diskurs ist geführt. Die Arbeitsfläche wird komponentenbasiert gebaut; der minimale Austausch bleibt die Interop- und Teil-Update-Schicht. Den Ausschlag gab eine Beobachtung, die den Begriff „dokumentenzentriert“ schärft: Compliance ist kein Seiteninhalt, sondern ein Kontext — aus Anforderungs-, Prüf- und Nachweisketten über viele Dokumente gleichzeitig. Solchen Kontext hält man an einer Stelle, statt ihn je Fragment neu aufzubauen.
Transfer
- Darstellung ist Architektur. Theme, Sprache und Formular-Zustand sind Querschnittszustände — sie werden entworfen, nicht nachgerüstet.
- Zustände haben zwei Seiten. Was eine serverseitige Antwort beeinflusst, gehört an die Anfrage; was die interaktive Sitzung betrifft, gehört in die Sitzung.
- Lokalisierung scheitert am Finden, nicht am Übersetzen — drei Prüfpunkte genügen.
- Muster sind modusabhängig. Vor der Übernahme prüfen, für welche Welt sie geschrieben wurden.
- Erst die Dokumentation, dann das Forum. Das spart nicht nur Zeit, es vermeidet die schlechtere Lösung.
Nächster Beitrag: der Diskurs zur Framework-Wahl — mit den Kriterien, an denen wir ihn messen.