Skip to content

Commit

Permalink
Merge pull request #41 from cnbluefire/main
Browse files Browse the repository at this point in the history
Add support for pri files
  • Loading branch information
AndrewKeepCoding authored Apr 12, 2024
2 parents 0cd51fc + d8cfc79 commit fefc9a0
Show file tree
Hide file tree
Showing 7 changed files with 210 additions and 17 deletions.
4 changes: 3 additions & 1 deletion WinUI3Localizer.SampleApp/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,9 @@ private async Task InitializeLocalizer()
#endif

ILocalizer localizer = await new LocalizerBuilder()
.AddStringResourcesFolderForLanguageDictionaries(StringsFolderPath)
.AddPriResourcesForLanguageDictionaries(new[] { "en-US", "es-ES", "ja" } )
.AddPriResourcesForLanguageDictionaries(new[] { "en-US", "es-ES", "ja" }, "ErrorMessages")
//.AddStringResourcesFolderForLanguageDictionaries(StringsFolderPath)
//.SetLogger(Host.Services
// .GetRequiredService<ILoggerFactory>()
// .CreateLogger<Localizer>())
Expand Down
52 changes: 52 additions & 0 deletions WinUI3Localizer.Tests/PriResourceReaderTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using FluentAssertions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WinUI3Localizer.Tests;

public class PriResourceReaderTest
{
[Fact]
public void GetItems_ReturnsAllItems()
{
LanguageDictionary.Item[]? resourcesItems = null;
LanguageDictionary.Item[]? errorMessagesItems = null;

Thread? thread = new Thread(() =>
{
Microsoft.Windows.ApplicationModel.DynamicDependency.Bootstrap.Initialize(0x00010005);

string? priFile = Path.Combine(AppContext.BaseDirectory, "resources.pri");
PriResourceReaderFactory? factory = new();

PriResourceReader? reader = factory.GetPriResourceReader(priFile);

resourcesItems = reader.GetItems("en-US").ToArray();
errorMessagesItems = reader.GetItems("es-ES", "ErrorMessages").ToArray();

Microsoft.Windows.ApplicationModel.DynamicDependency.Bootstrap.Shutdown();
});

thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join();

LanguageDictionary.Item item1 = new LanguageDictionary.Item("ControlsPage_Button", "ContentProperty", "Click", "ControlsPage_Button.Content");
LanguageDictionary.Item item2 = new LanguageDictionary.Item("StylesPage_Top", "TextProperty", "Top", "StylesPage_Top.Text");
LanguageDictionary.Item item3 = new LanguageDictionary.Item("/ErrorMessages/ErrorMessageExample", "TextProperty", "Ejemplo de mensajes de error", "/ErrorMessages/ErrorMessageExample.Text");

resourcesItems.Should().HaveCount(55);
resourcesItems.Should().Contain(item1);
resourcesItems.Should().Contain(item2);
resourcesItems.Should().NotContain(item3);

errorMessagesItems.Should().HaveCount(1);
errorMessagesItems.Should().NotContain(item1);
errorMessagesItems.Should().NotContain(item2);
errorMessagesItems.Should().Contain(item3);
}

}
9 changes: 9 additions & 0 deletions WinUI3Localizer.Tests/WinUI3Localizer.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<Platforms>x86;x64;arm64</Platforms>
<RuntimeIdentifiers>win10-x86;win10-x64;win10-arm64</RuntimeIdentifiers>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.9.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.240404000" />
<PackageReference Include="Moq" Version="4.18.4" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
Expand All @@ -30,4 +33,10 @@
<Content Include="xunit.runner.json" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>

