Friday 13 September 2019

Multi language with resource file in asp net core 2.0 (API)

Step 1 : Create Resource folder
Step 2 : SharedResources.cs
namespace PillarCore.Api
{
    public class SharedResources
    {
    }
}
Step 3 : Create SharedResources.en-US.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="Msg-required" xml:space="preserve">
    <value>Api call done</value>
  </data>
   <data name="model-user-required" xml:space="preserve">
    <value>User Name is required.</value>
  </data>

</root>

Step 4 : create SharedResources.ja-JP.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="Msg-required" xml:space="preserve">
    <value>API呼び出し完了</value>
  </data>
   <data name="model-user-required" xml:space="preserve">
    <value>ユーザー名が必要です。</value>
  </data>

</root>
Step 5 : create ResourceKeys.cs
  public static class ResourceKeys
    {
     

        public const string modeluserrequired= "model-user-required";
        public const string Msg = "Msg-required";
    }
Step 6 : create ValidateModelStateAttribute.cs
namespace PillarCore.Api.Infrastructure
{
    #pragma warning disable
    public class ValidateModelStateAttribute : ActionFilterAttribute
    {
        private readonly IStringLocalizer _localizer;
        public ValidateModelStateAttribute(IStringLocalizer localizer)
        {
             _localizer = localizer;
        }
        public override void OnActionExecuting(ActionExecutingContext context)
        {
            if (!context.ModelState.IsValid)
            {
                string _message = string.Join(",", context.ModelState.Values
                                               .SelectMany(x => x.Errors)
                                               .Select(x => _localizer[x.ErrorMessage]).Distinct());
             
                                             
                context.Result = new ObjectResult(JsonResponse(1,_message));
            }
        }

        public JsonResponse JsonResponse(int StatusCode, string Message, object Data = null) => new JsonResponse {
            StatusCode = StatusCode, Message = Message, Data = Data ?? new object()
        };
    }
}
Step 7 : ExceptionAttributeFilter.cs //Custome error Exception

namespace PillarCore.Api.Infrastructure
{
    #pragma warning disable
    public class ExceptionAttributeFilter : ExceptionFilterAttribute
    {
        public override void OnException(ExceptionContext context)
        {
            var controllerActionDescriptor = context.ActionDescriptor as ControllerActionDescriptor;
            Common.SetLog(context?.Exception, (controllerActionDescriptor?.ActionName ?? ""));
            context.Result = new ObjectResult(JsonResponse(Status.Error, StatusMessage.Error));
            base.OnException(context);
        }
        public JsonResponse JsonResponse(int StatusCode, string Message, object Data = null) => new JsonResponse { StatusCode = StatusCode, Message = Message, Data = Data ?? new object() };
 
    }
}
Step 8 : create model class
public class UserModel
    {
        [Required(ErrorMessage = ResourceKeys.modeluserrequired)]
        public string user{ get; set; }
}
Step 9 : Login method in API
[AllowAnonymous]
        [HttpPost("LoginUser")]
        public async Task<JsonResponse> LoginUser([FromBody]UserModel value)
        {
return JsonResponse("success", _localizer[ResourceKeys.Msg].Value);
}
Step 10 : change in Startup file

  private const string defaultResourcePath = "Resources";///Resource folder
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
            DefaultCulture = Configuration["Configuration:DefaultCulture"] ?? "ja-JP";///Resource defualt language set return Japanese you can set en-US means return English
        }
public void ConfigureServices(IServiceCollection services)
        {
RegisterLocalizationService(services);
  var supportedCultures = GetSupportedCultures();
  services.AddMvc(options =>
            {
                    options.Filters.Add(typeof(ValidateModelStateAttribute)); //Set Dynamic validation for invalied argument
                    options.Filters.Add(typeof(ExceptionAttributeFilter)); ///Set Custome error Exception

            }).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);//.AddJsonOptions(opt => opt.SerializerSettings.ContractResolver = new DefaultContractResolver());
            services.AddMvc().AddJsonOptions(options => { options.SerializerSettings.ReferenceLoopHandling =
                Newtonsoft.Json.ReferenceLoopHandling.Ignore;
            });
           
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {         
     
            var supportedCultures = GetSupportedCultures();
            app.UseRequestLocalization(new RequestLocalizationOptions()
            {
                DefaultRequestCulture = new RequestCulture(DefaultCulture),
                SupportedCultures = supportedCultures,
                SupportedUICultures = supportedCultures
            });
         
            app.UseMvc();
        }
  private void RegisterLocalizationService(IServiceCollection services)
        {
            services.AddLocalization(options => options.ResourcesPath = defaultResourcePath);
            var sharedResourceType = typeof(SharedResources);
            services.AddSingleton(x => x.GetService<IStringLocalizerFactory>().Create(sharedResourceType));
        }
        private List<CultureInfo> GetSupportedCultures() 
        {
            return new List<CultureInfo>
            {
               new CultureInfo(DefaultCulture)
            };
        }

SqlDataBaseLibrary

using System; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using AOS.Repository.Infrastructure; using S...