Coder Social home page Coder Social logo

localizationresourcemanager.maui's Introduction

LocalizationResourceManager.Maui Buy Me A Coffee

Enhanced .NET MAUI version of the Xamarin Community Toolkit LocalizationResourceManager.

NuGet

Name Info
LocalizationResourceManager.Maui NuGet

Background

I have been a fan of the Localization helpers and extensions in the Xamarin Community Toolkit and have been using this in my Xamarin projects. Since moving to .NET MAUI, I hoped for this to be part of the MAUI Community Toolkit. For good reasons the team has decided not to include this in MCT and a proposal is issued in the .NET Community Toolkit. But the XCT solution have dependencies to the Xamarin IMarkupExtension interface and the XCT WeakEventManager helper class, which makes it tricky to port to a non MAUI library. So until we have a official solution, or anyway is added to MCT, I have created this library for .NET MAUI.

Big shoutout to the original authors, Charlin Agramonte, Brandon Minnick, Maksym Koshovyi and the entire Xamarin Community Toolkit Team!

What's included?

Compared to the original solution we have some enhanced and added features:

  • Easy setup with builder pattern extension
  • Supports multiple Resource managers
  • Supports file based Resource managers
  • Supports storing and restoring of the latest set culture
  • New ILocalizationResourceManager interface registered for constructor injection with DI
  • Stores current Default / System culture
  • Supports Resource names with dots.
  • Option to set a placeholder text to be displayed if text is not found.
  • TranslateBindingExtension for custom binding with format and plural support in XAML by Stephen Quan.
  • Uses the WeakEventManager (.NET MAUI)

For localized texts used in XAML and/or code behind, we still have:

  • TranslateExtension (XAML Markup Extension)
  • LocalizedString (Track Culture Change in code behind)

Setup

Use the UseLocalizationResourceManagerbuilder pattern extension method for library configuration.

var builder = MauiApp.CreateBuilder();
builder
    .UseMauiApp<App>()
    .ConfigureFonts(fonts =>
    {
        fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
        fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
    })
    .UseLocalizationResourceManager(settings =>
    {
        settings.AddResource(AppResources.ResourceManager);
        settings.RestoreLatestCulture(true);
    });

Settings contains 6 methods for configuration:

  • AddResource (Add one or more Resource Managers)
  • AddFileResource (Add file based Resource Managers. Create/Read/Write at runtime with ResourceWriter and ResourceReader.)
  • InitialCulture (Set initial/startup culture, Default: Current System Culture)
  • RestoreLatestCulture (Restore latest set culture flag, Default: false, Note: Will override InitalCulture!)
  • SupportNameWithDots (Activate support for Resource Names with Dots when used with TranslateExtension. Option to set custom dot substitution. Default: "_")
  • SuppressTextNotFoundException (Suppress/Deactivate throwing the text not found exception. Option to set a placeholder text to be displayed if text is not found.)

Use in XAML

When used for localized texts in XAML pages, use the TranslateExtension:

  • Add namespace reference to library.
  • Use Translate extension with name of resource. (All resource libraries will be searched until name is found!)
<ContentPage
    x:Class="LocalizationResourceManager.Maui.Sample.MainPage"
    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:localization="clr-namespace:LocalizationResourceManager.Maui;assembly=LocalizationResourceManager.Maui">
<Label
    FontSize="18"
    HorizontalOptions="Center"
    SemanticProperties.Description="{localization:Translate WelcomeToMAUI}"
    SemanticProperties.HeadingLevel="Level2"
    Text="{localization:Translate WelcomeToMAUI}" />

Custom binding in XAML

Use the TranslateBindingExtension for custom binding with format and plural support.

Plural support in XAML

<Button
    x:Name="CounterBtn"
    Clicked="OnCounterClicked"
    HorizontalOptions="Center"
    SemanticProperties.Hint="{localization:Translate CounterBtnHint}"
    Text="{localization:TranslateBinding Count, TranslateFormat=ClickedManyTimes, TranslateOne=ClickedOneTime, TranslateZero=ClickMe}" />

The way it works is:

