Skip to content
This repository was archived by the owner on May 1, 2024. It is now read-only.

[Enhancement] Support for more than one resource manager #1047

Closed
wants to merge 56 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
5a113c7
Remove LRM from LocalizedString
maxkoshevoi Mar 6, 2021
4f30798
LRM doesn't store culture and RM
maxkoshevoi Mar 6, 2021
8c580c0
Update TranslateExtension.shared.cs
maxkoshevoi Mar 6, 2021
b32f431
LRM stores culture
maxkoshevoi Mar 7, 2021
918eeb9
Preserve indexer
maxkoshevoi Mar 7, 2021
6990f0c
Add RM null check
maxkoshevoi Mar 7, 2021
8adf268
Add examples
maxkoshevoi Mar 7, 2021
c8d8876
Fix tests
maxkoshevoi Mar 7, 2021
6c0e595
Fix build
maxkoshevoi Mar 7, 2021
6a30350
Fix build
maxkoshevoi Mar 7, 2021
bae1d89
Make ObservableResourceManager internal
maxkoshevoi Mar 7, 2021
dedc83a
Make DefaultResourceManager internal
maxkoshevoi Mar 7, 2021
a0db18c
Merge branch 'develop' into 832-multi-lrm
maxkoshevoi Mar 9, 2021
4803303
Fix nullability errors
maxkoshevoi Mar 9, 2021
d321e59
CurrentCulture is nullable
maxkoshevoi Mar 9, 2021
a9a1d2c
generator can return null
maxkoshevoi Mar 9, 2021
6e10716
Update SettingViewModel.cs
maxkoshevoi Mar 10, 2021
22e996b
Update App.xaml.cs
maxkoshevoi Mar 10, 2021
6f08261
Update error massage
maxkoshevoi Mar 10, 2021
b580fdd
CurrentCulture is not nullable
maxkoshevoi Mar 11, 2021
63633c5
Set initial culture
maxkoshevoi Mar 11, 2021
34107ae
Merge branch 'develop' into 832-multi-lrm
maxkoshevoi Mar 12, 2021
6306479
Commit to rerun CI
maxkoshevoi Mar 12, 2021
d24aeda
Revert "SupportedLanguages" demo changes
maxkoshevoi Mar 13, 2021
da3d75e
Merge branch 'develop' into 832-multi-lrm
maxkoshevoi Mar 13, 2021
fc3d3ba
Revert "CurrentCulture" null checking from demo app
maxkoshevoi Mar 13, 2021
75cfc8e
Merge branch 'develop' into 832-multi-lrm
TheCodeTraveler Mar 15, 2021
aa70c72
Merge branch 'develop' into 832-multi-lrm
jsuarezruiz Mar 17, 2021
304e289
Merge branch 'develop' into 832-multi-lrm
maxkoshevoi Mar 18, 2021
17ac9a7
Merge branch 'develop' into 832-multi-lrm
TheCodeTraveler Mar 18, 2021
ed6560c
Merge branch 'develop' into 832-multi-lrm
TheCodeTraveler Mar 18, 2021
43b4bc3
Merge branch 'develop' into 832-multi-lrm
jsuarezruiz Mar 19, 2021
575ccc9
Merge branch 'develop' into 832-multi-lrm
maxkoshevoi Mar 19, 2021
9e0a9c1
Fix tests
maxkoshevoi Mar 20, 2021
64a8fa6
Merge branch 'develop' into 832-multi-lrm
maxkoshevoi Mar 23, 2021
c260d77
Merge branch 'develop' into 832-multi-lrm
maxkoshevoi Mar 25, 2021
0a41fab
Re-add LocalizedString Constructor
TheCodeTraveler Apr 3, 2021
e57e9c1
Remove unnecessary field
TheCodeTraveler Apr 3, 2021
6b11fd4
Remove Obsolete Methods
TheCodeTraveler Apr 4, 2021
27780fa
Merge branch 'develop' into 832-multi-lrm
TheCodeTraveler Apr 5, 2021
3de4d3f
Merge branch 'develop' into 832-multi-lrm
TheCodeTraveler Apr 6, 2021
5197602
Revert "Remove unnecessary field"
maxkoshevoi Apr 6, 2021
cf15bce
Merge branch 'develop' into 832-multi-lrm
TheCodeTraveler Apr 8, 2021
9d11648
Merge branch 'develop' into 832-multi-lrm
pictos Apr 10, 2021
c43c931
Merge branch 'develop' into 832-multi-lrm
maxkoshevoi Apr 11, 2021
2cfb0e2
Merge branch 'develop' into 832-multi-lrm
maxkoshevoi Apr 13, 2021
929f0ab
Merge branch 'develop' into 832-multi-lrm
maxkoshevoi Apr 14, 2021
05c1e8d
Merge branch 'develop' into 832-multi-lrm
maxkoshevoi Apr 14, 2021
23d7ad5
Merge branch 'develop' into 832-multi-lrm
maxkoshevoi Apr 16, 2021
433b26d
Merge branch 'develop' into 832-multi-lrm
maxkoshevoi Apr 24, 2021
dd6d875
Merge branch 'develop' into 832-multi-lrm
maxkoshevoi May 1, 2021
b61f2c5
Merge branch 'develop' into 832-multi-lrm
maxkoshevoi May 7, 2021
c2dd09d
Merge branch 'develop' into 832-multi-lrm
jsuarezruiz May 10, 2021
8b1ecbb
Merge branch 'develop' into 832-multi-lrm
pictos Jun 16, 2021
d96fc4d
Merge branch 'develop' into 832-multi-lrm
TheCodeTraveler Jul 1, 2021
bf3874f
Merge branch 'develop' into 832-multi-lrm
maxkoshevoi Nov 3, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions samples/XCT.Sample/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ public App()
On<Windows>().SetImageDirectory("Assets");

