WinUI3Localizer icon indicating copy to clipboard operation
WinUI3Localizer copied to clipboard

Error building the localizer

Open LluisV opened this issue 1 year ago • 9 comments

Hey, a client sent me this screenshot, which corresponds to this code snippet. I've been trying to fix it for a couple of hours, but I can't figure out what the problem is.

image


public App()
{
    Instance = this;
    this.InitializeComponent();
    InitializeLocalizerAsync();
    Settings.LoadConfig();
}

private async void InitializeLocalizerAsync()
{
    await InitializeLocalizer();
}

public async Task InitializeLocalizer()
{
    try
    {
        // Initialize a "Strings" folder in the executables folder.
        var StringsFolderPath = System.IO.Path.Combine(AppContext.BaseDirectory, "Strings");

        // Ensure the folder exists
        if (!Directory.Exists(StringsFolderPath))
        {
            MessageBox.Show($"ERROR: Strings folder does not exist at path: {StringsFolderPath}");
            return;
        }

        StorageFolder stringsFolder = await StorageFolder.GetFolderFromPathAsync(StringsFolderPath);

        var currentLanguage = Settings.GetCurrentLanguage();
        if (string.IsNullOrWhiteSpace(currentLanguage))
        {
            MessageBox.Show("Invalid current language specified.");
            currentLanguage = "en-US";
        }

        ILocalizer localizer = await new LocalizerBuilder()
            .AddStringResourcesFolderForLanguageDictionaries(StringsFolderPath)
            .SetOptions(options =>
            {
                options.DefaultLanguage = currentLanguage;
            })
            .Build();

        try
        {
            var languageDictionary = localizer.GetCurrentLanguageDictionary();
            if (languageDictionary == null)
            {
                MessageBox.Show("ERROR: Language dictionary is null.");
                return;
            }

            language_items = languageDictionary.GetItems().ToList();
        }
        catch (Exception ex)
        {
            MessageBox.Show("ERROR 5.1: " + ex.Message + "\nDetails: " + ex.StackTrace);
        }

    }
    catch (Exception ex)
    {
        MessageBox.Show($"ERROR 5: {ex.Message}\nDetails: {ex.StackTrace}");
    }
}
  • We know that the directory exists because that error message is not displayed.
  • We know that the try catch block corresponding to the languageDictionary check is not executed, meaning that the error is before that.
  • I have tried disabling the exceptions produced by AddStringResourcesFolderForLanguageDictionaries, but then of course I don't get the strings.

Do you know where the error could come from?

Thank you in advance for your time.

LluisV avatar Dec 10 '24 11:12 LluisV

Hi @LluisV The exception is coming from CreateLanguageDictionaryItem, so it seems that there is something wrong in the *.resw file. 🤔

AndrewKeepCoding avatar Dec 10 '24 13:12 AndrewKeepCoding

@AndrewKeepCoding

Thank you for your quick response!

It seems that there is something wrong in the *.resw file. 🤔

I have asked them to send me a copy of their .resw file. It is identical to the one I have.
It's strange because the app has thousands of users and this is the first one this has happened to. They told me that they reinstalled the app and the error persists.

LluisV avatar Dec 10 '24 13:12 LluisV

The only way that I can reproduce this exception message is by setting a negative value in firstSeparatorIndex before AsSpan is called. But I can't come up with a case that firstSeparatorIndex could be less than 0 or larger than the text length.

internal static LanguageDictionary.Item CreateLanguageDictionaryItem(string name, string value)
{
    (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,
        value,
        name);
}
// Exceptions:
//   T:System.ArgumentNullException:
//     text is null.
//
//   T:System.ArgumentOutOfRangeException:
//     start is less than 0 or greater than text.Length.
public static ReadOnlySpan<char> AsSpan(this string? text, int start);

AndrewKeepCoding avatar Dec 10 '24 14:12 AndrewKeepCoding

I supposed this used to work, right? Can your user try installing the app on another system?

AndrewKeepCoding avatar Dec 10 '24 14:12 AndrewKeepCoding

@AndrewKeepCoding Yes, the app has thousands of users (Windows x64 only), and this is the first one to report this error. Unfortunately he says he doesn't have access to another system.

LluisV avatar Dec 10 '24 14:12 LluisV

