json

В данном посте рассмотрено создание настраиваемой программы для чтения ресурсов из json файла под Net Core. По итогу, у нас будет библиотека локализации ресурсов программы, с помощью которой мы сможем добавить в любое свое программное обеспечение поддержку нескольких языков. Давайте приступим.

Во-первых, нам нужен класс, описывающий наш json-файл:

[pastacode lang=»c» manual=»using%20System.Collections.Generic%3B%0A%0Anamespace%20I18N%0A%7B%0A%20%20%20%20internal%20class%20JsonLocalization%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20public%20string%20Key%20%7B%20get%3B%20set%3B%20%7D%0A%20%20%20%20%20%20%20%20public%20Dictionary%3Cstring%2C%20string%3E%20LocalizedValues%20%7B%20get%3B%20set%3B%20%7D%0A%20%20%20%20%7D%0A%7D%0A» message=»» highlight=»» provider=»manual»/]

Key — это уникальный идентификатор для локализации, а LocalizedValues — это словарь, ключ которого — язык, а значение — текст, который должен отображаться.

Мы также собираемся создать новый тип Exception, чтобы легко определить, что пошло не так с приложением.

[pastacode lang=»c» manual=»using%20System%3B%0A%0Anamespace%20I18N%0A%7B%0A%20%20%20%20public%20class%20I18NException%20%3A%20Exception%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20public%20I18NException(string%20message)%20%3A%20base(message)%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20public%20I18NException(string%20message%2C%20Exception%20innerException)%20%3A%20base(message%2C%20innerException)%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20public%20I18NException()%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%7D%0A» message=»» highlight=»» provider=»manual»/]

Здесь происходит магия: класс JsonLocalizer будет читать наши файлы ресурсов json, сохранять их в памяти и делать доступными для нашего приложения.

В конструктор должны быть переданы два параметра: useBase и additionalPaths.

Если для useBase установлено значение true, локализатор загрузит файлы * .json, находящиеся в папке Resources.
AdditionalPaths использует тип в качестве ключа, локализатор будет использовать этот тип для поиска пути сборки и чтения файлов * .json в папке Resources.