TranslateFormat : (optional) similar to StringFormat, but the format comes from a string resource, e.g. "Clicked {0} times"
TranslateOne : (optional) similar to StringFormat, but used for when the binding value is one (1), e.g. "Clicked {0} time"
TranslateZero : (optional) similar to StringFormat, but used for when the binding value is zero (0), e.g. "Click Me"

Date/Time in XAML

public DateTime CurrentDateTime { get; set; } = DateTime.Now;
<!-- DateIs string resource: "Date is: {0}" -->
<Label Text="{localization:TranslateBinding CurrentDateTime, TranslateFormat=DateIs}/"/>

<! -- TimeIs string resource: "Time is: {0:HH}:{0:mm}:{0:ss}" -->
<Label Text="{localization:TranslateBinding CurrentDateTime, TranslateFormat=TimeIs}/" />

Currency in XAML

public decimal Price { get; set; } = 123.45;
<!-- TotalPrice string resource: "Total Price is: {0:C}" -->
<Label Text="{localization:TranslateBinding Price, TranslateFormat=TotalPrice}" />

Translate collections in XAML

TranslateValue : (optional) Apply localization changes to a view model, e.g.

public IList<string> Fruits { get; set; } = new List<string> { "LBL_APPLES", "LBL_ORANGES" };
<CollectionView ItemsSource="{Binding Fruits}">
    <CollectionView.ItemTemplate>
        <DataTemplate>
            <Label Text="{localization:TranslateBinding . , TranslateValue=True}"/>
        <DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>

Translate true/false states in XAML

TranslateTrue : (optional) string resource used for when the binding value is true, e.g. "Yes", "On", "Activated"
TranslateFalse : (optional) string resource used for when the binding value is false, e.g. "No", "Off", "Deactivated"

public bool OrderSent { get; set; } = false;
<!-- Yes/No string resources: "Yes" / "No" -->
<Label Text="{localization:TranslateBinding OrderSent, TranslateTrue=Yes, TranslateFalse=No}" />

Use in Code

When used to handle localized texts in code behind or ViewModel, use the LocalizedString class:

  • Add LocalizedString to code behind or ViewModel to track culture changes
  • If needed, make binding to LocalizedString in XAML
public LocalizedString HelloWorld { get; } = new(() => $"{AppResources.Hello}, {AppResources.World}!");

...or to support multiple Resource managers...

public LocalizedString HelloWorld { get; }

