खोज…


JSON भाषा संसाधनों का उपयोग करके स्थानीयकरण

ASP.NET Core में कई अलग-अलग तरीके हैं जिनसे हम अपने ऐप को लोकलाइज़ / ग्लोबल कर सकते हैं। ऐसा तरीका चुनना महत्वपूर्ण है जो आपकी आवश्यकताओं के अनुरूप हो। इस उदाहरण में आप देखेंगे कि हम एक बहुभाषी ASP.NET कोर ऐप कैसे बना सकते हैं जो .json फाइलों से भाषा के विशिष्ट तार पढ़ता है और ऐप के सभी अनुभागों में स्थानीयकरण प्रदान करने के साथ-साथ उच्च प्रदर्शन बनाए रखने के लिए उन्हें मेमोरी में संग्रहीत करता है।

जिस तरह से हम करते हैं वह Microsoft.EntityFrameworkCore.InMemory पैकेज का उपयोग करके है।

टिप्पणियाँ:

  1. इस परियोजना का नाम स्थान DigitalShop जिसे आप अपनी परियोजनाओं के नामस्थान में बदल सकते हैं
  2. एक नई परियोजना बनाने पर विचार करें ताकि आप अजीब त्रुटियों में न चलें
  3. किसी भी तरह से यह उदाहरण सर्वोत्तम प्रथाओं को दिखाता है, इसलिए यदि आपको लगता है कि इसमें सुधार किया जा सकता है तो कृपया इसे संपादित करें

शुरू करने के लिए कृपया निम्नलिखित पैकेज को project.json में मौजूदा dependencies अनुभाग में जोड़ें। project.json फ़ाइल:

"Microsoft.EntityFrameworkCore": "1.0.0",
"Microsoft.EntityFrameworkCore.SqlServer": "1.0.0",
"Microsoft.EntityFrameworkCore.InMemory": "1.0.0"

अब चलो की जगह जाने Startup.cs साथ फ़ाइल: ( using के रूप में वे बाद में आसानी से जोड़ा जा सकता है बयान निकाल दिए जाते हैं)

Startup.cs

namespace DigitalShop
{
    public class Startup
    {
        public static string UiCulture;
        public static string CultureDirection;
        public static IStringLocalizer _e; // This is how we access language strings

        public static IConfiguration LocalConfig;

        public Startup(IHostingEnvironment env)
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) // this is where we store apps configuration including language
                .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
                .AddEnvironmentVariables();

            Configuration = builder.Build();
            LocalConfig = Configuration;
        }

        public IConfigurationRoot Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc().AddViewLocalization().AddDataAnnotationsLocalization();

            // IoC Container
            // Add application services.
            services.AddTransient<EFStringLocalizerFactory>();
            services.AddSingleton<IConfiguration>(Configuration);
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, EFStringLocalizerFactory localizerFactory)
        {
            _e = localizerFactory.Create(null);

            // a list of all available languages
            var supportedCultures = new List<CultureInfo>
            {
                new CultureInfo("en-US"),
                new CultureInfo("fa-IR")
            };

            var requestLocalizationOptions = new RequestLocalizationOptions
            {
                SupportedCultures = supportedCultures,
                SupportedUICultures = supportedCultures,
            };
            requestLocalizationOptions.RequestCultureProviders.Insert(0, new JsonRequestCultureProvider());
            app.UseRequestLocalization(requestLocalizationOptions);

            app.UseStaticFiles();

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }

    public class JsonRequestCultureProvider : RequestCultureProvider
    {
        public override Task<ProviderCultureResult> DetermineProviderCultureResult(HttpContext httpContext)
        {
            if (httpContext == null)
            {
                throw new ArgumentNullException(nameof(httpContext));
            }

            var config = Startup.LocalConfig;

            string culture = config["AppOptions:Culture"];
            string uiCulture = config["AppOptions:UICulture"];
            string culturedirection = config["AppOptions:CultureDirection"];

            culture = culture ?? "fa-IR"; // Use the value defined in config files or the default value
            uiCulture = uiCulture ?? culture;

            Startup.UiCulture = uiCulture;

            culturedirection = culturedirection ?? "rlt"; // rtl is set to be the default value in case culturedirection is null
            Startup.CultureDirection = culturedirection;

            return Task.FromResult(new ProviderCultureResult(culture, uiCulture));
        }
    }
}