[pastacode lang=»c» manual=»%0ASkip%20to%20content%0ALog%20in%0ACreate%20account%0ACreating%20a%20json%20resource%20reader%20for%20dotnet%20core%0A%23csharp%0A15%20%D0%B8%D1%8E%D0%BB.%20%E3%83%BB3%20min%20read%0A%0AIn%20this%20post%2C%20we’re%20going%20to%20create%20a%20custom%20Resource%20reader%20to%20use%20with%20dotnet%20core%20libraries.%20In%20the%20end%2C%20we%20can%20have%20a%20project%20dedicated%20to%20resources.%0A%0AFirst%2C%20we%20need%20a%20class%20that%20represents%20our%20json%20file%3A%0A%0Ausing%20System.Collections.Generic%3B%0A%0Anamespace%20I18N%0A%7B%0A%20%20%20%20internal%20class%20JsonLocalization%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20public%20string%20Key%20%7B%20get%3B%20set%3B%20%7D%0A%20%20%20%20%20%20%20%20public%20Dictionary%3Cstring%2C%20string%3E%20LocalizedValues%20%7B%20get%3B%20set%3B%20%7D%0A%20%20%20%20%7D%0A%7D%0A%0AThe%20Key%20is%20an%20unique%20identifier%20for%20the%20localization%20and%20LocalizedValues%20is%20a%20dictionary%2C%20which%20its%20key%20is%20the%20language%20and%20the%20value%20the%20text%20that%20must%20be%20displayed.%0A%0AWe%20also%20going%20to%20create%20a%20new%20type%20of%20Exception%2C%20to%20easily%20pinpoint%20what%20went%20wrong%20with%20the%20application.%0A%0Ausing%20System%3B%0A%0Anamespace%20I18N%0A%7B%0A%20%20%20%20public%20class%20I18NException%20%3A%20Exception%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20public%20I18NException(string%20message)%20%3A%20base(message)%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20public%20I18NException(string%20message%2C%20Exception%20innerException)%20%3A%20base(message%2C%20innerException)%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20public%20I18NException()%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%7D%0A%0AHere%20is%20where%20the%20magic%20happens%2C%20the%20JsonLocalizer%20class%20will%20read%20our%20json%20resources%20files%2C%20store%20them%20in%20memory%20and%20make%20them%20available%20to%20our%20application.%0A%0AIn%20our%20constructor%20we%20expect%20two%20parameters%2C%20useBase%20and%20additionalPaths.%0A%0AIf%20useBase%20is%20set%20to%20true%2C%20the%20localizer%20will%20load%20*.json%20files%20that%20are%20in%20the%20Resources%20folder.%0AadditionalPaths%20uses%20a%20type%20as%20a%20key%2C%20the%20localizer%20will%20use%20this%20type%20to%20find%20the%20assembly%20path%20and%20read%20the%20*.json%20files%20in%20the%20Resources%20folder.%0A%0Ausing%20System%3B%0Ausing%20System.Collections.Generic%3B%0Ausing%20System.Globalization%3B%0Ausing%20System.IO%3B%0Ausing%20System.Linq%3B%0Ausing%20Newtonsoft.Json%3B%0A%0Anamespace%20I18N%0A%7B%0A%20%20%20%20public%20class%20JsonLocalizer%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20private%20readonly%20Dictionary%3Cstring%2C%20JsonLocalization%5B%5D%3E%20_localization%0A%20%20%20%20%20%20%20%20%20%20%20%20%3D%20new%20Dictionary%3Cstring%2C%20JsonLocalization%5B%5D%3E()%3B%0A%0A%20%20%20%20%20%20%20%20public%20JsonLocalizer(bool%20useBase%20%3D%20true%2C%20Dictionary%3CType%2C%20string%3E%20additionalPaths%20%3D%20null)%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20(useBase)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20PopulateLocalization(%22Resources%22)%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20(additionalPaths%20%3D%3D%20null)%20return%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20foreach%20(var%20additional%20in%20additionalPaths)%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20var%20codeBase%20%3D%20additional.Key.Assembly.CodeBase%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20var%20uri%20%3D%20new%20UriBuilder(codeBase)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20var%20data%20%3D%20Uri.UnescapeDataString(uri.Path)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20var%20path%20%3D%20Path.GetDirectoryName(data)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20var%20fullPath%20%3D%20Path.Combine(path%2C%20additional.Value)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20PopulateLocalization(fullPath)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%2F%2F%2F%20%3Csummary%3E%0A%20%20%20%20%20%20%20%20%2F%2F%2F%20resource%3Akey%3Aculture%0A%20%20%20%20%20%20%20%20%2F%2F%2F%20resource%20is%20the%20resource%20name%0A%20%20%20%20%20%20%20%20%2F%2F%2F%20key%20is%20the%20key%20you’re%20looking%20for%0A%20%20%20%20%20%20%20%20%2F%2F%2F%20culture%20is%20optional%0A%20%20%20%20%20%20%20%20%2F%2F%2F%20%3C%2Fsummary%3E%0A%20%20%20%20%20%20%20%20%2F%2F%2F%20%3Cparam%20name%3D%22key%22%3E%3C%2Fparam%3E%0A%20%20%20%20%20%20%20%20public%20string%20this%5Bstring%20key%5D%20%3D%3E%20GetString(key)%3B%0A%0A%0A%20%20%20%20%20%20%20%20private%20void%20PopulateLocalization(string%20path)%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20foreach%20(var%20resource%20in%20Directory.GetFiles(path%2C%20%22*.json%22%2C%20SearchOption.AllDirectories))%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20try%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20var%20fileInfo%20%3D%20new%20FileInfo(resource)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20var%20fileName%20%3D%20fileInfo.Name.Substring(0%2C%20fileInfo.Name.IndexOf(‘.’))%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20var%20loc%20%3D%20JsonConvert.DeserializeObject%3CJsonLocalization%5B%5D%3E(File.ReadAllText(resource))%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20_localization.Add(fileName%2C%20loc)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20catch%20(ArgumentException%20e)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20throw%20new%20I18NException(%24%22Resource%20%7Bresource%7D%20was%20already%20added%2C%20check%20your%20files.%22%2C%20e)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20catch%20(Exception%20ex)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20throw%20new%20I18NException(%22Something%20wrong%20is%20not%20right%2C%20check%20inner%20exception%22%2C%20ex)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20private%20string%20GetString(string%20query)%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20try%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20string%20culture%20%3D%20null%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20var%20split%20%3D%20query.Split(‘%3A’)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20var%20resource%20%3D%20split%5B0%5D%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20var%20key%20%3D%20split%5B1%5D%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(split.Length%20%3E%202)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20culture%20%3D%20split%5B2%5D%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20culture%20%3D%20culture%20%3F%3F%20CultureInfo.CurrentCulture.Name%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20_localization%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20.Single(l%20%3D%3E%20l.Key%20%3D%3D%20resource)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20.Value.Single(x%20%3D%3E%20x.Key%20%3D%3D%20key)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20.LocalizedValues%5Bculture%5D%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20catch%20(Exception%20ex)%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20throw%20new%20I18NException(%24%22Couldn’t%20find%20key%3A%20%7Bquery%7D%22%2C%20ex)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%7D%0A%0AIn%20dotnet%20core%20applications%2C%20you%20can%20add%20the%20JsonLocalizer%20using%20the%20IServiceCollection%20in%20the%20ConfigureServices%20method.%0A%0A%2F%2F%20use%20it%20in%20DI%20as%20a%20singleton%0Apublic%20void%20ConfigureServices(IServiceCollection%20services)%0A%7B%0A%20%20%20%2F%2F%20Other%20configurations%20…%0A%20%20%20services.AddSingleton%3CJsonLocalizer%3E()%3B%0A%7D%0A%0ATo%20handle%20additionalPaths%0A%0Avar%20additional%20%3D%20new%20Dictionary%3CType%2C%20string%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%20typeof(MyClass)%2C%20%22My%20Resource%20Folder%22%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%20typeof(MyAnotherClass)%2C%20%22My%20Resource%20Folder%2FEven%20Handles%20sub%20folders%22%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%3B%0A%0Avar%20withExternalSources%20%3D%20new%20JsonLocalizer(additionalPaths%3A%20additional)%3B%0A%0ANow%20that%20we%20have%20everything%20set%20up%2C%20we%20can%20start%20using%20our%20localizer%3A%0A%0Aprivate%20readonly%20JsonLocalizer%20_localizer%3B%0A%0Apublic%20class%20MySampleClass(JsonLocalizer%20localizer)%0A%7B%0A%20%20%20_localizer%20%3D%20localizer%3B%0A%7D%0A%0Apublic%20string%20GetLocalizedMessage()%0A%7B%0A%20%20%20return%20_localizer%5B%22MyAppResource%3AMyKey%22%5D%3B%0A%7D%0A%0AThe%20Localizer%20will%20find%20your%20text%20by%3A%0A%0AFileName%3AKey%3ALanguage%0A%0AHere%20are%20some%20examples%20of%20how%20to%20write%20your%20resource%20files%3A%0AFile%20Name%20%09Resource%20Name%0AMyResource.json%20%09MyResource%0AMyApp.Resource.json%20%09MyApp%0AMyApp-Errors.Resource.json%20%09MyApp-Errors%0AMyApp.Errors.Resource.json%20%09MyApp%0A%0AThe%20Key%20is%20the%20key%20inside%20the%20resource%20file%2C%20and%20the%20Language%20is%20the%20culture%2C%20if%20not%20informed%2C%20will%20use%20the%20CultureInfo.CurrentCulture%20value.%0A%0AThe%20json%20resource%20file%20should%20follow%20this%20format%3A%0A%0A%5B%0A%20%20%20%7B%0A%20%20%20%20%20%20%22Key%22%3A%22Name%22%2C%0A%20%20%20%20%20%20%22LocalizedValues%22%3A%7B%0A%20%20%20%20%20%20%20%20%20%22en-US%22%3A%22Name%22%2C%0A%20%20%20%20%20%20%20%20%20%22pt-BR%22%3A%22Nome%22%0A%20%20%20%20%20%20%7D%0A%20%20%20%7D%2C%0A%20%20%20%7B%0A%20%20%20%20%20%20%22Key%22%3A%22Age%22%2C%0A%20%20%20%20%20%20%22LocalizedValues%22%3A%7B%0A%20%20%20%20%20%20%20%20%20%22en-US%22%3A%22Age%22%2C%0A%20%20%20%20%20%20%20%20%20%22pt-BR%22%3A%22Idade%22%0A%20%20%20%20%20%20%7D%0A%20%20%20%7D%0A%5D%0A%0ADiscussion%20(0)%0Apic%0ACode%20of%20Conduct%20%E2%80%A2%20Report%20abuse%0ARead%20next%0Amr_eking%20profile%20image%0AAdvanced%20Blazor%20State%20Management%20Using%20Fluxor%2C%20part%206%20-%20Persisting%20State%0A%0AEric%20King%20-%20Mar%2014%0Ajessicanathany%20profile%20image%0ACreating%20a%20simple%20project%20Web%20API%20with%20VSCode%20and%20Entity%20Framework%0A%0AJ%C3%A9ssica%20Nathany%20-%20Mar%2013%0Atechwithpat%20profile%20image%0ACall%20an%20existing%20Web%20API%20with%20a%20C%23%20application%20(VIDEO)%0A%0APatrick%20Tshibanda%20-%20Feb%2021%0Aisaacojeda%20profile%20image%0AC%23%3A%20Llamadas%20confiables%20a%20APIs%20con%20Polly%20y%20RestEase%0A%0AIsaac%20Ojeda%20-%20Feb%208%0AEduardo%20Juli%C3%A3o%0ASoftware%20Developer%20%7C%20Geek%20since%201993%20%7C%20%40monsieurxuxu%20on%20twitter%0A%20%0A%0A%20%20%20%20Work%0A%20%20%20%20Software%20Engineer%0A%20%20%20%20Joined%0A%20%20%20%203%20%D0%B8%D1%8E%D0%BB.%202021%20%D0%B3.%0A%0AMore%20from%20Eduardo%20Juli%C3%A3o%0AEvent%20Stream%20-%20Server%20Side%0A%23csharp%0ADependency%20Inversion%20Principle%0A%23beginners%20%23csharp%0AInterface%20Segregation%20Principle%0A%23beginners%20%23csharp%0A%0Ausing%20System%3B%0Ausing%20System.Collections.Generic%3B%0Ausing%20System.Globalization%3B%0Ausing%20System.IO%3B%0Ausing%20System.Linq%3B%0Ausing%20Newtonsoft.Json%3B%0A%0Anamespace%20I18N%0A%7B%0A%20%20%20%20public%20class%20JsonLocalizer%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20private%20readonly%20Dictionary%3Cstring%2C%20JsonLocalization%5B%5D%3E%20_localization%0A%20%20%20%20%20%20%20%20%20%20%20%20%3D%20new%20Dictionary%3Cstring%2C%20JsonLocalization%5B%5D%3E()%3B%0A%0A%20%20%20%20%20%20%20%20public%20JsonLocalizer(bool%20useBase%20%3D%20true%2C%20Dictionary%3CType%2C%20string%3E%20additionalPaths%20%3D%20null)%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20(useBase)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20PopulateLocalization(%22Resources%22)%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20(additionalPaths%20%3D%3D%20null)%20return%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20foreach%20(var%20additional%20in%20additionalPaths)%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20var%20codeBase%20%3D%20additional.Key.Assembly.CodeBase%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20var%20uri%20%3D%20new%20UriBuilder(codeBase)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20var%20data%20%3D%20Uri.UnescapeDataString(uri.Path)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20var%20path%20%3D%20Path.GetDirectoryName(data)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20var%20fullPath%20%3D%20Path.Combine(path%2C%20additional.Value)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20PopulateLocalization(fullPath)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%2F%2F%2F%20%3Csummary%3E%0A%20%20%20%20%20%20%20%20%2F%2F%2F%20resource%3Akey%3Aculture%0A%20%20%20%20%20%20%20%20%2F%2F%2F%20resource%20is%20the%20resource%20name%0A%20%20%20%20%20%20%20%20%2F%2F%2F%20key%20is%20the%20key%20you’re%20looking%20for%0A%20%20%20%20%20%20%20%20%2F%2F%2F%20culture%20is%20optional%0A%20%20%20%20%20%20%20%20%2F%2F%2F%20%3C%2Fsummary%3E%0A%20%20%20%20%20%20%20%20%2F%2F%2F%20%3Cparam%20name%3D%22key%22%3E%3C%2Fparam%3E%0A%20%20%20%20%20%20%20%20public%20string%20this%5Bstring%20key%5D%20%3D%3E%20GetString(key)%3B%0A%0A%0A%20%20%20%20%20%20%20%20private%20void%20PopulateLocalization(string%20path)%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20foreach%20(var%20resource%20in%20Directory.GetFiles(path%2C%20%22*.json%22%2C%20SearchOption.AllDirectories))%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20try%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20var%20fileInfo%20%3D%20new%20FileInfo(resource)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20var%20fileName%20%3D%20fileInfo.Name.Substring(0%2C%20fileInfo.Name.IndexOf(‘.’))%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20var%20loc%20%3D%20JsonConvert.DeserializeObject%3CJsonLocalization%5B%5D%3E(File.ReadAllText(resource))%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20_localization.Add(fileName%2C%20loc)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20catch%20(ArgumentException%20e)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20throw%20new%20I18NException(%24%22Resource%20%7Bresource%7D%20was%20already%20added%2C%20check%20your%20files.%22%2C%20e)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20catch%20(Exception%20ex)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20throw%20new%20I18NException(%22Something%20wrong%20is%20not%20right%2C%20check%20inner%20exception%22%2C%20ex)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20private%20string%20GetString(string%20query)%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20try%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20string%20culture%20%3D%20null%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20var%20split%20%3D%20query.Split(‘%3A’)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20var%20resource%20%3D%20split%5B0%5D%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20var%20key%20%3D%20split%5B1%5D%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(split.Length%20%3E%202)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20culture%20%3D%20split%5B2%5D%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20culture%20%3D%20culture%20%3F%3F%20CultureInfo.CurrentCulture.Name%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20_localization%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20.Single(l%20%3D%3E%20l.Key%20%3D%3D%20resource)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20.Value.Single(x%20%3D%3E%20x.Key%20%3D%3D%20key)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20.LocalizedValues%5Bculture%5D%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20catch%20(Exception%20ex)%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20throw%20new%20I18NException(%24%22Couldn’t%20find%20key%3A%20%7Bquery%7D%22%2C%20ex)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%7D» message=»» highlight=»» provider=»manual»/]