LocalizationResourceManager.Current.PropertyChanged += (sender, e) => AppResources.Culture = LocalizationResourceManager.Current.CurrentCulture;
LocalizationResourceManager.Current.Init(AppResources.ResourceManager);
LocalizationResourceManager.Current.CurrentCulture = new CultureInfo("en");
LocalizationResourceManager.Current.Init(AppResources.ResourceManager, new CultureInfo("en"));

InitializeComponent();
MainPage = new BaseNavigationPage(new WelcomePage());
Expand Down
6 changes: 5 additions & 1 deletion samples/XCT.Sample/Pages/SettingPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
xmlns:pages="clr-namespace:Xamarin.CommunityToolkit.Sample.Pages"
xmlns:vm="clr-namespace:Xamarin.CommunityToolkit.Sample.ViewModels"
xmlns:resx="clr-namespace:Xamarin.CommunityToolkit.Sample.Resx"
x:Class="Xamarin.CommunityToolkit.Sample.Pages.SettingPage"
x:DataType="vm:SettingViewModel">

Expand All @@ -18,13 +19,16 @@
<StackLayout Padding="{StaticResource ContentPadding}"
VerticalOptions="Center"
Spacing="10">

<!--Example of using default ResourceManager-->
<Label Text="{xct:Translate ChangeLanguage}"/>

<Picker ItemsSource="{Binding SupportedLanguages}"
ItemDisplayBinding="{Binding Name}"
SelectedItem="{Binding SelectedLanguage}"/>

<Button Text="{xct:Translate Save}"
<!--Example of explicitly passing ResourceManager-->
<Button Text="{xct:Translate Save, ResourceManager={x:Static resx:AppResources.ResourceManager}}"
Command="{Binding ChangeLanguageCommand, Mode=OneTime}"/>