Is the exception gone if you pass an *.resw file with no string resources? If so, might take time but can you try reducing the strings by chunks to narrow it down which string is causing the issue?

AndrewKeepCoding avatar Dec 10 '24 14:12 AndrewKeepCoding

@AndrewKeepCoding I just realized that I was using a 1.x version of the library. Is it possible that this bug was fixed in later versions? I see that the code for CreateLanguageDictionaryItem has changed. The problem is that in order to upgrade to version 2.x I need to upgrade the Windows SDK version, which introduces a bug in the materials that use the acrylic backdrop (you know, WinUI 3 stuff). Anyway, I'll keep you updated as soon as I get more info. Thanks for your help!

Edit: still happening in version 2.2

LluisV avatar Dec 11 '24 16:12 LluisV

Update: a Resources.resw with just 1 string still fails.

LluisV avatar Dec 11 '24 19:12 LluisV

This seems to be a very rare case, and I have no idea what might be causing this issue. If you are up to it, I guess it's faster if you just clone the repo, edit the code (add some logging or throw exceptions), create a test package and locally install it to your app. I can help you out with the packaging process if you are not familiar with it.

AndrewKeepCoding avatar Dec 11 '24 23:12 AndrewKeepCoding

@AndrewKeepCoding I've finally found the pattern that triggers this bug. Yesterday, another client reported the same error to me, and when he sent me a video showing it, I noticed a detail that matches both clients who have had this problem: their calendar showed the year 2568, which means their Windows language is Thai. I installed the Thai language pack, and that is indeed the trigger for the bug. Could you please run the same test on your side? Just to make sure it's indeed a bug in this library.

LluisV avatar May 19 '25 17:05 LluisV

I installed the Thai language package, set it as the Windows display language, and ran the WinUI3LocalizerSample app, but everything seems to be working as expected. What could I be missing?

AndrewKeepCoding avatar May 20 '25 01:05 AndrewKeepCoding

In the latest versions, no exception is thrown when building the localizer while using the Thai language (I'm using an older version), but the strings are not displayed in the sample app for the latest version either.

Notice how the content of the UI elements is empty when setting the Windows language to Thai.

Image

This does not happen when using Spanish or English.

Image

LluisV avatar May 20 '25 08:05 LluisV

You just switched the Windows display language here, and a re-logged in right?

Image

AndrewKeepCoding avatar May 20 '25 08:05 AndrewKeepCoding

Yes

LluisV avatar May 20 '25 08:05 LluisV

I'm not sure why but I can't reproduce the issue on my side. Can you try debugging the WinUI3Localizer sample app and see what is failing?

It should around the CreateLanguageDictionaryItem method.

AndrewKeepCoding avatar May 20 '25 09:05 AndrewKeepCoding

Sure. Could you please give me some guidance on how to debug it? Visual Studio tells me I'm trying to debug a Release version of WinUI3Localizer.dll, so breakpoints aren't working. I'm running WinUI3Localiser.SampleApp x64 in Debug mode.

LluisV avatar May 20 '25 09:05 LluisV

Follow these steps.

  1. Clone the repo.
  2. On the WinUI3LocalizerSampleApp project:
    • Remove the WinUI3Localizer NuGet package.
    • Add the WinUI3Localizer project as reference.

AndrewKeepCoding avatar May 20 '25 11:05 AndrewKeepCoding

Thanks. In the CreateLanguageDictionaryItem method, the values 'Uid', 'value' and 'name' seem correct. The 'DependencyPropertyName' is always an empty string. Is that normal? The dictionaries appear to be generated correctly in LocalizerBuilder.Buid(). All three dictionaries (en, es, ja) have both keys and values.

LluisV avatar May 20 '25 11:05 LluisV

Update: I have expanded the method to be able to debug correctly and it seems that the problem lies in the fact that hasSeparator is always false because name.IndexOf(".") always returns the value 0 even though a priori the strings do not have the '.' character at the beginning.

Video: https://gyazo.com/6200848bfd182a845b92207280d96089

LluisV avatar May 20 '25 12:05 LluisV

Update 2: using name.IndexOf(".", StringComparison.Ordinal) instead of just name.IndexOf(".") fixes the problem.

LluisV avatar May 20 '25 12:05 LluisV

The fix is now available on v2.3.0. Thanks for your contribution!

AndrewKeepCoding avatar May 21 '25 13:05 AndrewKeepCoding