उपरोक्त कोड में, हम पहले तीन public static क्षेत्र चर जोड़ते हैं जिन्हें हम बाद में सेटिंग्स फ़ाइल से पढ़े गए मानों का उपयोग करके आरंभ करेंगे।

Startup क्लास के लिए कंस्ट्रक्टर में हम builder वैरिएबल में एक json सेटिंग्स फ़ाइल जोड़ते हैं। एप्लिकेशन के काम करने के लिए पहली फ़ाइल आवश्यक है, इसलिए आगे बढ़ें और अपने प्रोजेक्ट रूट में appsettings.json बनाएं यदि यह पहले से मौजूद नहीं है। Visual Studio 2015 का उपयोग करके, यह फ़ाइल स्वचालित रूप से बनाई गई है, इसलिए बस इसकी सामग्री को इसमें बदलें: (यदि आप इसका उपयोग करते हैं तो Logging अनुभाग को छोड़ सकते हैं)

appsettings.json

{
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Debug",
      "System": "Information",
      "Microsoft": "Information"
    }
  },
  "AppOptions": {
    "Culture": "en-US", // fa-IR for Persian
    "UICulture": "en-US", // same as above
    "CultureDirection": "ltr" // rtl for Persian/Arabic/Hebrew
  }
}

आगे बढ़ते हुए, अपने प्रोजेक्ट रूट में तीन फ़ोल्डर बनाएं:

Models , Services और LanguagesModels फ़ोल्डर में Localization नाम का एक और फ़ोल्डर बनाएँ।

Services फ़ोल्डर में हम EFLocalization नामक एक नई .cs फ़ाइल EFLocalization । सामग्री होगी: (फिर से बयानों using शामिल नहीं हैं)

EFLocalization.cs

namespace DigitalShop.Services
{
    public class EFStringLocalizerFactory : IStringLocalizerFactory
    {
        private readonly LocalizationDbContext _db;

        public EFStringLocalizerFactory()
        {
            _db = new LocalizationDbContext();
            // Here we define all available languages to the app
            // available languages are those that have a json and cs file in
            // the Languages folder
            _db.AddRange(
                new Culture
                {
                    Name = "en-US",
                    Resources = en_US.GetList()
                },
                new Culture
                {
                    Name = "fa-IR",
                    Resources = fa_IR.GetList()
                }
            );
            _db.SaveChanges();
        }

        public IStringLocalizer Create(Type resourceSource)
        {
            return new EFStringLocalizer(_db);
        }

        public IStringLocalizer Create(string baseName, string location)
        {
            return new EFStringLocalizer(_db);
        }
    }

    public class EFStringLocalizer : IStringLocalizer
    {
        private readonly LocalizationDbContext _db;

        public EFStringLocalizer(LocalizationDbContext db)
        {
            _db = db;
        }

        public LocalizedString this[string name]
        {
            get
            {
                var value = GetString(name);
                return new LocalizedString(name, value ?? name, resourceNotFound: value == null);
            }
        }

        public LocalizedString this[string name, params object[] arguments]
        {
            get
            {
                var format = GetString(name);
                var value = string.Format(format ?? name, arguments);
                return new LocalizedString(name, value, resourceNotFound: format == null);
            }
        }

        public IStringLocalizer WithCulture(CultureInfo culture)
        {
            CultureInfo.DefaultThreadCurrentCulture = culture;
            return new EFStringLocalizer(_db);
        }

