Class Localizer

Namespace
RentADeveloper.ResXLocalization
Assembly
ResXLocalization.Core.dll

The default ILocalizer implementation: an ambient, process-wide localizer that resolves strings from registered ResourceManager instances and notifies bindings when the culture changes. Access the shared instance through Current.

public sealed class Localizer : ILocalizer, INotifyPropertyChanged
Inheritance
Localizer
Implements
Inherited Members
Extension Methods

Remarks

Like the UI frameworks it serves, this class is designed for UI-thread use and is not synchronized: register resource managers once at startup and set CurrentCulture, MessageFormatter, LookupFailureTextBuilder and EnumKeyBuilder from the UI thread, so change notifications reach bindings on the thread WPF and Avalonia expect.

Constructors

Localizer()

Initializes a new, isolated localizer with no registered resource managers. The XAML markup extensions always resolve through the ambient Current instance - create your own instance only where isolation matters, such as unit tests or code-behind consumers that inject ILocalizer.

public Localizer()

Properties

Current

Gets or sets the shared, ambient localizer instance used throughout the application. Register resource managers on it at startup and set CurrentCulture to switch languages. Applications may replace it at startup with their DI-owned implementation; markup extensions always resolve through the current value.

public static ILocalizer Current { get; set; }

Property Value

ILocalizer

Exceptions

ArgumentNullException

The assigned value is null.

CurrentCulture

Gets or sets the culture used to resolve resources. Setting a different value raises PropertyChanged, advances RefreshTriggeringRevision and raises CultureChanged.

public CultureInfo CurrentCulture { get; set; }

Property Value

CultureInfo

The active culture. Defaults to CurrentUICulture.

Remarks

Setting this property changes only how this localizer resolves translations; it deliberately does not touch CurrentUICulture or DefaultThreadCurrentUICulture. ResXLocalization is a translation engine, not a formatting engine - if number, date, or currency formatting should follow the selected language too, set those thread cultures yourself when handling CultureChanged.

Exceptions

ArgumentNullException

The supplied value is null.

EnumKeyBuilder

Gets or sets the function that builds the resource key of an enum lookup from the enumeration value and an optional key prefix.

public Func<Enum, string?, string> EnumKeyBuilder { get; set; }

Property Value

Func<Enum, string, string>

The enum key function. Defaults to the Enum_{EnumTypeName}_{Value} convention.

Remarks

The function receives, in order:

  1. The enumeration value of the lookup (Enum); never null.
  2. The key prefix the lookup passed (keyPrefix, KeyPrefix; string?), or null when it passed none. null means "use your own default"; a non-null value is an explicit override.

It returns the resource key, never null or empty. The default builds {keyPrefix ?? "Enum_"}{EnumTypeName}_{Value}, for example Enum_FileSortOrder_Ascending. A custom convention might look like this: Localizer.Current.EnumKeyBuilder = (value, keyPrefix) => (keyPrefix ?? "Text_") + value.GetType().Name + "." + value;

The builder applies to every enum lookup: Get(Enum, …), {l:LocalizeEnum} and LocalizeEnumConverter. It must not throw or return null or an empty string: an exception it throws propagates out of the lookup, and a null or empty result makes the lookup throw InvalidOperationException.

Assigning a different delegate instance advances RefreshTriggeringRevision, so bound text re-resolves; assigning the current instance again does nothing.

Exceptions

ArgumentNullException

The supplied value is null.

this[string]

Gets the localized string for the specified key by searching every registered ResourceManager in registration order.

public string this[string key] { get; }

Parameters

key string

The resource key to resolve.

Property Value

string

The localized string, Empty for a null or empty key, or the failure text (see ILocalizer).

LookupFailureTextBuilder

Gets or sets the function that produces the failure text: the text a lookup returns instead of a translation when no resource file contains the key or its value cannot be formatted.

public Func<LookupFailure, string> LookupFailureTextBuilder { get; set; }

Property Value

Func<LookupFailure, string>

The failure-text function. Defaults to failure => $"!{failure.Key}!".

Remarks

The function receives a LookupFailure describing the failed lookup; Reason tells the two cases apart. It returns the text shown instead of the translation; an empty string shows nothing.

Examples:

  • failure => $"[{failure.Key}]"
  • failure => failure.Key[(failure.Key.LastIndexOf('.') + 1)..]
  • failure => failure.Culture.TwoLetterISOLanguageName == "de" ? $"[fehlt: {failure.Key}]" : $"[missing: {failure.Key}]"
  • failure => failure.Reason == LookupFailureReason.TranslationNotFound ? $"[missing: {failure.Key}]" : $"[broken: {failure.Key}]"

