5

I've implemented localization in ASP.NET Core according to this: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/localization

I have a common library project (MyProject.Common) where I keep the resource file (since it is used by multiple ASP.NET Core applications).

So I have a .resx file under MyProject.Common\Resources\Localization\SharedResources.sv.resx:

<?xml version="1.0" encoding="utf-8"?>
<root>
  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
    <xsd:element name="root" msdata:IsDataSet="true">
      <xsd:complexType>
        <xsd:choice maxOccurs="unbounded">
          <xsd:element name="metadata">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" />
              </xsd:sequence>
              <xsd:attribute name="name" use="required" type="xsd:string" />
              <xsd:attribute name="type" type="xsd:string" />
              <xsd:attribute name="mimetype" type="xsd:string" />
              <xsd:attribute ref="xml:space" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="assembly">
            <xsd:complexType>
              <xsd:attribute name="alias" type="xsd:string" />
              <xsd:attribute name="name" type="xsd:string" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="data">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
              <xsd:attribute ref="xml:space" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="resheader">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" use="required" />
            </xsd:complexType>
          </xsd:element>
        </xsd:choice>
      </xsd:complexType>
    </xsd:element>
  </xsd:schema>
  <resheader name="resmimetype">
    <value>text/microsoft-resx</value>
  </resheader>
  <resheader name="version">
    <value>2.0</value>
  </resheader>
  <resheader name="reader">
    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <resheader name="writer">
    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <data name="English string" xml:space="preserve">
    <value>Swedish string</value>
  </data>
</root>

And I have an empty .cs file under MyProject.Common\Localization\SharedResources.cs that looks like this:

namespace MyProject.Common.Localization
{
    /// <summary>
    /// This is just a placeholder so that we can have all our resources in the same .resx file
    /// </summary>
    public class SharedResources
    {
    }
}

I have a class called Localizer that I use to get translated strings:

using System;
using Microsoft.Extensions.Localization;

namespace MyProject.Common.Localization
{
    public class Localizer
    {
        private readonly IStringLocalizer _localizer;

        public Localizer(IStringLocalizer<SharedResources> localizer)
        {
            _localizer = localizer;
        }

        public virtual LocalizedString this[string name]
        {
            get
            {
                if (name == null)
                {
                    throw new ArgumentNullException(nameof(name));
                }

                return _localizer[name];
            }
        }

        public virtual LocalizedString this[string name, params object[] arguments]
        {
            get
            {
                if (name == null)
                {
                    throw new ArgumentNullException(nameof(name));
                }

                return _localizer[name, arguments];
            }
        }
    }
}

And it is used liked this (will return swedish translation if culture of the request is swedish):

_localizer["English string"];

In Startup.cs for each of the ASP.NET Core projects I configure localization like this:

services
    // Add the localization services to the services container
    .AddLocalization(options => options.ResourcesPath = "Resources")

    // Configure supported cultures and localization options
    .Configure<RequestLocalizationOptions>(options =>
    {
        var supportedCultures = new[]
        {
            new CultureInfo("en"),
            new CultureInfo("sv")
        };

        // State what the default culture for your application is. This will be used if no specific culture
        // can be determined for a given request.
        options.DefaultRequestCulture = new RequestCulture(culture: "en", uiCulture: "en");

        // You must explicitly state which cultures your application supports.
        // These are the cultures the app supports for formatting numbers, dates, etc.
        options.SupportedCultures = supportedCultures;

        // These are the cultures the app supports for UI strings, i.e. we have localized resources for.
        options.SupportedUICultures = supportedCultures;
    });

I'm trying to figure out how to reuse this in a (.NET Core) console application? If I try to use my custom Localizer via dependency injection, I get: 'Unable to resolve service for type 'Microsoft.AspNetCore.Hosting.IHostingEnvironment' while attempting to activate 'Microsoft.Extensions.Localization.ResourceManagerStringLocalizerFactory. So this is because Microsoft.Extensions.Localization is dependent on ASP.NET Core, which doesn't exist in my console project.

How can I use the same .resx file in my console project?

1 Answer 1

3

I know I'm a bit late to the party here but you should be able to access your translations directly. Of course in a console app there is no such concept as request localization, so you'll need to manually handle culture lookup/storage/whatever, but once you know the culture it is possible to access your translations like this (no dependency injection required!):

var german = new System.Globalization.CultureInfo("de-DE");
var english = new System.Globalization.CultureInfo("en-AU");

// result "Guten tag"
var greeting1 = Shared.Resources.Lang.ResourceManager.GetString("GREETING", german); 

// result "Gidday mate"
var greeting2 = Shared.Resources.Lang.ResourceManager.GetString("GREETING", english);

You can probably figure out a nice way to handle language codes and resource tokens, but this should be enough to do your translations common resource files.

NOTE: this works in .Net Core 2.0, haven't tested in other versions.

Sign up to request clarification or add additional context in comments.

1 Comment

I'm glad you're late to the party ;). I'm trying to do exactly that, but without success. I created two ResX files, one neutral the other culture-specific, but it I always get the neutral value. Would that sound familiar to you? Details here: github.com/dotnet/core/issues/1036

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.