        public IEnumerable<LocalizedString> GetAllStrings(bool includeAncestorCultures)
        {
            return _db.Resources
                .Include(r => r.Culture)
                .Where(r => r.Culture.Name == CultureInfo.CurrentCulture.Name)
                .Select(r => new LocalizedString(r.Key, r.Value, true));
        }

        private string GetString(string name)
        {
            return _db.Resources
                .Include(r => r.Culture)
                .Where(r => r.Culture.Name == CultureInfo.CurrentCulture.Name)
                .FirstOrDefault(r => r.Key == name)?.Value;
        }
    }

    public class EFStringLocalizer<T> : IStringLocalizer<T>
    {
        private readonly LocalizationDbContext _db;

        public EFStringLocalizer(LocalizationDbContext db)
        {
            _db = db;
        }

        public LocalizedString this[string name]
        {
            get
            {
                var value = GetString(name);
                return new LocalizedString(name, value ?? name, resourceNotFound: value == null);
            }
        }

        public LocalizedString this[string name, params object[] arguments]
        {
            get
            {
                var format = GetString(name);
                var value = string.Format(format ?? name, arguments);
                return new LocalizedString(name, value, resourceNotFound: format == null);
            }
        }

        public IStringLocalizer WithCulture(CultureInfo culture)
        {
            CultureInfo.DefaultThreadCurrentCulture = culture;
            return new EFStringLocalizer(_db);
        }

        public IEnumerable<LocalizedString> GetAllStrings(bool includeAncestorCultures)
        {
            return _db.Resources
                .Include(r => r.Culture)
                .Where(r => r.Culture.Name == CultureInfo.CurrentCulture.Name)
                .Select(r => new LocalizedString(r.Key, r.Value, true));
        }

        private string GetString(string name)
        {
            return _db.Resources
                .Include(r => r.Culture)
                .Where(r => r.Culture.Name == CultureInfo.CurrentCulture.Name)
                .FirstOrDefault(r => r.Key == name)?.Value;
        }
    }
}

उपरोक्त फ़ाइल में हम कस्टम लोकलाइज़र सेवा बनाने के लिए Entity Framework Core से IStringLocalizerFactory इंटरफ़ेस लागू करते हैं। महत्वपूर्ण हिस्सा EFStringLocalizerFactory का EFStringLocalizerFactory जहां हम सभी उपलब्ध भाषाओं की एक सूची बनाते हैं और इसे डेटाबेस संदर्भ में जोड़ते हैं। इन भाषाओं में से प्रत्येक एक अलग डेटाबेस के रूप में कार्य करती है।

अब निम्न फ़ाइलों में से प्रत्येक को Models/Localization फ़ोल्डर में जोड़ें:

Culture.cs

namespace DigitalShop.Models.Localization
{
    public class Culture
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public virtual List<Resource> Resources { get; set; }
    }
}

Resource.cs

namespace DigitalShop.Models.Localization
{
    public class Resource
    {
        public int Id { get; set; }
        public string Key { get; set; }
        public string Value { get; set; }
        public virtual Culture Culture { get; set; }
    }
}

LocalizationDbContext.cs

namespace DigitalShop.Models.Localization
{
    public class LocalizationDbContext : DbContext
    {
        public DbSet<Culture> Cultures { get; set; }
        public DbSet<Resource> Resources { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseInMemoryDatabase();
        }
    }
}

उपरोक्त फाइलें सिर्फ मॉडल हैं जो भाषा संसाधनों, संस्कृतियों के साथ पॉपुलेटेड होंगी और इसमें EF कोर द्वारा उपयोग किया जाने वाला एक विशिष्ट DBContext भी है।

इस काम को करने के लिए हमें जो आखिरी चीज चाहिए वह है भाषा संसाधन फाइलें बनाना। JSON फाइलें आपके ऐप में उपलब्ध विभिन्न भाषाओं के लिए एक कुंजी-मूल्य जोड़ी को संग्रहीत करने के लिए उपयोग की जाती हैं।

