skills/davidortinau/maui-skills/maui-localization

maui-localization

SKILL.md

.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 NeutralLanguage set, ResourceManager may return null for 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:

  1. Specific culture — e.g. en-USAppResources.en-US.resx
  2. Neutral culture — e.g. enAppResources.en.resx
  3. 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

  • NeutralLanguage set in .csproj
  • Default AppResources.resx contains all keys
  • Each target language has its own AppResources.{culture}.resx
  • iOS/Mac: CFBundleLocalizations lists all supported languages
  • Windows: Package.appxmanifest declares <Resource Language="..." />
  • RTL cultures set FlowDirection appropriately
  • dotnet build regenerates .Designer.cs after .resx changes
Weekly Installs
6
GitHub Stars
71
First Seen
Mar 1, 2026
Installed on
opencode6
gemini-cli6
github-copilot6
codex6
kimi-cli6
amp6