В приложениях dotnet вы можете добавить JsonLocalizer с помощью IServiceCollection в методе ConfigureServices.

[pastacode lang=»c» manual=»%2F%2F%20use%20it%20in%20DI%20as%20a%20singleton%0Apublic%20void%20ConfigureServices(IServiceCollection%20services)%0A%7B%0A%20%20%20%2F%2F%20Other%20configurations%20…%0A%20%20%20services.AddSingleton%3CJsonLocalizer%3E()%3B%0A%7D%0A» message=»» highlight=»» provider=»manual»/]

Для обработки дополнительных путей

[pastacode lang=»c» manual=»var%20additional%20%3D%20new%20Dictionary%3CType%2C%20string%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%20typeof(MyClass)%2C%20%22My%20Resource%20Folder%22%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%20typeof(MyAnotherClass)%2C%20%22My%20Resource%20Folder%2FEven%20Handles%20sub%20folders%22%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%3B%0A%0Avar%20withExternalSources%20%3D%20new%20JsonLocalizer(additionalPaths%3A%20additional)%3B%0A» message=»» highlight=»» provider=»manual»/]

Теперь, когда у нас все настроено, мы можем начать использовать наш локализатор:

[pastacode lang=»c» manual=»private%20readonly%20JsonLocalizer%20_localizer%3B%0A%0Apublic%20class%20MySampleClass(JsonLocalizer%20localizer)%0A%7B%0A%20%20%20_localizer%20%3D%20localizer%3B%0A%7D%0A%0Apublic%20string%20GetLocalizedMessage()%0A%7B%0A%20%20%20return%20_localizer%5B%22MyAppResource%3AMyKey%22%5D%3B%0A%7D%0A» message=»» highlight=»» provider=»manual»/]