<Label HorizontalTextAlignment="Center" Text="{Binding AppVersion.Localized}"/>
Expand Down
4 changes: 2 additions & 2 deletions samples/XCT.Sample/ViewModels/SettingViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ void LoadLanguages()
{
SupportedLanguages = new List<Language>()
{
{ new Language(AppResources.English, "en") },
{ new Language(AppResources.Spanish, "es") }
new Language(AppResources.English, "en"),
new Language(AppResources.Spanish, "es")
};
SelectedLanguage = SupportedLanguages.FirstOrDefault(pro => pro.CI == LocalizationResourceManager.Current.CurrentCulture.TwoLetterISOLanguageName);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,54 +10,19 @@ namespace Xamarin.CommunityToolkit.UnitTests.Helpers.LocalizationResourceManager
[NonParallelizable]
public class LocalizationResourceManagerTests
{
ResourceManager? resourceManager;
CultureInfo? initialCulture;
LocalizationResourceManager? localizationManager;
readonly ResourceManager resourceManager = new MockResourceManager();
readonly CultureInfo initialCulture = CultureInfo.InvariantCulture;
readonly LocalizationResourceManager localizationManager = LocalizationResourceManager.Current;

[SetUp]
public void Setup()
{
resourceManager = new MockResourceManager();
initialCulture = CultureInfo.InvariantCulture;
localizationManager = LocalizationResourceManager.Current;

localizationManager.Init(resourceManager, initialCulture);
}

[Test]
public void LocalizationResourceManager_GetCulture_Equal_Indexer()
{
_ = localizationManager ?? throw new NullReferenceException();
_ = resourceManager ?? throw new NullReferenceException();

// Arrange
var testString = "test";
var culture2 = new CultureInfo("en");

// Act
var responceIndexerCulture1 = localizationManager[testString];
var responceGetValueCulture1 = localizationManager.GetValue(testString);
var responceResourceManagerCulture1 = resourceManager.GetString(testString, initialCulture);

localizationManager.CurrentCulture = culture2;
var responceIndexerCulture2 = localizationManager[testString];
var responceGetValueCulture2 = localizationManager.GetValue(testString);
var responceResourceManagerCulture2 = resourceManager.GetString(testString, culture2);

// Assert
Assert.AreEqual(responceResourceManagerCulture1, responceIndexerCulture1);
Assert.AreEqual(responceResourceManagerCulture1, responceGetValueCulture1);
Assert.AreEqual(responceResourceManagerCulture2, responceIndexerCulture2);
Assert.AreEqual(responceResourceManagerCulture2, responceGetValueCulture2);
}

[Test]
public void LocalizationResourceManager_PropertyChanged_Triggered()
{
_ = initialCulture ?? throw new NullReferenceException();
_ = resourceManager ?? throw new NullReferenceException();
_ = localizationManager ?? throw new NullReferenceException();

// Arrange
var culture2 = new CultureInfo("en");
CultureInfo? changedCulture = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,33 +10,25 @@ namespace Xamarin.CommunityToolkit.UnitTests.Helpers.LocalizedStringTests
[NonParallelizable]
public class LocalizedStringTests
{
CultureInfo? initialCulture;
ResourceManager? resourceManager;
LocalizationResourceManager? localizationManager;
readonly ResourceManager resourceManager = new MockResourceManager();
readonly CultureInfo initialCulture = CultureInfo.InvariantCulture;
readonly LocalizationResourceManager localizationManager = LocalizationResourceManager.Current;

LocalizedString? localizedString;

[SetUp]
public void Setup()
{
resourceManager = new MockResourceManager();
initialCulture = CultureInfo.InvariantCulture;
localizationManager = LocalizationResourceManager.Current;

localizationManager.Init(resourceManager, initialCulture);
}

[Test]
public void LocalizedStringTests_Localized_ValidImplementation()
{
_ = initialCulture ?? throw new NullReferenceException();
_ = resourceManager ?? throw new NullReferenceException();
_ = localizationManager ?? throw new NullReferenceException();

// Arrange
var testString = "test";
var culture2 = new CultureInfo("en");
localizedString = new LocalizedString(localizationManager, () => localizationManager[testString]);
localizedString = new LocalizedString(() => resourceManager.GetString(testString, localizationManager.CurrentCulture));

string? responceOnCultureChanged = null;
localizedString.PropertyChanged += (sender, args) => responceOnCultureChanged = localizedString.Localized;
Expand Down Expand Up @@ -71,12 +63,10 @@ public void LocalizedStringTests_ImplicitConversion_ValidImplementation()
[Test]
public void LocalizedStringTests_WeekSubscribe_ValidImplementation()
{
_ = localizationManager ?? throw new NullReferenceException();

// Arrange
var isTrigered = false;
var culture2 = new CultureInfo("en");
localizedString = new LocalizedString(localizationManager, () => string.Empty);
localizedString = new LocalizedString(() => string.Empty);
localizedString.PropertyChanged += (_, __) => isTrigered = true;

// Act
Expand All @@ -93,10 +83,6 @@ public void LocalizedStringTests_WeekSubscribe_ValidImplementation()
[Test]
public void LocalizedStringTests_Disposed_IfNoReferences()
{
_ = initialCulture ?? throw new NullReferenceException();
_ = resourceManager ?? throw new NullReferenceException();
_ = localizationManager ?? throw new NullReferenceException();

// Arrange
var testString = "test";
SetLocalizedString();
Expand All @@ -111,7 +97,7 @@ public void LocalizedStringTests_Disposed_IfNoReferences()

void SetLocalizedString()
{
localizedString = new LocalizedString(localizationManager, () => localizationManager[testString]);
localizedString = new LocalizedString(() => resourceManager.GetString(testString, localizationManager.CurrentCulture));
}
}
#endif
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
using System;
using System.Resources;
using Xamarin.CommunityToolkit.Helpers;
using Xamarin.CommunityToolkit.ObjectModel;
using Xamarin.Forms;
using Xamarin.Forms.Internals;
using Xamarin.Forms.Xaml;

namespace Xamarin.CommunityToolkit.Extensions
Expand All @@ -12,27 +15,48 @@ public class TranslateExtension : IMarkupExtension<BindingBase>

public string? StringFormat { get; set; }

public ResourceManager? ResourceManager { get; set; }

object IMarkupExtension.ProvideValue(IServiceProvider serviceProvider) => ProvideValue(serviceProvider);

public BindingBase ProvideValue(IServiceProvider serviceProvider)
{
#if NETSTANDARD1_0
throw new NotSupportedException("Translate XAML MarkupExtension is not supported on .NET Standard 1.0");
#else
#region Required work-around to prevent linker from removing the implementation
if (DateTime.Now.Ticks < 0)
_ = LocalizationResourceManager.Current[Text];
#endregion
ResourceManager ??= LocalizationResourceManager.Current.DefaultResourceManager;

if (ResourceManager == null)
{
throw new ArgumentNullException(nameof(ResourceManager), $"Call LocalizationResourceManager.Current.Init(defaultResourceManager) first or provide {nameof(ResourceManager)} argument. Text: {Text}");
}

var binding = new Binding
{
Mode = BindingMode.OneWay,
Path = $"[{Text}]",
Source = LocalizationResourceManager.Current,
Source = new ObservableResourceManager(ResourceManager),
StringFormat = StringFormat
};
return binding;
#endif
}

#if !NETSTANDARD1_0
class ObservableResourceManager : ObservableObject
{
readonly ResourceManager resourceManager;

public ObservableResourceManager(ResourceManager resourceManager)
{
this.resourceManager = resourceManager;
LocalizationResourceManager.Current.PropertyChanged += (sender, e) => OnPropertyChanged(null);
}

[Preserve(Conditional = true)]
public string? this[string name] =>
resourceManager.GetString(name, LocalizationResourceManager.Current.CurrentCulture);
}
#endif
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,44 +14,32 @@ public class LocalizationResourceManager : ObservableObject

public static LocalizationResourceManager Current => currentHolder.Value;

ResourceManager? resourceManager;
CultureInfo currentCulture = Thread.CurrentThread.CurrentUICulture;
CultureInfo currentCulture =
CultureInfo.DefaultThreadCurrentUICulture ??
CultureInfo.DefaultThreadCurrentCulture ??
Thread.CurrentThread.CurrentUICulture;

LocalizationResourceManager()
{
}

public void Init(ResourceManager resource) => resourceManager = resource;
public void Init(ResourceManager defaultResourceManager) => DefaultResourceManager = defaultResourceManager;

public void Init(ResourceManager resource, CultureInfo initialCulture)
{
CurrentCulture = initialCulture;
Init(resource);
}
public void Init(CultureInfo initialCulture) => CurrentCulture = initialCulture;

public string GetValue(string text)
public void Init(ResourceManager defaultResourceManager, CultureInfo initialCulture)
{
if (resourceManager == null)
throw new InvalidOperationException($"Must call {nameof(LocalizationResourceManager)}.{nameof(Init)} first");

return resourceManager.GetString(text, CurrentCulture) ?? throw new NullReferenceException($"{nameof(text)}: {text} not found");
Init(defaultResourceManager);
Init(initialCulture);
}

public string this[string text] => GetValue(text);

[Obsolete("Please, use " + nameof(CurrentCulture) + " to set culture")]
[EditorBrowsable(EditorBrowsableState.Never)]
public void SetCulture(CultureInfo language) => CurrentCulture = language;

public CultureInfo CurrentCulture
{
get => currentCulture;
set => SetProperty(ref currentCulture, value, null);
set => SetProperty(ref currentCulture, value);
}

[Obsolete("This method is no longer needed with new implementation of " + nameof(LocalizationResourceManager) + ". Please, remove all references to it.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public void Invalidate() => OnPropertyChanged(null);
internal ResourceManager? DefaultResourceManager { get; private set; }
}
#endif
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,28 @@ namespace Xamarin.CommunityToolkit.Helpers
#if !NETSTANDARD1_0
public class LocalizedString : ObservableObject
{
readonly Func<string> generator;
readonly Func<string?> generator;

public LocalizedString(Func<string> generator)
: this(LocalizationResourceManager.Current, generator)
{
}

public LocalizedString(LocalizationResourceManager localizationManager, Func<string> generator)
public LocalizedString(Func<string?> generator)
{
this.generator = generator;

// This instance will be unsubscribed and GCed if no one references it
// since LocalizationResourceManager uses WeekEventManger
localizationManager.PropertyChanged += (sender, e) => OnPropertyChanged(null);
LocalizationResourceManager.Current.PropertyChanged += (sender, e) => OnPropertyChanged(nameof(Localized));
}

[Obsolete("Use `LocalizedString(Func<string?> generator)` instead")]
public LocalizedString(LocalizationResourceManager localizationManager, Func<string> generator)
: this(generator)
{
}

[Preserve(Conditional = true)]
public string Localized => generator();
public string? Localized => generator();

[Preserve(Conditional = true)]
public static implicit operator LocalizedString(Func<string> func) => new LocalizedString(func);
public static implicit operator LocalizedString(Func<string?> func) => new LocalizedString(func);
}
#endif
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
using System.Collections.Generic;

#nullable enable

namespace Xamarin.CommunityToolkit.ObjectModel
{
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
using System.Runtime.CompilerServices;
using Xamarin.CommunityToolkit.Helpers;

#nullable enable

namespace Xamarin.CommunityToolkit.ObjectModel
{
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
using System.Collections.Specialized;
using System.ComponentModel;

#nullable enable

namespace Xamarin.CommunityToolkit.ObjectModel
{
/// <summary>
Expand Down