इस उदाहरण में हमारे ऐप में केवल दो भाषाएँ उपलब्ध हैं। अंग्रेजी और फारसी। प्रत्येक भाषा के लिए हमें दो फ़ाइलों की आवश्यकता होती है। JSON फ़ाइल जिसमें की-वैल्यू पेयर और .cs फ़ाइल होती है, जिसमें JSON फ़ाइल के समान नाम वाला एक वर्ग होता है। उस क्लास में एक तरीका है, GetList जो JSON फाइल को GetList करता है और उसे लौटाता है। इस विधि को EFStringLocalizerFactory के निर्माता में कहा जाता है जिसे हमने पहले बनाया था।

तो, इन चार फ़ाइलों को अपने Languages फ़ोल्डर में बनाएँ:

एन US.cs

namespace DigitalShop.Languages
{
    public static class en_US
    {
        public static List<Resource> GetList()
        {
            var jsonSerializerSettings = new JsonSerializerSettings();
            jsonSerializerSettings.MissingMemberHandling = MissingMemberHandling.Ignore;
            return JsonConvert.DeserializeObject<List<Resource>>(File.ReadAllText("Languages/en-US.json"), jsonSerializerSettings);
        }
    }
}

एन US.json

[
  {
    "Key": "Welcome",
    "Value": "Welcome"
  },
  {
    "Key": "Hello",
    "Value": "Hello"
  },
]

पिता-IR.cs

public static class fa_IR
{
    public static List<Resource> GetList()
    {
        var jsonSerializerSettings = new JsonSerializerSettings();
        jsonSerializerSettings.MissingMemberHandling = MissingMemberHandling.Ignore;
        return JsonConvert.DeserializeObject<List<Resource>>(File.ReadAllText("Languages/fa-IR.json", Encoding.UTF8), jsonSerializerSettings);
    }
}

पिता-IR.json

[
  {
    "Key": "Welcome",
    "Value": "خوش آمدید"
  },
  {
    "Key": "Hello",
    "Value": "سلام"
  },
]

हम सब कर रहे हैं। अब अपने कोड ( .cs या .cshtml ) में कहीं भी भाषा के तार (की-वैल्यू पेयर) को एक्सेस करने के लिए आप निम्न कार्य कर सकते हैं:

इन .cs फ़ाइल (नियंत्रक हो या न हो, कोई फर्क नहीं पड़ता):

// Returns "Welcome" for en-US and "خوش آمدید" for fa-IR
var welcome = Startup._e["Welcome"];

एक रेजर दृश्य फ़ाइल ( .cshtml ) में:

<h1>@Startup._e["Welcome"]</h1>