The function is called on every failed lookup, so it should be cheap. It must not throw or return null: an exception it throws propagates out of the lookup, and a null result makes the lookup throw InvalidOperationException.

Assigning a different delegate instance advances RefreshTriggeringRevision; assigning the current instance again does nothing.

Exceptions

ArgumentNullException

The supplied value is null.

MessageFormatter

Gets or sets the formatter that turns each resolved resource value and the arguments of its lookup into the final string.

public IMessageFormatter MessageFormatter { get; set; }

Property Value

IMessageFormatter

The formatter. Defaults to Instance.

Remarks

Every lookup that resolves a value calls the formatter, also a lookup without arguments, so it is the one hook for all translated texts (see ILocalizer).

Localizer.Current.UseMessageFormat() from the ResXLocalization.MessageFormat package installs an ICU MessageFormat formatter here (plural, select). That formatter parses every resolved value as an ICU message, including the values of lookups without arguments.

Assigning a different instance advances RefreshTriggeringRevision; assigning the current instance again does nothing. Exceptions thrown by PropertyChanged handlers propagate from the assignment.

Exceptions

ArgumentNullException

The supplied value is null.

RefreshTriggeringRevision

Gets a value that changes whenever something changes that can change the result of a lookup - the culture, the formatter, the lookup-failure text builder, the enum key builder or the registered resource managers. Bind to it to re-resolve localized text; only whether the value differs is meaningful.

public int RefreshTriggeringRevision { get; }

Property Value

int

Remarks

The value advances after each real change of CurrentCulture, MessageFormatter, LookupFailureTextBuilder or EnumKeyBuilder, and after RegisterResourceManager(ResourceManager), UnregisterResourceManager(ResourceManager) and ClearResourceManagers() when they change the registered set. Each advance raises PropertyChanged for this property and then for the indexer (Item[]). Strings previously returned by Get are not updated.

Make these changes on the UI thread once views exist. In XAML, the trigger is <Binding Path="RefreshTriggeringRevision" Source="{x:Static core:Localizer.Current}" />.

Methods

ClearResourceManagers()

Removes every registered ResourceManager from the search-all set. Typed and scoped lookups are unaffected - they never consult the registration set. When at least one manager was registered, advances RefreshTriggeringRevision.

public void ClearResourceManagers()

Get(ResourceKey, MessageArguments?)

Resolves the localized string for a typed ResourceKey, which carries both the key name and the owning ResourceManager, and passes it to MessageFormatter with the supplied arguments.

public string Get(ResourceKey key, MessageArguments? arguments = null)

Parameters

key ResourceKey

The typed key identifying the resource and its resource manager.

arguments MessageArguments

The arguments of the lookup, built with MessageArguments; null or empty for none.

Returns

string

The localized (or formatted) string, or the failure text (see ILocalizer).

Remarks

Every resolved value passes through MessageFormatter; a formatting failure returns the failure text and raises LookupFailed (see ILocalizer).

Exceptions

ArgumentNullException

key is a default or otherwise uninitialized ResourceKey, whose Manager is null.

Get(Enum, MessageArguments?, ResourceManager?, string?)

Resolves the localized string for an enumeration value: EnumKeyBuilder builds the resource key from the value, which is then resolved like a string key and passed to MessageFormatter with the supplied arguments.

public string Get(Enum value, MessageArguments? arguments = null, ResourceManager? resourceManager = null, string? keyPrefix = null)

Parameters

value Enum

The enumeration value to resolve.

arguments MessageArguments

The arguments of the lookup, built with MessageArguments, for example MessageArguments.FromValues(count); null or empty for none.

resourceManager ResourceManager

The single resource manager to read the key from, or null (the default) to search every registered resource manager in registration order.

keyPrefix string

A key prefix passed to EnumKeyBuilder, overriding only its default prefix (by default Enum_); null (the default) lets the builder decide. Pass it by name: Get(sortOrder, keyPrefix: "Display_").

Returns

string

The localized (or formatted) string, or the failure text (see ILocalizer).

Remarks

Every resolved value passes through MessageFormatter; a formatting failure returns the failure text and raises LookupFailed (see ILocalizer).

Exceptions

ArgumentNullException

value is null.

InvalidOperationException

EnumKeyBuilder returned null or an empty string.

Get(string, MessageArguments?, ResourceManager?)

Resolves the localized string for the specified key - searching every registered ResourceManager in registration order, or reading a single, explicit one - and passes it to MessageFormatter with the supplied arguments.

public string Get(string key, MessageArguments? arguments = null, ResourceManager? resourceManager = null)

Parameters

key string

The resource key to resolve.

arguments MessageArguments

The arguments of the lookup, built with MessageArguments, for example MessageArguments.FromValues(3, 10); null or empty for none.

resourceManager ResourceManager

