Lokalisierungsgrenze und Quellgenerator
PowerPortalsPro holt jeden von Dataverse Metadata abgeleiteten String, den eine Route benötigt, vor dem ersten Paint der Route, sodass Nutzer nie den kurzen Rückfall-Key-Text sehen, den On-Demand-Lokalisierungsbibliotheken normalerweise erzeugen. Der Mechanismus besteht aus drei Hälften – einem statischen Standardbundle , das einmal pro Sitzung geladen wird und Framework + App-Strings (alles außerhalb tables.* / choices.*abdeckt), einen Build-Time-Quellcode-Generator, der ein Manifest pro Komponente der Tabelle und View-Tokens ausgibt, die jede Komponente benötigt, sowie eine Laufzeitkomponente LocalizationBoundary , die die Routenansicht umwickelt und parallele Pro-Ressourcen-Bundle-Abrufe verteilt, bevor die Route rendert.
Die LocalizationBoundary-Komponente
LocalizationBoundary wickelt AuthorizeRouteView (oder RouteView) in Routes.razor. Bei jeder Navigation liest es das quellgenerierte LocalizationManifest statische Feld des aktuellen Seitentyps, vereint die Token mit beliebigen BaselinePrefixes Consumer-Konfigurationen, ruft IAsyncStringLocalizer.EnsurePrefixesLoadedAsync die kombinierte Tokenliste auf und hält die Kinder vom Rendern ab, bis jedes Ressourcenpaket abgerufen und in den Cache eingegliedert wurde. Während das Abholen im Flug ist, bedeckt ein durchscheinendes Overlay mit einem Spinner das Sichtfenster, sodass der abgestandene Text der vorherigen Seite nicht sichtbar ist.
Baseline-Token
Die meisten Apps müssen überhaupt nicht gesetzt BaselinePrefixes werden – der Quellgenerator erkennt die Route pro Route Tabelle und sieht automatisch Token aus deinem Komponentencode an. Verwende es nur für app-bekannte tables.{name} / views.{viewId} Tokens, die nicht statisch erkennbar sind (z. B. eine Layout-Komponente, die immer einen Account Picker rendert, bei der die Tabelle implizit ist, aber kein TableName="account" Attribut im Markup erscheint, das der Generator scannt). Andere Token-Formen werden während der Laufzeit stillschweigend verworfen – die eigenen Komponentenstrings des Frameworks werden im statischen Standardpaket ausgeliefert, daher ist Passing components.PowerPortalsPro.* or app.* Here ein No-Op.
Verkabeln
Die Starter-Vorlage konfiguriert LocalizationBoundary sich für dich. Wenn du es manuell integrierst, platziere es im Wrapping Routes.razor der Routenansicht, so:
// <LocalizationBoundary> Wickelt den Pro-Route-Inhalt ab und wartet zurück</LocalizationBoundary>
// rendert, bis die deklarierten Bündel jeder Seite sich festgelegt haben. Es erbt
// die Präfix-Deklarationen von nachträglichen useLocalization()-Aufrufen und
// aus dem 'Präfixe'-Prop auf dem Parent <PowerPortalsProProvider> (Satz</PowerPortalsProProvider>
// da sind app-weite Tabellen, die Chrome auf jeder Seite berührt).
import { LocalizationBoundary } from '@powerportalspro/react-fluent';
import { Outlet } from 'react-router-dom';
function Layout() {
return (
<LocalizationBoundary>
<Outlet />
</LocalizationBoundary>
);
}@using PowerPortalsPro.Web.Blazor.FluentUI.Components
<Router AppAssembly="typeof(MyApp.RouteUrls).Assembly">
<Found Context="routeData">
<LocalizationBoundary RouteData="routeData">
<AuthorizeRouteView RouteData="routeData"
DefaultLayout="typeof(Layout.MainLayout)" />
</LocalizationBoundary>
</Found>
</Router>Der Quellgenerator und das Manifest
Das PowerPortalsPro.Localization NuGet-Paket liefert einen Roslyn-Quellcode-Generator, der alle und Code-Behind *.razor im konsumierenden Projekt zur Build-Zeit scannt. Für jede Komponente (alles, was erbt ComponentBase) emittiert sie eine partielle Klassenerweiterung, die einen public static readonly LocalizationKeyManifest LocalizationManifest Körper trägt. Das Manifest enthält die transitive Menge von tables.{name} und views.{viewId} Tokens, die die Komponente (und jedes statisch deklarierte Kind, das sie rendert) zur Laufzeit anfordert – jeder Token löst sich auf eine pro-Ressourcen-Bundle-URL auf, in der die Grenze vor dem ersten Malen parallel abruft. Strings außerhalb des tables.* / choices.* Teilbaums (app.*, components.*) stammen vom statischen Standardbündel und werden absichtlich aus dem Manifest ausgeschlossen.
Was der Generator erkennt
Der Scanner erkennt die gängigen Muster automatisch – keine Anmerkungen erforderlich:
_localizer["tables.foo.…"]Indexer-Zugriffe werden in den besitzendentables.fooToken gezwungen;_localizer["tables.foo.views.{guid}.…"]Zugriffe werden zu einem Tokenviews.{guid}.@inject IStringLocalizer<T>In einer Razor-Datei kann der Generator verfolgen, an welche Felder der Lokalizer gebunden ist, sodass Indexer-Zugriff auf diese Felder ihre Token korrekt beiträgt.GetPrefixedLocalizer("tables.foo")Calls haben ihr Präfix in die Matching-Tabelle oder das View-Token gezwungen.DefaultViewId/ViewId/ AttributeViewIdsauf Grid-Komponenten sendenviews.{guid}Token aus — Der Server löst die besitzende Tabelle zur Anfrage aus der ID auf, sodass das Manifest sie nicht kennen muss.TableName="name"Attribute auf Grid-/Datensatzkomponenten senden ein Tokentables.{name}aus. Das zugleichende per-Tabellen-Bundle liefert Anzeigenamen, Spaltenmetadaten, die Ansichten der Tabelle (und deren Spaltenüberschreibungen) sowie alle globalen Optionsset-Optionen, auf die die Spalten der Tabelle verweisen – alles, was die Seite für diese Tabelle in einem Abruf benötigen könnte.- Direkte Tags der Kindkomponenten UND
@typeof(SomeComponent)Referenzen in Attributwerten ziehen das Manifest des Kindes in die transitive Menge des Elternteils, sodass eine Seite, die dynamische Komponenten hostet, weiterhin ihre Token vorherholt.
Inspektion eines Manifests
Das generierte LocalizationManifest Feld ist ein gewöhnliches öffentliches statisches Feld – du kannst es aus jedem C#-Code überprüfen oder in deinem Debugger erweitern. Tokens sind deterministisch sortiert, damit inkrementelle Builds die generierte Datei nicht umkrempeln.
// React hat kein Build-Time-Manifest – deklarieren Sie die Präfixe pro Seite
// imperativ über useLocalization(). Der Hook schickt die Token durch
// dieselbe entbounced-Abruf-Pipeline, aus der die LocalizationBoundary liest,
// Eine Seite, die sie deklariert, zeigt also keine rohen Schlüssel an, wenn man zu ihnen navigiert.
import { useLocalization } from '@powerportalspro/react';
function Dashboard() {
useLocalization([
'tables.opportunity',
'views.a1b2c3d4-e5f6-4789-abcd-112233445566',
]);
// ... Seiteninhalt ...
}// Erstellt von PowerPortalsPro.Localization.SourceGenerators
public partial class Dashboard
{
public static readonly LocalizationKeyManifest LocalizationManifest =
new LocalizationKeyManifest(
"tables.opportunity",
"views.a1b2c3d4-e5f6-4789-abcd-112233445566"
);
}On-Demand-Fallback
Alles, was der Generator zur Kompilierungszeit nicht auflösen kann – Schlüssel, die aus Laufzeitausdrücken zusammengesetzt sind, Referenzen, new Guid(SomeQualifiedConst) die eine Projekt-Referenz-Grenze überschreiten usw. – fällt in den On-Demand-Pfad des Lokalizers. Ein Cache-Miss auf einem tables.X.… oder tables.X.views.Y.… Schlüssel wird in den besitzenden Bundle-Token gezwungen, in die Warteschlange gestellt, kurz entbounced und im Hintergrund abgerufen; LocalizationBoundary der Nachfolge-Teilbaum wird nach Abruf wieder eingebunden, sodass veralteter Fallback-Text automatisch korrigiert wird. Fehler bei anderen Tastenformen (app.*, components.*) werden ignoriert – das statische Standardbündel hatte seine Chance, also bedeutet ein Fehler, dass der Schlüssel tatsächlich nicht existiert.
Benutzerdefinierte Ansichten, die in JSON definiert sind
Benutzerdefinierte Ansichten, die Sie in einer Lokalisierungs-JSON-Datei ausliefern (z. B. app.en.json) – typischerweise Grid-Ansichten mit einem handverlesenen GUID, clientseitig CustomViewDefinitions auf einem Raster definiert – werden vom Per-View-Bundle-Endpunkt automatisch aufgenommen. Der Bundle-Service leitet die Besitztabelle aus der Schlüsselform (tables.{owningTable}.views.{viewId}.…) ab und nicht aus der reinen savedquery Metadaten-Karte, sodass eine Ansicht, die in Dataverse nicht existiert, weiterhin über das Manifest ausgeliefert wird. Schlüssel werden unbeschreibungssensitiv abgeglichen: Eine JSON-Datei mit "28299C6F-EBC0-4206-9E11-A373D4C9891F" (Großbuchstaben, die natürliche Art, ein GUID-Literal zu erstellen) löst sich korrekt auf, wenn der Quellgenerator aus deinem Code sendet views.28299c6f-ebc0-4206-9e11-a373d4c9891f .
Anmerkung
Referenz
PowerPortalsPro.Localizationaus jedem Projekt, das Blazor-Komponenten hostet. Das Paket liefert sowohl die Laufzeittypen (IStringLocalizer,IAsyncStringLocalizer, ,LocalizationKeyManifest)LocalizationBoundaryals auch den Quellgenerator als Analysator-Asset – die Verbraucher erhalten beim nächsten Build automatisch manifeste Emission ohne zusätzliche Konfiguration.
LocalizationBoundary Baureihe
Parameter
Name | Typ | Default | Beschreibung |
|---|---|---|---|
BaselinePrefixes | IEnumerable<string>? | Zusätzliche | |
ChildContent | RenderFragment? | Der Routen-Inhalt wird gerendert, sobald die Lokalisierungen bereit sind. Normalerweise ist ein | |
RouteData | RouteData? | Die aktuelle Route ist LocalizationBoundary.RouteData. Das abgeleitete Präfix ist |
BaselinePrefixesChildContentRouteDataLocalizationBoundary.RouteData. Das abgeleitete Präfix ist 