कुछ बातों का ध्यान रखें:

  • यदि आप JSON फ़ाइल में मौजूद किसी Key को एक्सेस करने का प्रयास करते हैं या लोड किया जाता है, तो आपको बस कुंजी शाब्दिक मिलेगा (उपरोक्त उदाहरण में, Startup._e["How are you"] तक पहुंचने का प्रयास करेंगे। How are you कोई भी भाषा सेटिंग नहीं है क्योंकि यह मौजूद नहीं है
  • यदि आप एक भाषा में एक स्ट्रिंग मान को बदलते हैं .json फ़ाइल, आप अनुप्रयोग को पुनः आरंभ करने की आवश्यकता होगी। अन्यथा यह केवल डिफ़ॉल्ट मान (मुख्य नाम) दिखाएगा। यह विशेष रूप से महत्वपूर्ण है जब आप डिबगिंग के बिना अपना ऐप चला रहे हों।
  • appsettings.json उपयोग उन सभी प्रकार की सेटिंग्स को स्टोर करने के लिए किया जा सकता है appsettings.json आपके ऐप को आवश्यकता हो सकती है
  • यदि आप सिर्फ appsettings.json फ़ाइल से भाषा / संस्कृति सेटिंग्स बदलना चाहते हैं, तो ऐप को फिर से शुरू करना आवश्यक नहीं है । इसका मतलब है कि उपयोगकर्ताओं को रनटाइम पर भाषा / संस्कृति को बदलने देने के लिए आपके ऐप इंटरफ़ेस में एक विकल्प हो सकता है।

यहाँ अंतिम परियोजना संरचना है:

यहाँ छवि विवरण दर्ज करें

Url पथ के माध्यम से अनुरोध संस्कृति सेट करें

डिफ़ॉल्ट रूप से अंतर्निहित अनुरोध स्थानीयकरण मिडलवेयर केवल क्वेरी, कुकी या Accept-Language हेडर के माध्यम से सेटिंग संस्कृति का समर्थन करता है। यह उदाहरण दिखाता है कि एक मिडलवेयर कैसे बनाया जाता है जो संस्कृति को पथ के भाग के रूप में /api/en-US/products रूप में सेट करने की अनुमति देता है।

यह उदाहरण मिडलवेयर को पथ के दूसरे खंड में स्थान मान लेता है।

public class UrlRequestCultureProvider : RequestCultureProvider
{
    private static readonly Regex LocalePattern = new Regex(@"^[a-z]{2}(-[a-z]{2,4})?$",
                                                        RegexOptions.IgnoreCase);

    public override Task<ProviderCultureResult> DetermineProviderCultureResult(HttpContext httpContext)
    {
        if (httpContext == null)
        {
            throw new ArgumentNullException(nameof(httpContext));
        }

        var url = httpContext.Request.Path;

        // Right now it's not possible to use httpContext.GetRouteData()
        // since it uses IRoutingFeature placed in httpContext.Features when
        // Routing Middleware registers. It's not set when the Localization Middleware
        // is called, so this example simply assumes the locale will always 
        // be located in the second segment of a path, like in /api/en-US/products
        var parts = httpContext.Request.Path.Value.Split('/');
        if (parts.Length < 3)
        {
            return Task.FromResult<ProviderCultureResult>(null);
        }

        if (!LocalePattern.IsMatch(parts[2]))
        {
            return Task.FromResult<ProviderCultureResult>(null);
        }

        var culture = parts[2];
        return Task.FromResult(new ProviderCultureResult(culture));
    }
}

मिडिलवेयर पंजीकरण

var localizationOptions = new RequestLocalizationOptions
{
    SupportedCultures = new List<CultureInfo>
    {
        new CultureInfo("de-DE"),
        new CultureInfo("en-US"),
        new CultureInfo("en-GB")
    },
    SupportedUICultures = new List<CultureInfo>
    {
        new CultureInfo("de-DE"),
        new CultureInfo("en-US"),
        new CultureInfo("en-GB")
    },
    DefaultRequestCulture = new RequestCulture("en-US")
};

// Adding our UrlRequestCultureProvider as first object in the list
localizationOptions.RequestCultureProviders.Insert(0, new UrlRequestCultureProvider
{
    Options = localizationOptions
});

app.UseRequestLocalization(localizationOptions);

कस्टम रूट की कमी

कस्टम मार्ग बाधाओं को जोड़ना और बनाना मार्ग अवरोध उदाहरण में दिखाए जाते हैं। बाधाओं का उपयोग करना कस्टम मार्ग बाधाओं के उपयोग को सरल करता है।

मार्ग का पंजीकरण

कस्टम बाधाओं का उपयोग किए बिना मार्गों को पंजीकृत करने का उदाहरण

app.UseMvc(routes => 
{ 
    routes.MapRoute( 
        name: "default", 
        template: "api/{culture::regex(^[a-z]{{2}}-[A-Za-z]{{4}}$)}}/{controller}/{id?}"); 
    routes.MapRoute( 
        name: "default", 
        template: "api/{controller}/{id?}"); 
});


Modified text is an extract of the original Stack Overflow Documentation
के तहत लाइसेंस प्राप्त है CC BY-SA 3.0
से संबद्ध नहीं है Stack Overflow