The single resource manager to read the key from, or null (the default) to search every registered resource manager in registration order. Pass it by name: Get("Key", resourceManager: rm).

Returns

string

The localized (or formatted) string, Empty for a null or empty key, or the failure text (see ILocalizer) in case the lookup failed.

Remarks

This is the lookup every other Get overload, the indexer and the XAML markup extensions go through. Every resolved value passes through MessageFormatter; a formatting failure returns the failure text and raises LookupFailed (see ILocalizer).

GetAvailableCultures()

Discovers the cultures for which any registered ResourceManager ships its own resources - the natural source for a language picker.

public IReadOnlyList<CultureInfo> GetAvailableCultures()

Returns

IReadOnlyList<CultureInfo>

The union of the available cultures across every registered resource manager, sorted by culture name, with InvariantCulture (the neutral resources) first when present. Empty when nothing is registered.

Remarks

The neutral (fallback) resources embedded in the main assembly are reported as InvariantCulture, because a ResourceManager does not expose which language they are written in. Map it to the language you declared in <NeutralLanguage> when building a picker.

Discovery probes every known culture for a satellite assembly, which is I/O-bound: call it once at startup and cache the result rather than per lookup or per binding.

GetAvailableCultures(ResourceManager)

Discovers the cultures for which a single ResourceManager ships its own resources. See GetAvailableCultures() for how neutral resources are reported and why the result should be cached.

public IReadOnlyList<CultureInfo> GetAvailableCultures(ResourceManager resourceManager)

Parameters

resourceManager ResourceManager

The resource manager to probe.

Returns

IReadOnlyList<CultureInfo>

The available cultures, sorted by culture name, with InvariantCulture (the neutral resources) first when present.

Exceptions

ArgumentNullException

resourceManager is null.

RegisterResourceManager(ResourceManager)

Registers a ResourceManager to be searched by search-all lookups. Managers are searched in registration order, and the first match wins; registering the same instance more than once has no effect. Adding a manager advances RefreshTriggeringRevision.

public void RegisterResourceManager(ResourceManager resourceManager)

Parameters

resourceManager ResourceManager

The resource manager to add to the search set.

Remarks

Which lookups need registration, which instance to register and where to register it is described on RegisterResourceManager(ResourceManager).

The registrations belong to this instance. A localizer assigned to Current later does not inherit them; register the resource managers on the instance that serves the lookups.

A manager whose type is exactly ResourceManager is checked once, when it is first registered: if no resources exist under its BaseName, it is rejected and not registered. A subclass of ResourceManager is registered without that check.

Exceptions

ArgumentNullException

resourceManager is null.

ArgumentException

resourceManager is exactly of type ResourceManager, and no resources exist under its base name - usually a hand-written base name that does not match the embedded resource.

UnregisterResourceManager(ResourceManager)

Removes a previously registered ResourceManager from the search-all set, for example when the plugin or module that owns its strings is unloaded. Typed and scoped lookups are unaffected - they never consult the registration set. Removing a manager advances RefreshTriggeringRevision.

public bool UnregisterResourceManager(ResourceManager resourceManager)

Parameters

resourceManager ResourceManager

The resource manager to remove from the search set.

Returns

bool

true when the manager was registered and has been removed; false when it was not registered.

Remarks

Managers are compared by reference: pass the instance that was registered, normally the generated <Name>Keys.ResourceManager. Another instance of the same file, such as the designer's <Name>.ResourceManager, is not found.

Exceptions

ArgumentNullException

resourceManager is null.

Events

CultureChanged

Occurs after CurrentCulture changes, carrying the previous and current culture. Subscribe to refresh values that are not resolved through a binding.

public event EventHandler<CultureChangedEventArgs>? CultureChanged

Event Type

EventHandler<CultureChangedEventArgs>

LookupFailed

Occurs when a lookup fails, before it returns the failure text: no resource file contains the key, or MessageFormatter threw while formatting its value. Reason tells the two apart. Subscribe to log missing or broken translations.

public event EventHandler<LookupFailedEventArgs>? LookupFailed

Event Type

EventHandler<LookupFailedEventArgs>

Remarks

Normal .NET resource fallback runs first, so a key that resolves from a parent or the neutral resources is not reported. A formatting failure is not specific to ICU MessageFormat: the default StringFormatMessageFormatter reports one for Broken {0, or for File {0} of {1} with one argument.

Handlers must not throw (see ILocalizer).

PropertyChanged

Occurs when a property value changes, including CurrentCulture, RefreshTriggeringRevision and the indexer, so that localized bindings re-resolve after a change that can change the result of a lookup.

public event PropertyChangedEventHandler? PropertyChanged

Event Type

PropertyChangedEventHandler