maui-localization
.NET MAUI Localization
Resource files (.resx)
.NET MAUI uses standard .NET resource files for localization. Each .resx file contains name/value string pairs.
File naming convention
| File | Purpose |
|---|---|
AppResources.resx |
Default (fallback) language |
AppResources.es.resx |
Spanish (neutral) |
AppResources.fr-FR.resx |
French – France (specific) |
AppResources.zh-Hans.resx |
Chinese Simplified |
Place resource files in a Resources/Strings folder or project root. The naming pattern is {BaseName}.{CultureCode}.resx.
Project configuration
Set the neutral language in .csproj so the ResourceManager resolves the default culture correctly:
<PropertyGroup>
<NeutralLanguage>en-US</NeutralLanguage>
</PropertyGroup>
WARNING — Without
NeutralLanguageset,ResourceManagermay returnnullfor default-culture lookups at runtime. Always set it.
Generated accessor class
The default .resx file auto-generates a strongly-typed class (typically AppResources) with static properties for each string key:
// Auto-generated — do not edit manually
public static string WelcomeMessage => ResourceManager.GetString("WelcomeMessage", resourceCulture);
Access strings in C#: string welcome = AppResources.WelcomeMessage;
Culture resolution order
The runtime resolves resources in this order:
- Specific culture — e.g.
en-US→AppResources.en-US.resx - Neutral culture — e.g.
en→AppResources.en.resx - Default (fallback) —
AppResources.resx
If no match is found at any level, the fallback file is used.
XAML usage
Using x:Static
<ContentPage xmlns:resx="clr-namespace:MyApp.Resources.Strings">
<Label Text="{x:Static resx:AppResources.WelcomeMessage}" />
</ContentPage>
Using a binding with a localization service
For runtime language switching without restarting, expose resource strings through a helper that raises PropertyChanged:
public class LocalizationResourceManager : INotifyPropertyChanged
{
public static LocalizationResourceManager Instance { get; } = new();
public string this[string key] =>
AppResources.ResourceManager.GetString(key, AppResources.Culture)!;
public event PropertyChangedEventHandler? PropertyChanged;
public void SetCulture(CultureInfo culture)
{
AppResources.Culture = culture;
CultureInfo.CurrentUICulture = culture;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null));
}
}
<Label Text="{Binding [WelcomeMessage], Source={x:Static local:LocalizationResourceManager.Instance}}" />
Runtime culture switching
Change the UI culture at runtime:
var culture = new CultureInfo("es");
CultureInfo.CurrentUICulture = culture;
CultureInfo.CurrentCulture = culture; // for dates/numbers
AppResources.Culture = culture;
After setting the culture, UI elements bound via x:Static will not update automatically. Use the binding approach above or navigate to a new page to pick up the change.
Platform declarations
iOS and Mac Catalyst
Add supported localizations to Platforms/iOS/Info.plist (and Platforms/MacCatalyst/Info.plist):
<key>CFBundleLocalizations</key>
<array>
<string>en</string>
<string>es</string>
<string>fr</string>
</array>
Without this, iOS will not offer the app's languages in system Settings and may ignore culture overrides.
Windows
Declare supported languages in Platforms/Windows/Package.appxmanifest:
<Resources>
<Resource Language="en-US" />
<Resource Language="es" />
<Resource Language="fr-FR" />
</Resources>
Android
Android picks up .resx-based localization automatically. No additional manifest entries are required.
RTL layout support
Set FlowDirection to support right-to-left languages (Arabic, Hebrew, etc.):
<!-- App-wide -->
<Application FlowDirection="RightToLeft" />
<!-- Per-page or per-element -->
<ContentPage FlowDirection="RightToLeft">
<StackLayout FlowDirection="MatchParent">
<Label Text="{x:Static resx:AppResources.Greeting}" />
</StackLayout>
</ContentPage>
Detect and apply at runtime:
bool isRtl = CultureInfo.CurrentUICulture.TextInfo.IsRightToLeft;
FlowDirection = isRtl ? FlowDirection.RightToLeft : FlowDirection.LeftToRight;
Image localization
For culture-specific images, use a naming or folder convention and select at runtime:
string cultureSuffix = CultureInfo.CurrentUICulture.TwoLetterISOLanguageName;
string imageName = $"banner_{cultureSuffix}.png";
// Fall back to default if culture-specific image doesn't exist
bannerImage.Source = ImageSource.FromFile(
FileSystem.AppPackageFileExistsAsync(imageName).Result ? imageName : "banner.png");
Alternatively, reference image paths in .resx files so each culture points to its own asset.
VS Code setup
When using VS Code (not Visual Studio), the auto-generated .Designer.cs file for .resx may not regenerate on save. Ensure DesignTimeBuild is enabled:
<PropertyGroup>
<CoreCompileDependsOn>PrepareResources;$(CoreCompileDependsOn)</CoreCompileDependsOn>
</PropertyGroup>
Run dotnet build after adding or modifying .resx entries to regenerate the accessor class.
Quick checklist
-
NeutralLanguageset in.csproj - Default
AppResources.resxcontains all keys - Each target language has its own
AppResources.{culture}.resx - iOS/Mac:
CFBundleLocalizationslists all supported languages - Windows:
Package.appxmanifestdeclares<Resource Language="..." /> - RTL cultures set
FlowDirectionappropriately -
dotnet buildregenerates.Designer.csafter.resxchanges