Search code examples
c#.netresourcesdotnet-sdk

Generate localized string resources when building with dotnet sdk


I'm trying to convert an old-style Visual Studio 2015 .net framework 4.8 project to an sdk-style project in VS 2019. Works quite well so far, but I'm struggling with the following requirements:

  • We have localized text resources in restext format, like
    • \Resources\Strings\strings.en_US.restext
    • \Resources\Strings\strings.de_DE.restext
    • \Resources\Strings\strings.es_ES.restext
    • ...
  • We'd like to convert those restext files into
    • embedded resources
    • and corresponding strongly typed resources (aka .Designer.cs files)
  • We'd like those localized string resources to end up in the final .exe so that they are accessible at runtime
  • We'd like this to work both in VS and -- perhaps even more importantly -- when building with dotnet.exe on the command line

Althoug there seems to exist some support for resource handling and l10n with dotnet.exe (like the GenerateResource task, which, however, seems to have no support for the .restext format), I just can't figure out a way to accomplish this. (To me, the documentation available on this topic appears rather sparse and vague...)

Anyone any ideas how to set up a build satisfying the requirements described above?


Solution

  • Thanks to a lot of help on https://github.com/dotnet/sdk/issues/26337, I'm almost there:

    <Project Sdk="Microsoft.NET.Sdk">
    
        <PropertyGroup>
            <TargetFrameworks>net48</TargetFrameworks>
            <OutputType>WinExe</OutputType>
            <UseWPF>true</UseWPF>
                    ...
            <NeutralLanguage>en-US</NeutralLanguage>
        </PropertyGroup>
    
        <!-- Include output folder for generated strongly typed text resources -->
        <ItemGroup>
            <Folder Include="Properties" />
        </ItemGroup>
    
        <!-- Prevent default handling of text resource files -->
        <ItemGroup>
            <None Remove="Resources\Strings\*.restext" />
        </ItemGroup>
    
        <!-- Use a copy of en-US-specific text resource file for neutral-language resource generation -->
        <Target Name="CreateNeturalLanguageTextResource" BeforeTargets="BeforeBuild">
            <Message Text="Creating neutral language text resource" />
            <Copy SourceFiles="Resources\Strings\Strings.en-US.restext" DestinationFiles="Resources\Strings\Strings.restext" />
        </Target>
    
        <!-- Text resources handling -->
        <ItemGroup>
            <!-- Generate binary und strongly typed resource from neutral language text resource file -->
            <EmbeddedResource Include="Resources\Strings\Strings.restext">
                <DesignTime>True</DesignTime>
                <StronglyTypedLanguage>C#</StronglyTypedLanguage>
                <StronglyTypedClassName>%(Filename)</StronglyTypedClassName>
                <StronglyTypedNamespace>$(RootNamespace).Properties</StronglyTypedNamespace>
                <StronglyTypedFilename>$(MSBuildProjectDirectory)\Properties\%(Filename).cs</StronglyTypedFilename>
                <PublicClass>true</PublicClass>
            </EmbeddedResource>
            <!-- Generate language-specific resources (binary only) -->
            <EmbeddedResource Include="Resources\Strings\Strings.en-US.restext"/>
            <EmbeddedResource Include="Resources\Strings\Strings.de-DE.restext"/>
        </ItemGroup>
    
            ...
    
    </Project>
    

    A .cspoj file like this gives me a strongly typed C# resource file called Strings.cs, with the properties defined therein being readily accessbile while coding. When compiling the project -- be it from within Visual studio, be it using dotnet.exe -- I also get satellite assemblies at the expected locations (i. e. language-code subdirectories).

    Alas, when trying to run the compiled application, it crashes saying that no resource is found, neither for the current nor the neutral language (which should be en-US according to the project's tag.

    Any ideas why no resources are found at runtime?

    ---- Edit 2022-06-07:

    It's working now, thanks to another lead by the dotnet sdk people -- an additional

    <StronglyTypedManifestPrefix>$(RootNamespace).Resources.Strings</StronglyTypedManifestPrefix>
    

    inside the EmbeddedResource tag used for strongly typed resource generation does the trick.