public MainPage(ILocalizationResourceManager resourceManager)
{
    HelloWorld = new(() => $"{resourceManager["Hello"]}, {resourceManager["World"]}!");
<Label
    FontSize="32"
    HorizontalOptions="Center"
    SemanticProperties.HeadingLevel="Level1"
    Text="{Binding HelloWorld.Localized}" />

Set and Get Culture

To handle and access the Current or Default Culture, we inject the ILocalizationResourceManager interface into our code behind or ViewModel to access the LocalizationResourceManager instance:

  • Add the ILocalizationResourceManager interface to your constructor and store locally for later access.
  • Use CurrentCulture property to Get or Set CurrentCulture. (All text accessed by TranslateExtension or LocalizedString will be updated immediately!)
  • Use DefaultCulture property to Get Default/System culture.
  • Use GetValue method or Indexer operator [] to manually retrieve localized text based on Current culture.
  • Use ReleaseAllResources method to Release/Close all resources for all registered resources. (Use before manually accessing registered file based resources!)
public partial class MainPage : ContentPage
{
    private readonly ILocalizationResourceManager resourceManager;

    public MainPage(ILocalizationResourceManager resourceManager)
    {
        InitializeComponent();
        this.resourceManager = resourceManager;
public string? CurrentCulture => resourceManager?.CurrentCulture.NativeName;

...or...

public LocalizedString CurrentCulture { get; }

public MainPage(ILocalizationResourceManager resourceManager)
{
    CurrentCulture = new(() => resourceManager.CurrentCulture.NativeName);

One line to change Current Culture and Refresh ALL localized texts!

resourceManager.CurrentCulture = new CultureInfo("en");

Sample

Look at the Sample project for a example of how to use this library in an .NET MAUI application.

Sample Application

localizationresourcemanager.maui's People

Contributors

sirjohnk avatar stephenquan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

localizationresourcemanager.maui's Issues

Question: Custom filestructure with one .resx file per screen

Hi!

Thanks for creating this awesome project. :)

I am trying to figure out how its possible to have another filestructure than the original AppResource.[lang].resx and instead have one .resx file per screen.

The file structure I am looking for would be something like this:

Resources
    - Localization
        - StartView
            - StartView.da-DK.resx
            - LoginView.en-US.resx
            - ...
        - LoginView
            - LoginView.da-DK.resx
            - LoginView.en-US.resx
            - ...

Would this be possible? and if so how? Do I have to create a CustomResource manager? @SirJohnK

I tried adding multiple multiple resources as seen below, but I couldn't get it to work:

settings.AddResource(Resources.Localization.StartView.StartView.ResourceManager);
settings.AddResource(Resources.Localization.QrScanView.QrScanView.ResourceManager);
settings.AddResource(Resources.Localization.ModalAbout.ModalAbout.ResourceManager);

and then using it in a file like this (after successfully implementing your guide from here on how to use ContentView

DeviceIDText = new(() => resourceManager["deviceid"]);

It seems that it is not able to find the translated text of deviceid - and I suspect its related to my AddedResources?

Not an issue but question - multiple versions of the same language

Hi
Thanks for this one , its really good. I have a question that does not fall into the localization area , but in a way still does.
We have an app that is a white label app and different clients have different text

All using "en-gb"
Example LabelWelcome on MainPage
Client A LabelWelcome =Welcome
Client B LabelWelcome = Good Morning
Client C LabelWelcome = Morning

do you see the pattern?

Currently we just have one resource I.E AppResources.resx and we copy over the individual one for each client .

Is there any way a better solution?
thanks

Question about LocalizedString

hi
Is it possible to use LocalizedString in class or struct as property ?
if it possible , is there any consideration about it?
thank you

Use dots in resource names

It seems it's not possible to use dots in resource names because of the way the Path is parsed (here) and because of the brackets dynamically added here.

We have a project where the resource files are generated and contain lots of dots, so I'm looking for a way to use the LocalizationResourceManager but so far not much luck.

I found a way by referencing the entire LocalizationResourceManager.Maui project, reference my text resources using underscores instead of dots, and finally replace those underscores by dots programmatically with a text.Replace('_', '.') here.

I'm sure there must be a better way?

Thanks!

How can I use multiple ResourceManagers?

I like to have a .resx file per ContentPage, so my MauiApp builder looks like:

var builder = MauiApp.CreateBuilder()
	.UseMauiApp<App>()
	.UseLocalizationResourceManager(settings =>
	{
		settings.RestoreLatestCulture(true);

		settings.AddResource(Vistas.Localizables.LocalizacionPage.ResourceManager);
		settings.AddResource(Vistas.Localizables.MainPage.ResourceManager);
		// ...
	})
	// ...

Problem is that all my views .resx files have a key named "Title".

So, I use ILocalizationResourceManager from my ViewModel and the translate extension from my views XAML, but I always get Title from LocalizacionPage's resource manager (the first one I registered on my MauiApp builder).

I mean, if I write this on MainPage's XAML:
<ContentPage Title="{loc:Translate Title}">
Title gets LocalizacionPage's resource manager "Title" key value, but I want MainPage's resource manager value.

Or if I go:

public MainViewModel(ILocalizationResourceManager localizador)
{
	_localizador = localizador;
	
	var titleTest = _localizador[nameof(Title)];
}

titleTest var gets LocalizacionPage's resource manager "Title" key value, but I want MainPage's resource manager value.

So, how could I tell ILocalizationResourceManager to use certain ResourceManager?
Or even better:

  • Tell once at XAML
  • Inject correct ResourceManager on my ViewModel (maybe it would be needed to specify my ViewModel type on the settings.AddResource(...) method)

How to Use in a ContentView

How would I use/provide this ILocalizationResourceManager instance to a Code-Behind of a ContentView?
I registered the Code-Behind in MauiProgram.cs via builder.Services.AddTransient<MyContentView>();, but when I try to use it as follows, it just says that it can't be used like this and needs a default constructor. So, how would I use this in a ContentView?

public partial class MyContentView : ContentView
{
    private readonly ILocalizationResourceManager resourceManager;

    public MyContentView(ILocalizationResourceManager resourceManager)
    {
    
    }
}

Not working in iOS release mode

Hi, testing your solution i found out it works fine in all platforms but doesn't apply translations in RELEASE-mode builds for iOS. No problem at all in DEBUG-mode.

I am currently testing in a physical device, I will post again here if i find a workaround for this.

At least one resource must be added with Settings.AddResource!

I'm currently migrating my Xamarin.Forms app to Maui. Upon initializing my first View I receive the following error:

System.InvalidOperationException: At least one resource must be added with Settings.AddResource!

I'm adding the resource as described in my MauiProgram.cs:

.UseLocalizationResourceManager(settings =>
{
    settings.AddResource(AppLang.ResourceManager);
    settings.RestoreLatestCulture(true);
});

What am I missing? Using v1.2.1

using _localizationResourceManager

How can I use the _localizationResourceManager without passing it as parameter to

public MainPage(ILocalizationResourceManager localizationResourceManager)

Problem with properties

Hi
I am using your nuget.
I have this in my viewmodel:

THIS WORKS!
public LocalizedString Versione { get; } = new(() => string.Format(AppResource.VersioneFmt, AppInfo.Version, AppInfo.BuildString));

THIS NOT WORKS
[ObservableProperty]
Config configurazione;

    public LocalizedString Utente { get; } = new(() => string.Format(AppResource.UtenteFmt, Configurazione.Utente));

I have a compile error

Gravità Codice Descrizione Progetto File Riga Stato eliminazione
Errore CS0236 Un inizializzatore di campo non può fare riferimento alla proprietà, al metodo o al campo non statico 'MainViewModel.Configurazione' Esselunga STR Mobile (net7.0-windows10.0.19041.0) C:\Soluzione1\Progetti\str_mobile\ViewModels\MainViewModel.cs 33 Attivo

Translated in:
A field initializer cannot refer to the property, method, or non-static field

Thanks

Enhancement: return placeholder in GetValue instead of exception

Hello,

I'd like to suggest a modification to the GetValue(string text) method in the LocalizationResourceManager.

Current Behavior:

The method throws a NullReferenceException when a localized string isn't found:

return value ?? throw new NullReferenceException($"{nameof(text)}: {text} not found!");

when I use it in xaml like that:
xmlns:loc="clr-namespace:LocalizationResourceManager.Maui;assembly=LocalizationResourceManager.Maui"
<Label Text="{loc:Translate helloWorld}" />

Proposed Change:

Instead of throwing an exception, it might be more user-friendly to return a placeholder string:

return value ?? $"Text:{text}";

This approach would help avoid application crashes due to missing localization strings and instead display a placeholder, aiding in identifying the missing items.

I believe this change would enhance the overall usability of the library. Your feedback on this suggestion would be greatly appreciated.

Thank you!

FileNotFoundException

We have just an English and a French, but in trying to use this project we're getting an exception:

.UseLocalizationResourceManager(settings =>
{
    settings.AddResource(AppResources.ResourceManager);
    settings.RestoreLatestCulture(true);
});

This is the exception:
{System.IO.FileNotFoundException: File name: 'CabMdMobile.Maui.resources' at System.Reflection.Assembly.Load(AssemblyName assemblyRef, StackCrawlMark& stackMark, AssemblyLoadContext assemblyLoadContext) at System.Reflection.RuntimeAssembly.InternalGetSatelliteAssembly(Assembly assembly, CultureInfo culture, Version version, Boolean throwOnFileNotFound)}

Not really sure why it's having a problem. The files are in the same place as your example, have the same name "AppResources", the settings for the files in the csproj seem to be the same as your sample as well.

translations not working even when i followed all steps. im using .net 8.0 with prism

          why isnt this working, even tho everything is there

LocalizationResourceManager.CurrentCulture = new CultureInfo("es-ES");
xmlns:localization="clr-namespace:LocalizationResourceManager.Maui;assembly=LocalizationResourceManager.Maui"
<Label Margin="0,40,0,0" FontFamily="LatoLight" Text="{localization:Translate BuildVersion}" FontSize="12" HorizontalOptions="Center" TextColor="White" VerticalOptions="Start"/>

Screenshot 2024-04-16 at 01 25 02

Screenshot 2024-04-16 at 01 27 30

Originally posted by @mohammedmsadiq in #19 (comment)

Screenshot 2024-04-16 at 01 32 06

Use from ViewModels not having a dependency on MAUI?

I have my solution split in several projects where my project containing a ViewModels does not have a dependency on MAUI - just on the MVVM Community Toolkit.

Is there a way to access the ILocalizationResourceManager from ViewModels without taking a dependency on MAUI via LocalizationResourceManager.Maui?

E.g. in this example app, the MAUI dependent parts of localization and the view-agnostic parts of localization are put in separate projects so that the ViewModels can reference just the interface.

I see I could create a kind of wrapper pair (interface + implementation) myself, but it would be great to have this in the library!

Not working in android

Hello
I have a simple code like

<core:SfBusyIndicator Grid.Row="1"
                      BackgroundColor="White"
                      HeightRequest="150"
                      AnimationType="CircularMaterial"
                      OverlayFill="Transparent" 
                      TitlePlacement="Bottom"
                      TextColor="Black"
                      FontSize="Title"
                      Title="{localization:Translate SplashLoadingWaitContent}"
                      IsRunning="True">
</core:SfBusyIndicator>

The title is showing in Windows app but not showing in Android device
But the <Label Text="{localization:Translate SplashLoadingWaitContent}"></Label> works on both platfroms.

Make it easier to translate directly from a MVVM view model in XAML

Let's introduce a TranslateBinding markup extension (which is a mashup between Binding and Translate) to help support MVVM workflows:

Plural support in XAML

<Button
    x:Name="CounterBtn"
    Clicked="OnCounterClicked"
    HorizontalOptions="Center"
    SemanticProperties.Hint="{localization:Translate CounterBtnHint}"
    Text="{localization:TranslateBinding Count, TranslateFormat=ClickedManyTimes, TranslateOne=ClickedOneTime, TranslateZero=ClickMe}" />

The way it works is:

TranslateFormat : (optional) similar to StringFormat, but the format comes from a string resource, e.g. "Clicked {0} times"
TranslateOne : (optional) similar to StringFormat, but used for when the binding value is one (1), e.g. "Clicked {0} time"
TranslateZero : (optional) similar to StringFormat, but used for when the binding value is zero (0), e.g. "Click Me"

Date/Time in XAML

public DateTime CurrentDateTime { get; set; } = DateTime.Now;
<!-- DateIs string resource: "Date is: {0}" -->
<Label Text="{localization:TranslateBinding CurrentDateTime, TranslateFormat=DateIs}/"/>

<! -- TimeIs string resource: "Time is: {0:HH}:{0:mm}:{0:ss}" -->
<Label Text="{localization:TranslateBinding CurrentDateTime, TranslateFormat=TimeIs}/" />

Currency in XAML

public decimal Price { get; set; } = 123.45;
<!-- TotalPrice string resource: "Total Price is: {0:C}" -->
<Label Text="{localization:TranslateBinding Price, TranslateFormat=TotalPrice}" />

Translate collections in XAML

TranslateValue : (optional) Apply localization changes to a view model, e.g.

public IList<string> Fruits { get; set; } = new List<string> { "LBL_APPLES", "LBL_ORANGES" };
<CollectionView ItemsSource="{Binding Fruits}">
    <CollectionView.ItemTemplate>
        <DataTemplate>
            <Label Text="{localization:TranslateBinding . , TranslateValue=True}"/>
        <DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.