<ItemGroup>
<None Update="resources.pri">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
Binary file added WinUI3Localizer.Tests/resources.pri
Binary file not shown.
24 changes: 12 additions & 12 deletions WinUI3Localizer.sln
Original file line number Diff line number Diff line change
Expand Up @@ -46,20 +46,20 @@ Global
{C1E60FCC-8B43-4190-ADF1-A28B954F8DEB}.Release|x64.Build.0 = Release|Any CPU
{C1E60FCC-8B43-4190-ADF1-A28B954F8DEB}.Release|x86.ActiveCfg = Release|Any CPU
{C1E60FCC-8B43-4190-ADF1-A28B954F8DEB}.Release|x86.Build.0 = Release|Any CPU
{46D3D143-8BBF-4A74-960D-8CD34F35B58E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{46D3D143-8BBF-4A74-960D-8CD34F35B58E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{46D3D143-8BBF-4A74-960D-8CD34F35B58E}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{46D3D143-8BBF-4A74-960D-8CD34F35B58E}.Debug|ARM64.Build.0 = Debug|Any CPU
{46D3D143-8BBF-4A74-960D-8CD34F35B58E}.Debug|x64.ActiveCfg = Debug|Any CPU
{46D3D143-8BBF-4A74-960D-8CD34F35B58E}.Debug|x64.Build.0 = Debug|Any CPU
{46D3D143-8BBF-4A74-960D-8CD34F35B58E}.Debug|Any CPU.ActiveCfg = Debug|x64
{46D3D143-8BBF-4A74-960D-8CD34F35B58E}.Debug|Any CPU.Build.0 = Debug|x64
{46D3D143-8BBF-4A74-960D-8CD34F35B58E}.Debug|ARM64.ActiveCfg = Debug|arm64
{46D3D143-8BBF-4A74-960D-8CD34F35B58E}.Debug|ARM64.Build.0 = Debug|arm64
{46D3D143-8BBF-4A74-960D-8CD34F35B58E}.Debug|x64.ActiveCfg = Debug|x64
{46D3D143-8BBF-4A74-960D-8CD34F35B58E}.Debug|x64.Build.0 = Debug|x64
{46D3D143-8BBF-4A74-960D-8CD34F35B58E}.Debug|x86.ActiveCfg = Debug|Any CPU
{46D3D143-8BBF-4A74-960D-8CD34F35B58E}.Debug|x86.Build.0 = Debug|Any CPU
{46D3D143-8BBF-4A74-960D-8CD34F35B58E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{46D3D143-8BBF-4A74-960D-8CD34F35B58E}.Release|Any CPU.Build.0 = Release|Any CPU
{46D3D143-8BBF-4A74-960D-8CD34F35B58E}.Release|ARM64.ActiveCfg = Release|Any CPU
{46D3D143-8BBF-4A74-960D-8CD34F35B58E}.Release|ARM64.Build.0 = Release|Any CPU
{46D3D143-8BBF-4A74-960D-8CD34F35B58E}.Release|x64.ActiveCfg = Release|Any CPU
{46D3D143-8BBF-4A74-960D-8CD34F35B58E}.Release|x64.Build.0 = Release|Any CPU
{46D3D143-8BBF-4A74-960D-8CD34F35B58E}.Release|Any CPU.ActiveCfg = Release|x64
{46D3D143-8BBF-4A74-960D-8CD34F35B58E}.Release|Any CPU.Build.0 = Release|x64
{46D3D143-8BBF-4A74-960D-8CD34F35B58E}.Release|ARM64.ActiveCfg = Release|arm64
{46D3D143-8BBF-4A74-960D-8CD34F35B58E}.Release|ARM64.Build.0 = Release|arm64
{46D3D143-8BBF-4A74-960D-8CD34F35B58E}.Release|x64.ActiveCfg = Release|x64
{46D3D143-8BBF-4A74-960D-8CD34F35B58E}.Release|x64.Build.0 = Release|x64
{46D3D143-8BBF-4A74-960D-8CD34F35B58E}.Release|x86.ActiveCfg = Release|Any CPU
{46D3D143-8BBF-4A74-960D-8CD34F35B58E}.Release|x86.Build.0 = Release|Any CPU
{CF4A3EBB-18DA-4234-B0DB-3CD45AF4B054}.Debug|Any CPU.ActiveCfg = Debug|x64
Expand Down
41 changes: 37 additions & 4 deletions WinUI3Localizer/LocalizerBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using System.Xml;

Expand All @@ -27,6 +28,8 @@ private record StringResourceItems(string Language, IEnumerable<StringResourceIt

private ILogger? logger;

private PriResourceReaderFactory? priResourceReaderFactory;

public static bool IsLocalizerAlreadyBuilt => Localizer.Get() is Localizer;

public LocalizerBuilder SetDefaultStringResourcesFileName(string fileName)
Expand Down Expand Up @@ -86,6 +89,33 @@ public LocalizerBuilder AddStringResourcesFolderForLanguageDictionaries(
return this;
}

public LocalizerBuilder AddPriResourcesForLanguageDictionaries(
string[] languages,
string? subTreeName = null,
string? priFile = null)
{
this.builderActions.Add(() =>
{
if (this.priResourceReaderFactory == null)
{
this.priResourceReaderFactory = new();
}

for (int i = 0; i < languages.Length; i++)
{
PriResourceReader? reader = this.priResourceReaderFactory.GetPriResourceReader(priFile);

LanguageDictionary? dictionary = new(languages[i]);
foreach (LanguageDictionary.Item item in reader.GetItems(languages[i], subTreeName))
{
dictionary.AddItem(item);
}
this.languageDictionaries.Add(dictionary);
}
});
return this;
}

public LocalizerBuilder AddLanguageDictionary(LanguageDictionary dictionary)
{
this.builderActions.Add(() => this.languageDictionaries.Add(dictionary));
Expand Down Expand Up @@ -167,17 +197,20 @@ private static LanguageDictionary CreateLanguageDictionaryFromStringResourceItem
return dictionary;
}

private static LanguageDictionary.Item CreateLanguageDictionaryItem(StringResourceItem stringResourceItem)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static LanguageDictionary.Item CreateLanguageDictionaryItem(StringResourceItem stringResourceItem) =>
CreateLanguageDictionaryItem(stringResourceItem.Name, stringResourceItem.Value);

internal static LanguageDictionary.Item CreateLanguageDictionaryItem(string name, string value)
{
string name = stringResourceItem.Name;
(string Uid, string DependencyPropertyName) = name.IndexOf(".") is int firstSeparatorIndex && firstSeparatorIndex > 1
? (name[..firstSeparatorIndex], string.Concat(name.AsSpan(firstSeparatorIndex + 1), "Property"))
: (name, string.Empty);
return new LanguageDictionary.Item(
Uid,
DependencyPropertyName,
stringResourceItem.Value,
stringResourceItem.Name);
value,
name);
}

private static StringResourceItems? CreateStringResourceItemsFromResourcesFile(string sourceName, string filePath, string xPath = "//root/data")
Expand Down
97 changes: 97 additions & 0 deletions WinUI3Localizer/PriResourceReader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
using Microsoft.Windows.ApplicationModel.Resources;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WinUI3Localizer;
internal class PriResourceReader
{
private readonly ResourceManager resourceManager;

internal PriResourceReader(ResourceManager resourceManager)
{
this.resourceManager = resourceManager;
}

public IEnumerable<LanguageDictionary.Item> GetItems(string language, string subTreeName = "Resources")
{
if (string.IsNullOrEmpty(subTreeName) || subTreeName == "/")
{
subTreeName = "Resources";
}
else if (subTreeName.EndsWith('/'))
{
subTreeName = subTreeName[..^1];
}

ResourceMap resourceMap = this.resourceManager.MainResourceMap.TryGetSubtree(subTreeName);
if (resourceMap != null)
{
ResourceContext resourceContext = this.resourceManager.CreateResourceContext();
resourceContext.QualifierValues[KnownResourceQualifierName.Language] = language;

return GetItemsCore(resourceMap, subTreeName, resourceContext);
}

return Enumerable.Empty<LanguageDictionary.Item>();
}


private IEnumerable<LanguageDictionary.Item> GetItemsCore(ResourceMap resourceMap, string subTreeName, ResourceContext resourceContext)
{
bool isResourcesSubTree = string.Equals(subTreeName, "Resources", StringComparison.OrdinalIgnoreCase);
uint count = resourceMap.ResourceCount;

for (uint i = 0; i < count; i++)
{
(string key, ResourceCandidate? candidate) = resourceMap.GetValueByIndex(i, resourceContext);

if (candidate != null && candidate.Kind == ResourceCandidateKind.String)
{
key = key.Replace('/', '.');
if (!isResourcesSubTree)
{
key = $"/{subTreeName}/{key}";
}
yield return LocalizerBuilder.CreateLanguageDictionaryItem(key, candidate.ValueAsString);
}
}
}

}

internal class PriResourceReaderFactory
{
private readonly Dictionary<string, PriResourceReader> readers = new Dictionary<string, PriResourceReader>();

internal PriResourceReader GetPriResourceReader(string? priFile)
{
string? normalizedFilePath = string.Empty;

if (!string.IsNullOrEmpty(priFile))
{
normalizedFilePath = System.IO.Path.GetFullPath(priFile);
}

if (!this.readers.TryGetValue(normalizedFilePath, out PriResourceReader? reader))
{
ResourceManager manager;
if (string.IsNullOrEmpty(normalizedFilePath))
{
manager = new ResourceManager();
}
else
{
manager = new ResourceManager(normalizedFilePath);
}
reader = new PriResourceReader(manager);
this.readers[normalizedFilePath] = reader;
}

return reader;
}
}

0 comments on commit fefc9a0

Please sign in to comment.