Repository navigation
Localization de
🌐 Diese Seite auf: English · Português · Español · Français · Deutsch
Mehrsprachigkeit in eQuantic.UI folgt einem Gesetz: Wer entwickelt, lokalisiert mit dem, was .NET
selbst anbietet: .resx, dem stark typisierten Accessor, CultureInfo. Es sieht nie einen
JavaScript-Katalog oder eine DSL des Frameworks. Der vollständige Entwurf (Track L: wie eqc
ResourceManager in den Browser brückt, Kataloge je Kultur, die Formatierungs-Teilmenge, die
ehrlichen Zäune) lebt im Repository unter docs/I18N-PLAN.md. Diese Seite dokumentiert, was heute
ausgeliefert ist und was noch Entwurf ist.
Seit 0.2.0-preview.24
Komponenten verdrahten nie einen an den Nutzer gerichteten String fest. Jedes eingebaute Label (die
Umschaltankündigungen, die ein Screenreader hört ("Checked", "On"), die Verwerfen-Affordanzen, der
Suchplatzhalter, die Suchleiste des Code-Editors) liest SdkStrings, eine statische Klasse aus
Eigenschaften (nie Konstanten: Eine Konstante überquert Assemblys per WERT, und inlinete Aufrufstellen
würden nach dem Lernen der Kulturen weiter die Sprache der Build-Maschine ankündigen). Diese Naht ist
heute resx-gestützt: Jede Eigenschaft liest SdkResources, einen gewöhnlichen Designer-Accessor, und
keine Komponente hat sich dabei geändert. Im Web schreibt eqc den Accessor in eine Suche im
Kulturkatalog um, den die Shell inlinet, sodass dieselbe Eigenschaft auf dem Server aus einem
Satellitenassembly und im Browser aus JSON antwortet.
Eine Anwendung mit null eigenen resx ist dadurch schon lokalisiert: Die Schlüssel des SDK treten dem Katalog jeder Kultur bei, die die Anwendung ausgibt, sodass ein Screenreader "Marcado" auf einer pt-BR-Seite hört, die niemand übersetzt hat.
Welche Sprachen ausgeliefert werden, und wie man weniger ausliefert. Die Strings des SDK sind
heute nach pt-BR und es übersetzt, und sie reisen IM Paket als Satellitenassemblys
(lib/net10.0/<culture>/eQuantic.UI.Components.resources.dll, je etwa 8 KB): ein Paket, das seine
Sprachen trägt, wie Roslyn und die Testplattform die ihren ausliefern, statt ein Paket je Sprache.
Eine Anwendung, die nur einige davon will, braucht kein anderes Paket, weil .NET diese Entscheidung
schon besitzt:
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>Gemessen an einem Konsumenten des veröffentlichten Pakets: Ohne sie trägt die Ausgabe der Anwendung
es/ und pt-BR/; mit ihr keines von beiden. Die eine Stelle, an der sie nicht gilt, ist eine
PROJEKT-Referenz: Die eigenen Beispiele dieses Repositorys behalten die Satelliten in beiden Fällen,
weil die Eigenschaft filtert, was über ein Paket ankommt.
Seit 0.2.0-preview.24
Ein aus dem Finder gestarteter GUI-Prozess trägt kein LANG/LC_*, also startet .NET invariant,
selbst auf einer pt-BR-Maschine. Die Shells lösen die Locale-Wahrheit der Plattform vor dem ersten
Frame auf: NSLocale.preferredLanguages[0] als UI-Kultur und currentLocale als Formatkultur bei
Apple (dieselbe Trennung, die .NET als CurrentUICulture gegen CurrentCulture modelliert),
Locale.getDefault(), das auf Android beide versorgt, und PhotonCultureController kopiert das Paar
auf die Prozess-Statics und malt bei einem späteren Apply genau so neu, wie der Theme-Controller bei
einem Moduswechsel neu malt. Weil Photon echtes .NET ohne Transpilation ausführt, funktionieren
ResourceManager und Satellitenassemblys dort schon: Eine native Anwendung lokalisiert heute mit
gewöhnlichem resx.
Seit 0.2.0-preview.26
Das M0 von Track L: Eine Anwendung schreibt Lokalisierung genau so, wie jede .NET-Anwendung es tut:
ein Resources/Strings.resx plus Strings.pt-BR.resx, die gewöhnlichen Designer-Accessoren,
string.Format(Strings.Greeting, name), und die Seite antwortet in der Kultur der Anfrage auf BEIDEN
Hälften des Web-Ziels.
sequenceDiagram
participant B as Browser
participant S as Server
participant R as Runtime
B->>S: GET /page + Accept-Language
S->>S: wählt den Katalog (exakt, Eltern, neutral)
S-->>B: Shell + window.__EQ_CULTURE__
B->>R: der Boot installiert die Kultur
R->>R: hydriert mit DENSELBEN Strings
Die Teile, die Form der Theme-Brücke Zug für Zug spiegelnd:
-
eqcschreibt Accessoren um, es inlinet sie nie.Strings.Hero_Titlekompiliert zu$eq.str("Strings", "Hero.Title"), denn Inlinen würde die Kultur der Build-Maschine ins Bundle einbacken. Die Erkennung geschieht nach FORM (die statischenResourceManager- undCulture-Eigenschaften des Designers), also funktioniert jedes resx an jeder Stelle des Projekts, wie auch immer wer entwickelt es benannt hat. -
Der Build gibt Kataloge aus den Schlüsseln aus, die die Anwendung wirklich nutzt.
wwwroot/_equantic/strings/neutral.jsonplus eine{culture}.jsonje geschriebener Variante, jede zur Ausgabezeit gegen die Rückfallkette von .NET FLACHGEKLOPFT: Der Server wählt eine Datei, er verschmilzt nie. -
Der Server inlinet die Antwort.
UseRequestLocalization(die ANWENDUNG verdrahtet die Verhandlung, das SDK liest nur, was die Middleware gesetzt hat) treibt<html lang>und einen Shell-Platzwindow.__EQ_CULTURE__ = { name, formatName, format, strings }; der Boot installiert ihn VOR der Hydration, sodass der Client genau die Strings auflöst, die der Server gerendert hat.formatsind die eigenenNumberFormatInfoundDateTimeFormatInfoder Formatkultur, auf jeder Seite aus .NET geschrieben, ob die Anwendung einen String zu übersetzen hat oder nicht: Der Browser schreibt jede Zahl und jedes Datum und benennt jeden Monat eines Kalenders daraus (Seit 0.2.0-preview.61). -
string.Formatüber einem Accessor wird beim Build validiert. EQ2100 weist Argumente ab, die keine Strings sind, dazu Formatspezifizierer und Abweichungen in der Stelligkeit gegen das neutrale resx, die M0-Teilmenge von D7 des Plans.
Seit 0.2.0-preview.27
ICultureController ist die Hand der Anwendung an der SPRACHE, in der Form von IThemeController,
nach Schnittstellenname aufgelöst, sodass eine Komponente, die einen Umschalter anbietet, nie
erfährt, welches Ziel geantwortet hat. Sie spricht BCP-47-Namen, nie CultureInfo: Der Kontrakt
überquert in einen Browser, in dem es diesen Typ nicht gibt, und ein Name ist die eine Währung, die
.NET, das Web und beide Mobilplattformen teilen.
-
Web:
setCultureholt den Katalog der Kultur nur dann, wenn er nicht schon im Speicher ist, und wählt die Datei so, wie der SERVER sie wählt (exakt → Eltern → neutral; die Kette wurde beim Build flachgeklopft, die Suche bleibt also flach), und die Daten der Formatkultur aus/_equantic/culture/{name}.json(Seit 0.2.0-preview.61), tauscht sie und rendert über denselben Scheduler neu, denSetStatenutzt. Der Zustand überlebt; nur geänderter Text wird angefasst. Der Wechsel überdauert auch die Seite: Der Controller schreibt ASP.NETs eigenen Kultur-Cookie, in ASP.NETs eigenem Format, sodass die nächste Anfrage schon übersetzt ankommt. -
Native:
PhotonCultureControllerrealisiert dieselbe Schnittstelle: Die Prozess-Statics ändern sich und das Fenster malt neu. -
CultureSwitcherist die eingebaute Fläche: ein Segmented Control für zwei oder drei Sprachen, darüber hinaus ein Menü, das die aktive Option nach exaktem Namen und dann nach dem Sprachteil hervorhebt (eine alspt-BRausgelieferte Seite muss einenpt-Eintrag hervorheben).
new CultureSwitcher([
new("en", "English"),
new("pt-BR", "Português"),
new("es", "Español"),
])Seit 0.2.0-preview.27
{0:C2}, einmal in C# geschrieben, druckt R$ 1.234,50 auf einer pt-BR-Seite und 1234,50 € auf
einer es-Seite, wobei SSR und Client Zeichen für Zeichen übereinstimmen, und ein Kulturwechsel
formatiert live mit den Strings neu. Die D7-Teilmenge: N/F/P/C/D/E mit Präzision, die
Standardbuchstaben für Datum und Zeit, eigene Ziffern- und Datumsschablonen und das nackte {0} (das
in .NET NICHT invariant ist, und nun auch im Browser nicht mehr).
Der Beweis ist die Methode: Die gepinnte Fixture wird aus echtem .NET generiert
(value.ToString(spec, culture), sieben Kulturen, gewählt, um sich zu widersprechen) und die
transpilierte Runtime muss sie Byte für Byte reproduzieren, ein geschütztes Leerzeichen
eingeschlossen. Diese Richtung hat jede Abweichung zur Build-Zeit gefangen. Intl und .NET lesen
verschiedene ICU-Builds und legen eine Zahl nach verschiedenen Tabellen aus: Intl schrieb die
eigenen Ziffern von ar-EG, wo .NET ASCII-Ziffern schreibt, ein geschütztes Leerzeichen, wo das
Währungsmuster von .NET ein normales hat, und zwei Stellen für N, wo .NET auf ICU drei liest. Also
zeichnet der Browser jede Zahl und jedes Datum aus den eigenen NumberFormatInfo und
DateTimeFormatInfo der Kultur, die der Server mit der Seite sendet, und Intl legt nur eine Kultur
aus, zu der ohne Server zum Fragen gewechselt wurde (Seit 0.2.0-preview.61).
Was sich nicht einigen kann, wird beim Build abgelehnt, nie angenähert: EQ2100 weist Ausrichtung
und Spezifizierer außerhalb der Teilmenge in einer resx-Schablone ab; EQ2101 hält die Platzhalter
jeder Übersetzung gegen das neutrale resx, sodass ein pt-BR-String, der {2} verlangt, wo neutral
{0}/{1} hat, den Build scheitern lässt, statt nur für brasilianische Leserinnen zu werfen.
Der Umfang, klar gesagt: Das Versprechen exakter Übereinstimmung für STRINGS gilt für die Kulturen, die die Anwendung ausliefert, und eine zur Laufzeit verhandelte Kultur ohne geschriebenen Katalog fällt auf die neutralen Strings zurück. Ihre FORMATE sind die der Kultur der Anfrage, welche Kataloge auch existieren, aus den Daten geschrieben, die der Server sendet, deterministisch und nie browserabhängig; eine Seite ohne installierte Kultur formatiert in der invarianten Kultur (Seit 0.2.0-preview.61).
Seit 0.2.0-preview.29
Die Web-Hälfte dieser Geschichte ist eine Anfrage: eine Kultur hinein, SSR und Hydration stimmen überein. Ein Fenster hat keine Anfrage, ein Wechsel dort hat also drei Gelenke, die das Web nie hat: Die Plattform entscheidet die Anfangs-Locale, jemand kopiert sie auf die Statics von .NET, und der Baum muss NEU BAUEN, damit die Änderung die Pixel erreicht.
Eine Komponente sagt nichts davon. Der Abschnitt Language des Studio-Beispiels ist ein gewöhnlicher
CultureSwitcher, der ICultureController aus dem Kontext auflöst und keine Plattform nennt... die
gleiche Klasse, die der Browser rendert. Eine Kultur anzuwenden schreibt beide Statics (das Paar aus
D13: Ressourcen und Formate sind getrennte Entscheidungen) und malt das Fenster neu, der Wechsel landet
also im nächsten Frame, mit jedem Bit Seitenzustand unversehrt.
Der Screenshot-Pfad rendert es auf beide Weisen, und das hat den Meilenstein geschlossen: ohne
Argument zeigt das Fenster das Paar der MASCHINE (eine englische Oberfläche mit portugiesischen
Formaten, auf einem so eingestellten Mac), und mit --culture pt-BR zeigt es Marcado, Ativado,
Planilha neben R$ 1.234,50.
Das Beispiel zu schreiben fand den Fehler, den die Tests nicht finden konnten: Der Host registrierte nur den konkreten Controller, also löste eine Komponente, die die SCHNITTSTELLE verlangte, null auf, und der Umschalter schaltete nichts um. Zwei Registrierungen, eine Instanz. Eine Fähigkeit, die niemand in einem echten Fenster auflöst, ist eine Fähigkeit, die nicht funktioniert.
Seit 0.2.0-preview.28
dotnet new equantic-app erzeugt nun, was diese Seite predigt: ein Resources/Strings.resx mit
seinem pt-BR-Satelliten und dem gewöhnlichen Designer-Accessor, einen CultureSwitcher auf der
Startseite und die ganze Geschichte in einer Zeile,
string.Format(Strings.CountedTimes, _count). Das erste Projekt einer Entwicklerin antwortet in zwei
Sprachen, bevor sie Code geschrieben hat, und das ist der günstigste Moment zu lernen, dass
UI-Strings nie festverdrahtet werden. Siehe Erste Schritte für das vollständige
Scaffold.
Seit 0.2.0-preview.31
Eine Website zu übersetzen und die Übersetzungsgruppe nie zu deklarieren, so rankt eine lokalisierte
Website stillschweigend gegen sich selbst. Eine anwendungsweite Richtlinie,
options.UseAlternateLinks(AlternateUrls.PathPrefix(), "en", "pt-BR", "es"), setzt den ganzen
rel="alternate" hreflang-Satz plus x-default in den Head jeder Seite, mit erzwungenen Regeln für
Gegenseitigkeit und absolute URLs, statt sie der Anwendung zu überlassen. Siehe
Server-Integration.
Seit 0.2.0-preview.36
Eine öffentliche Website will jede Übersetzung an ihrer eigenen Adresse (/pt-BR/pricing), weil das
die einzige Form ist, die ein Crawler als Übersetzung indexiert und die eine Leserin teilen kann.
Eine Deklaration tut es:
builder.Services.AddUI(options => options
.ScanAssembly(typeof(Program).Assembly)
.UseCultureRoutes("en", "pt-BR", "es")); // die erste wird ohne Präfix ausgeliefert
…
app.UseRequestLocalization(); // ASP.NETs eigene Middleware, schon konfiguriert
app.MapUI();Nichts darin ist eigene Maschinerie des SDK. UseCultureRoutes füllt die
RequestLocalizationOptions der Plattform aus dieser Liste (Standardkultur, unterstützte Kulturen
und der RouteDataRequestCultureProvider von ASP.NET zuerst gesetzt) und registriert eine
culture-Routen-Einschränkung, sodass jede Seite (eine [Page] oder ein MapPage<T>) auch unter
/{culture:culture}/… gemappt wird. Von dort:
-
/pt-BR/pricingrendert in pt-BR, und das Segment gewinnt über den Cookie undAccept-Language: Eine URL, die eine Sprache nennt, ist ein Versprechen darüber, was die Seite sagt. -
Jede anwendungsinterne href trägt die Sprache: Die Autorin schreibt
/abouteinmal, und Server und Browser geben beide/pt-BR/aboutaus, während die Leserin auf Portugiesisch liest. EinLink, einTextRun, der verlinkt, und ein Markdown-Link ebenso (ein Run und ein Markdown-Link seit 0.2.0-preview.60: Sie behielten das Ziel wie geschrieben), und jeder interne wird beim Hover vorgewärmt. Absolute URLs, protokollrelative (//cdn…), Anker undmailto:werden in Ruhe gelassen. -
/fr/pricingist ein 404, nicht Englisch unter einer französischen Adresse: Die Einschränkung lässt genau die Präfixe zu, die Sie genannt haben. -
Ein nacktes
/pricing, auf Portugiesisch verlangt (der Cookie, denCultureSwitcherschreibt, oderAccept-Language), wird (302) nach/pt-BR/pricingumgeleitet, sodass das, was gesehen, geteilt und als Lesezeichen gesetzt wird, immer seine Sprache nennt. Die Standardkultur behält ihre nackten URLs. -
Die Sprache zu wechseln ist eine Navigation:
CultureSwitchergeht zur gleichen Seite an ihrer anderen Adresse, über den Router, ohne Neuladen. -
hreflangundx-defaultkommen aus derselben Karte ohne zweite Deklaration, und eincanonical, das eine Seite von Hand geschrieben hat, wird auf dem Weg hinaus lokalisiert, sodass es einen Crawler nie auf die falsche Übersetzung zeigt.
Eine Anwendung, die die Middleware mit der Lambda-Überladung konfiguriert
(app.UseRequestLocalization(o => …)), baut ihre Optionen inline und sieht nichts aus der DI, geben
Sie ihr dort also dieselbe Liste mit o.UseCultureRoutes("en", "pt-BR", "es").
Satelliten unter NativeAOT sind keine Frage mehr: Ein ausführbares Gestell
(scripts/verify-aot-satellites.sh) veröffentlicht ein natives Binary gegen die
Komponentenbibliothek und weigert sich zu bestehen, solange nicht en, pt-BR und es alle mit der
ÜBERSETZUNG antworten, denn ein verlorener Satellit stürzt nicht ab, er antwortet stillschweigend auf
Englisch, also vergleicht die Prüfung gegen die erwarteten Übersetzungen.
Noch vor uns: das Aufteilen der Kataloge je Seite, Pluralformen jenseits von zwei und ein
IHandleMetadata, das bei einem Kulturwechsel erneut läuft. RTL und die Schriftabdeckung je
Schriftsystem sind ausdrücklich andere Spuren.
🏁 Hier starten
🏗️ Architektur
- Architekturüberblick
- Write-once-Komponenten
- Deklarative Oberfläche
- Paketarchitektur
- Komponenten
- Styling
- Lokalisierung
- Analytics und GTM
📱 Write-once
- Photon-Engine
- Design System
- Fähigkeiten
- Speicher
- Formulare
- Code-Editor
- Markdown
- Mermaid
- E-Mail-Rendering
⚙️ Kompilierung
⚡ Runtime
🔌 Server
🎨 Ökosystem
🚀 Entwicklung