ProTranslate is designed so view models can stay independent from Avalonia, WPF, .NET MAUI, WinUI, and Uno. The core abstractions live in the ProTranslate namespace, and framework adapters only translate those abstractions into XAML markup extensions, attached properties, native FlowDirection, and binding refresh behavior.
Use XAML for view-only text. Use view-model properties when the text is part of application state, participates in command workflows, requires compiled binding or x:Bind, or combines translated strings with domain values.
| Scenario | Recommended surface |
|---|---|
| Static label in a view | T/Translate markup extension. |
| Static formatted label with supported native binding shape | F/Format markup extension. |
| Text attached to existing controls | Translation.Key, Translation.FallbackValue, and Translation.StringFormat. |
| Culture-aware shell direction | Translation.Culture plus Translation.AutoFlowDirection. |
| View-model text with commands or domain state | ITranslationService, generated accessors, or a typed proxy. |
| Region or measurement display | IGlobalizationService or a view-model service that wraps it. |
| Diagnostics display or logging bridge | DiagnosticReported or IProTranslateDiagnosticSink. |
Representative XAML:
<TextBlock Text="{Translate Shell.FileMenu}" />
<TextBlock Translation.Key="Orders.EmptyState"
Translation.FallbackValue="No orders" />
<Grid Translation.Culture="{Binding Strings.Culture}"
Translation.AutoFlowDirection="True" />
MAUI uses the shared ProTranslate XML namespace prefix, while WinUI uses using: syntax:
<ContentPage xmlns:pt="https://github.com/protranslate/xaml"
pt:Translation.AutoFlowDirection="True" />
<StackPanel xmlns:pt="using:ProTranslate.WinUI"
pt:Translation.AutoFlowDirection="True" />
A portable view model should depend on abstractions, not adapter packages:
public sealed class CulturePickerViewModel
{
private readonly IGlobalizationService _globalization;
public CulturePickerViewModel(IGlobalizationService globalization)
{
_globalization = globalization;
}
public CultureInfo CurrentCulture => _globalization.CurrentCulture;
public RegionProfile Region => _globalization.RegionProfile;
public MeasurementSystemProfile Units => _globalization.MeasurementSystemProfile;
public void UsePolish() =>
_globalization.SetCulture(CultureInfo.GetCultureInfo("pl-PL"));
}
Do not reference ProTranslate.Avalonia, ProTranslate.Wpf, ProTranslate.Maui, ProTranslate.WinUI, or ProTranslate.Uno from shared view-model projects. Adapters belong at the app or view layer.
Runtime culture switching flows through ICultureService and is observed by ITranslationService, IGlobalizationService, observable strings, and adapter binding sources.
public sealed class ShellViewModel : INotifyPropertyChanged, IDisposable
{
private readonly ITranslationService _translations;
public ShellViewModel(ITranslationService translations)
{
_translations = translations;
_translations.CultureChanged += OnCultureChanged;
}
public string Title => _translations.GetString("Shell.Title").Value;
public void Dispose() =>
_translations.CultureChanged -= OnCultureChanged;
private void OnCultureChanged(object? sender, CultureChangedEventArgs e) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Title)));
public event PropertyChangedEventHandler? PropertyChanged;
}
Use IObservableLocalizedString when the localized value itself is the object you want to bind:
public sealed class GreetingViewModel : IDisposable
{
private readonly IObservableLocalizedString _greeting;
public GreetingViewModel(ITranslationService translations)
{
_greeting = translations.Observe("Greeting", "Marta");
}
public string Greeting => _greeting.Value;
public void Dispose() => _greeting.Dispose();
}
Observable strings subscribe to culture changes. Dispose them from long-lived view models, view-model collections, or navigation caches.
ITranslationService.GetString uses the active UI culture for lookup. ITranslationService.Format formats with the active formatting culture:
public string InvoiceTotalText =>
_translations.Format("Invoice.Total", InvoiceTotal);
This separation is important for applications that display one language but format numbers, dates, or currency for another region. Format failures produce structured diagnostics and return the unformatted localized value by default unless TranslationFormatFailureBehavior.ReportAndThrow is configured.
Use IGlobalizationService when a view model owns region or unit policy:
public void UseUnitedStatesRegion()
{
_globalization.SetRegionOverride(new RegionInfo("US"));
OnPropertyChanged(nameof(RegionName));
OnPropertyChanged(nameof(UnitSystem));
}
public string RegionName => _globalization.RegionProfile.NativeName;
public MeasurementSystem UnitSystem => _globalization.MeasurementSystem;
The core resolves US, LR, and MM to USCustomary, GB to Imperial, metric regions to Metric, and remaining regions to Custom. Use IUnitConversionService and ILocalizedUnitFormatter for display values that depend on user unit preferences; the samples use these services for distance and temperature text.
x:BindCompiled binding systems need real CLR members. Prefer one of these patterns:
ProTranslateStrings properties for static text.ProTranslateAccessors wrapped by view-model properties for custom naming or composition.PropertyChanged when culture changes.Avalonia samples enable compiled bindings with x:DataType. WinUI and Uno samples use x:Bind against strongly typed view-model members:
<TextBlock Text="{x:Bind ViewModel.Strings.AppTitle, Mode=OneWay}" />
<TextBlock Text="{x:Bind ViewModel.InvoiceTotalText, Mode=OneWay}" />
This avoids reflection-only dynamic lookup paths and keeps trimming and AOT behavior easier to reason about.
For new code, treat adapter markup extensions as concise XAML convenience APIs. Use generated CLR properties or view-model properties as the default path when the target framework has compiled binding, x:Bind, NativeAOT, or strict trimming requirements.
LocalizedString with the same missing-key and diagnostic semantics as built-in providers.ITranslationService for text, ICultureService for culture switching, IGlobalizationService for region and measurement policy, and diagnostic abstractions for reporting.ProTranslate.Abstractions, not a UI framework package.PropertyChanged for every computed localized property affected by culture, region, measurement, or argument changes.CultureServiceOptions when a host needs current/default thread culture isolation.