Локализатор найдет ваш текст по такой структуре:

FileName:Key:Language

Вот несколько примеров того, как писать файлы ресурсов:

Снимок

Ключ — это ключ внутри файла ресурсов, а язык — это культура, если не сообщить, будет использоваться значение CultureInfo.CurrentCulture.

Файл ресурсов json должен иметь следующий формат:

[pastacode lang=»c» manual=»%5B%0A%20%20%20%7B%0A%20%20%20%20%20%20%22Key%22%3A%22Name%22%2C%0A%20%20%20%20%20%20%22LocalizedValues%22%3A%7B%0A%20%20%20%20%20%20%20%20%20%22en-US%22%3A%22Name%22%2C%0A%20%20%20%20%20%20%20%20%20%22pt-BR%22%3A%22Nome%22%0A%20%20%20%20%20%20%7D%0A%20%20%20%7D%2C%0A%20%20%20%7B%0A%20%20%20%20%20%20%22Key%22%3A%22Age%22%2C%0A%20%20%20%20%20%20%22LocalizedValues%22%3A%7B%0A%20%20%20%20%20%20%20%20%20%22en-US%22%3A%22Age%22%2C%0A%20%20%20%20%20%20%20%20%20%22pt-BR%22%3A%22Idade%22%0A%20%20%20%20%20%20%7D%0A%20%20%20%7D%0A%5D» message=»» highlight=»» provider=»manual»/]