{"id":23094898,"url":"https://github.com/aksoftware98/multilanguages","last_synced_at":"2025-06-21T05:35:19.242Z","repository":{"id":40859992,"uuid":"248874633","full_name":"aksoftware98/multilanguages","owner":"aksoftware98","description":"AKSoftware.Localization.MultiLanguages is a package for .NET developers allows them to easily build apps target multiple languages with just few lines of code","archived":false,"fork":false,"pushed_at":"2024-10-25T20:48:19.000Z","size":3817,"stargazers_count":86,"open_issues_count":15,"forks_count":15,"subscribers_count":10,"default_branch":"master","last_synced_at":"2025-06-14T17:17:07.711Z","etag":null,"topics":["blazor-server","blazor-webassembly","csharp","dotnet","dotnet-core","localization","multilanguage","nuget","yaml"],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aksoftware98.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-03-20T23:54:39.000Z","updated_at":"2025-02-21T01:49:12.000Z","dependencies_parsed_at":"2025-01-21T03:06:22.414Z","dependency_job_id":"75a8c729-e3bd-42ef-97da-46a89fadd6df","html_url":"https://github.com/aksoftware98/multilanguages","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/aksoftware98/multilanguages","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aksoftware98%2Fmultilanguages","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aksoftware98%2Fmultilanguages/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aksoftware98%2Fmultilanguages/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aksoftware98%2Fmultilanguages/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aksoftware98","download_url":"https://codeload.github.com/aksoftware98/multilanguages/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aksoftware98%2Fmultilanguages/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261070035,"owners_count":23105312,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["blazor-server","blazor-webassembly","csharp","dotnet","dotnet-core","localization","multilanguage","nuget","yaml"],"created_at":"2024-12-16T22:18:39.372Z","updated_at":"2025-06-21T05:35:14.230Z","avatar_url":"https://github.com/aksoftware98.png","language":"C#","readme":"# AKSoftware.Localization.MultiLanguages\n## Blazor Localization\n## ASP.NET Localization\n## .NET Localization\n\n### v6.0.0-alpha\n[![Build Badge](https://aksoftware98.visualstudio.com/AkMultiLanguages/_apis/build/status/aksoftware98.multilanguages?branchName=master)](https://aksoftware98.visualstudio.com/AkMultiLanguages/_build/latest?definitionId=4\u0026branchName=master)\n\n  \n\n![Nuget](https://img.shields.io/nuget/dt/AKSoftware.Localization.MultiLanguages?color=nuget\u0026label=Nuget\u0026style=plastic)\n\n  \u003cimg width=\"100\" src=\"https://github.com/aksoftware98/multilanguages/blob/master/src/AKSoftware.Localization.MultiLanguages/AkMultiLanguages.png?raw=true\" /\u003e\n    \u003cimg width=\"100\" src=\"https://github.com/aksoftware98/multilanguages/blob/master/src/AKSoftware.Localization.MultiLanguages.Blazor/AkMultiLanguages.png?raw=true\" /\u003e\n\nThe most advanced .NET localization package for your projects; light, fast, super easy to use, and much more.\nMaking your app available in many languages is now a simple straightforward process, *AKSoftware.Localization.Multilanguages* offers the ultimate solution.  \n\nWhy MultiLanguages is better:\n\n| Feature                                                                        | .NET Localization  | AKSoftware.Localization.MultiLanguages |\n|--------------------------------------------------------------------------------|:------------------:|:--------------------------------------:|\n| Memory Heavy, Hard to Maintain RESX Resource Files                             | :heavy_check_mark: |                                        |\n| Less Memory, Easy to Maintain YAML Resource Files                              |                    | :heavy_check_mark:                     |\n| Generate English Resource YAML File from Localizable Strings from your UI Code |                    | :heavy_check_mark:                     |\n| Automatically Replace Localizable Strings with Variables                                     |                    | :heavy_check_mark:                     |\n| Data Attribute Localization                                                    | :heavy_check_mark: | :heavy_check_mark:                     |\n| Hierarchal Language Key Support                                                |                    | :heavy_check_mark:                     |\n| Translate Resource Files into 69 Different Languages                           |                    | :heavy_check_mark:                     |\n| String Interpolation                                                           |                    | :heavy_check_mark:                     |\n| Get Registered Languages                                                       |                    | :heavy_check_mark:                     |\n| Use Enum as Translation Key                                                    | :heavy_check_mark: | :heavy_check_mark:                     |\n| Generate Enum Translation Key Code                                             |                    | :heavy_check_mark:                     |\n| Source generator for injectable keys accessor service                          |                    | :heavy_check_mark:                     |\n| Use Static Class as Translation Key                                            | :heavy_check_mark: | :heavy_check_mark:                     |\n| Generate Static Class Translation Key Code                                     | :heavy_check_mark: | :heavy_check_mark:                     |\n| Verify All Source Code Files are Localized                                     |                    | :heavy_check_mark:                     |\n| Verify All Keys Can Be Found                                                   |                    | :heavy_check_mark:                     |\n| Verify There Are No Unused Keys                                                |                    | :heavy_check_mark:                     |\n| Verify There Are No Duplicate Keys                                             |                    | :heavy_check_mark:                     |\n\n\n### Key Features\n\n\u003cimg width=\"400\" src=\"https://github.com/aksoftware98/multilanguages/blob/master/Images/v6/yaml-based.png?raw=true\" /\u003e\n\u003cimg width=\"400\" src=\"https://github.com/aksoftware98/multilanguages/blob/master/Images/v6/hierarchy-friendly.png?raw=true\" /\u003e\n\u003cimg width=\"400\" src=\"https://github.com/aksoftware98/multilanguages/blob/master/Images/v6/source-generator.png?raw=true\" /\u003e\n\u003cimg width=\"400\" src=\"https://github.com/aksoftware98/multilanguages/blob/master/Images/v6/interpolation-support.png?raw=true\" /\u003e\n\n\nIt can be used for all type of .NET Apps (Blazor, UWP, Xamarin, Windows, ASP.NET Core MVC, Razor Pages ....)\n\nhttps://akmultilanguages.azurewebsites.net\n\nBuilt with Love by Ahmad Mozaffar\n\nhttp://ahmadmozaffar.net\n\n  \n\n## YouTube Session\n\nhttps://youtu.be/Xz68c8GBYz4\n\n  \n\n![Simple UI supports German](https://github.com/aksoftware98/multilanguages/blob/master/Example/BlazorWasmMultiLanguages/BlazorWasmMultiLanguages/wwwroot/German.png?raw=true)\n\n  \n\n![Blazor UI with Japanease](https://raw.githubusercontent.com/aksoftware98/multilanguages/master/Example/BlazorWasmMultiLanguages/BlazorWasmMultiLanguages/wwwroot/Japan.png)\n\n\n# Why YAML\nMost common solution for multilanguage in .NET are .resx resource files. .resx files are XML based so they are not too friendly to deal with and most likely a GUI tool is needed for keys management. XML is also huge and slower to parse. On the other hand, YAML is new, very fast to parse, and the file structure is very simple and doesn't contain any unneeded characters which make the file size smaller compared to XML.\nFor modern SPA apps with Blazor WebAssembly for example, large language files with .resx might slow down the load time for the download.\nYAML file structure allows for nested objects which a lovely feature you can take advantage of to build an organized language key-values files without long concatenated names.\nFinally, due to the simplicity of YAML, it's makes it very easy to build automation on top of it like source generator and static classes creation.\n\n# Features\nAKSoftware.Localization.Multilanguage prvoides all the feature set needed for any multilanguage support like:\n- Easy to get started.\n- Online translator tool to translate your files in one click for more 65 languages  https://akmultilanguages.azurewebsites.net\n- Light and high-performance\n- Blazor Server \u0026 WebAssembly support\n- Out of the box state management for **Blazor** components\n- Multiple language file sources (Files in folder or embedded files)\n- String interpolation support\n- Dynamically list all language keys\n- Dynamically list all available langauges\n- Dependency injection support\n- Hierarchical language keys in YAML\n- Code generators to generate full keys accessor service, static class with const strings, enums, and more..\n- v6.1 will bring the localization assistant to localize existing apps with minimal effort.\n- Full UWP support\n\n\n\n# Getting Started\n\n  \n\nFor Nuget Package Manager install the package\n\n(Nuget Package Manager Console)\n\n``` PS\n\nInstall-Package AKSoftware.Localization.MultiLanguages -Version 6.0.0-alpha\n\n```\n\n(Using dotNet CLI)\n\n``` CLI\n\ndotnet add package AKSoftware.Localization.MultiLanuages --version 6.0.0-alpha\n\n```\n\n**For Blazor** additional package is required that helps managing the state of the component when changing the language\n\n(Nuget Package Manager Console)\n\n``` PS\n\nInstall-Package AKSoftware.Localization.MultiLanguages.Blazor -Version 6.0.0-alpha\n\n```\n\n(Using dotNet CLI)\n\n``` CLI\n\ndotnet add package AKSoftware.Localization.MultiLanuages.Blazor --version 6.0.0-alpha\n\n```\n\n**For Source Generator** install:\n(Nuget Package Manager Console)\n  \n``` PS\n\nInstall-Package AKSoftware.Localization.MultiLanguages.SourceGenerator -Version 6.0.0-alpha\n\n```\n\n(Using dotNet CLI)\n\n``` CLI\n\ndotnet add package AKSoftware.Localization.MultiLanuages.SourceGenerator--version 6.0.0-alpha\n\n```\n\n\u003e**Source Generator Note**\n\u003e When using the source generator, the package will take care of setting the file as embedded resources, and it generates a new interface named **IKeysAccessor** this service wraps the access to all the keys and tested keys in your language files.\n\u003e \n\u003e However, the package must be installed the project that contains the en-US.yml file, so if your solution is a single project you can directly install it in the same project. If your solution consists of multiple projects and you want to use the localization across all of them, make sure to create a seperate project for localization and reference it in your other projects.\n  \n\n## Create the Resources Folder\n\n  \n\nInside your project create a folder called \"Resources\"\n\nand inside it create a file called \"*en-US.yml*\" which is a YAML file\n\nthen set your keywords inside the file like this\n\n``` YAML\n\nHelloWorld: Hello World\n\nWelcome: Welcome\n\n...\n\n```\n\n\u003e We chose YAML files because it's very light comparing it to XML or JSON and make the output dll very small, in addition to that it's much way faster in serialization and deserialization\n\n  \n\n## Set the build action of the file to EmbeddedResource\n\n  \n\nSelect the file in the Solution Explorer window and from the properties window set the build action property to \"Embedded Resource\"\n\n\u003e**Note**\n\u003eIn case of using the Source Generator package, that will be taken care of automatically.\n\n  \n\n## Translate the file\n\n  \n\nVisit the online translation tool on the following link\n\nhttps://akmultilanguages.azurewebsites.net\n\n  \n\nGo to translate app page\n\n  \n\nUpload your YAML file and click submit\n\nAll the languages will be available with just one click - install all the languages you want to support in your application\n\n  \n\n## Import the files to the Resources folder\n\n  \n\nImport the files to the resources folder you have just created and set the build action property for them as Embedded Resources also\n\n  \n\n## Coding time\n\n### Blazor Demo:\n\n  \n\nGo to **program.cs** and register the Language Container Service in the Dependency Injection container\n\nImport the library\n\n``` C#\n\nusing  AKSoftware.Localization.MultiLanguages\n\n```\n\n  \n\nRegister the service\n\n``` C#\n\n// Specify the assembly that has the langauges files, in this situation it's the current assembly\n\nbuilder.Services.AddLanguageContainer\u003cEmbeddedResourceKeysProvider\u003e(Assembly.GetExecutingAssembly());\n\n// You can specify the default culture of the project like this\n\n// builder.Services.AddLanguageContainer(Assembly.GetExecutingAssembly(), CultureInfo.GetCultureInfo(\"fr-Fr\"));\n\n```\n\n**Note:**\n\nIf you don't specify a default culture the library will try to find the file that matches the culture of the current user, if it's not existing it will try to find any file that matches the same language, then if it's not there it will try to find the English file then the first file in the folder, otherwise it will throw an exception\n\n\u003e **Source Generator Note** \n\u003e In case of using the Source Generator and want to have direct access without using the ILanguageContainerService and provide the key as string make sure to also register the auto-generated interface instance in your dependency injection container:\n\u003e ``\n\u003e builder.Services.AddKeysAccessor();\n\u003e ``\n\n  \n  \n\n# Start dealing with components\n\nIn the _imports.razor file make sure to add the following namespaces\n\n``` C#\n\nusing  AKSoftware.Localization.MultiLanguages\n\nusing  AKSoftware.Localization.MultiLanguages.Blazor\n\n```\n\nWith in your components that you want to localize inject the service\n\n```csharp\n\n@inject  ILanguageContainerService  languageContainer\n\n```\nor with Source Genertaor inject the keys accessor instead\n```csharp\n@inject IKeysAccessor KeysAccessor\n```\n\nAnd start getting the values from your files just like this\n\n```razor\n@* Without Source Generator *@\n\u003ch1\u003e@languageContainer.Keys[\"HelloWorld\"]\u003c/h1\u003e\n\n@* With Source Generator *@\n\u003ch1\u003e@KeysAccessor.HelloWorld\u003c/h1\u003e\n\n```\n\n  \n\nAnd to be able to get the state updated for each component that contains localized text call the extension method in the OnInitialized or OnInitializedAsync overriden methods for each component as following\n\n```csharp\n\nprotected  override  void  OnInitialized()\n\n{\n\n// This will make the current component gets updated whenever you change the language of the application\n\nlanguageContainer.InitLocalizedComponent(this);\n\n}\n\n```\n\n  \n\n## Change the language from the UI\n\n  \n\nYou are able to change the language and choose any language you have added from the UI like this\n\nInject the service in the component\n\n``` C#\n\n@inject  ILanguageContainerService  languageContainer\n\n```\n\nAdd a button and set the @onclick method\n\n``` Razor\n\n\u003cbutton @onclick=\"SetFrench\"\u003eFrench\u003c/button\u003e\n\n@code\n\n{\n\nvoid SetFrench()\n\n{\n\nlanguageContainer.SetLanguage(System.Globalization.CultureInfo.GetCultureInfo(\"fr-FR\"));\n\n}\n\n}\n\n```\n\n  \n\n## Interpolation Feature\n\nStarting from version 4.0 now there is the ability to create dynamic values to replace their values at runtime using Interpolation feature:\n\nFollowing you can see how to use this feature\n\n  \n\nLanguage File en-US:\n\n```YAML\n\nWelcome: Welcome {username} to our system {version}\n\n```\n\n  \n\nIn C# to replace the value of username and version parameters at runtime you can use the new indexer that allows to pass an object for with these values as following:\n\n  \n\n```csharp\n// Without Source Generator\n_language[\"Welcome\", new\n\n{\n\nUsername = \"aksoftware98\",\n\nVersion = \"v4.0\"\n\n}]\n\n\n// With Source Generator\nKeysAccessor.Welcome(\"aksoftware98\", \"v4.0\")\n```\n\n  \n\n## Check the Sample Folder\n\nCheck the sample project here to see how to develop a full Blazor WebAssembly project with storing the last selected language with more than 8 languages available for one UI:\n\n[Full Blazor WASM Sample](https://github.com/aksoftware98/multilanguages/tree/master/src/BlazorAKLocalization)\n\n# Upcoming in Version 6.1\nWe are currently working on version 6.  Here are the upcoming features.\nVerify All Keys Can Be Found](#verify-all-keys-can-be-found)\n* [Verify No Unused Keys](#verify-no-unused-keys)\n* [Verify No Duplicate Keys](#verify-no-duplicate-keys)\n* [Data Attribute Localization Validation](#data-attribute-localization-validation)\n\n## Specify the assembly by name\nIf you have multiple projects in your Visual Studio Solution that depend upon language translation, as of version 6.0 and higher you can specify the assembly by name.  Place your resources in a project that can be used by the other projects in your Solution.\n\nExample Usage\n```C#\nstring assemblyName = \"MyCompany.MyProject.Common\";\nEmbeddedResourceKeysProvider keysProvider = new EmbeddedResourceKeysProvider(assemblyName, \"Resources\");\nLanguageContainer service = new LanguageContainer(CultureInfo.GetCultureInfo(\"en-US\"), keysProvider);\n```\n\n## Get all the keys for the current culture\n\n```C#\nList\u003cstring\u003e keys = _language.GetKeys();\n```\n\n## Loop through the key values for the current culture.\n\n```C#\nforeach (KeyValuePair\u003cobject, object\u003e keyValue in _service.Keys)\n{\n\tConsole.WriteLine($\"{keyValue.Key}: {keyValue.Value}\");\n}\n```\n\n## Get all the registered languages\n\n```C#\nIEnumerable\u003cCultureInfo\u003e registeredLanguages = _language.RegisteredLanguages;\n```\n\nFull example with a drop-down that is bound to the languages.\n\n```C#\n@page \"/\"\n@using System.Globalization\n\n\u003cselect @bind=\"SelectedCulture\" @bind:event=\"onchange\"\u003e\n    @foreach (var culture in Cultures)\n    {\n        \u003coption value=\"@culture.Name\"\u003e@culture.EnglishName\u003c/option\u003e\n    }\n\u003c/select\u003e\n\n@code {\n    [Inject] private ILanguageContainerService _language { get; set; }\n\t\n    public IEnumerable\u003cCultureInfo\u003e Cultures { get; set; } = new List\u003cCultureInfo\u003e();\n    \n    private string _selectedCulture;\n    public string SelectedCulture\n    {\n        get =\u003e _selectedCulture;\n        set\n        {\n            if (_selectedCulture != value)\n            {\n                _selectedCulture = value;\n                _language.SetLanguage(CultureInfo.GetCultureInfo(value));\n            }\n        }\n    }\n\n    protected override void OnInitialized()\n    {\n        _language.InitLocalizedComponent(this);\n\t\t\n        // Initialize the Cultures list here if not already populated\n        if (Cultures.Count == 0)\n        {\n            Cultures = _language.GetRegisteredLanguages();\n        }\n\n        // Set initial selected culture\n        _selectedCulture = _language.CurrentCulture.Name;\n    }\n}\n```\n## Use an Enum as a translation key\nThe name of the enum will be used as the key.  If there is a Description attribute, the Description will be used as the key. Note, as of Version 6.0 and higher, the library now has the ability to generate a LanguageKeys Enum file.  \n\nExample Enum\n```C#\n\tpublic enum LanguageKeys\n\t{\n\t\t[Description(\"HomePage:Title\")]\n\t\tHomePageTitle,\n\t\tFirstName\n\t}\n```\n\nExample Usage\n``` Razor\n\u003ch1\u003e@languageContainer.Keys[LanguageKeys.HomePageTitle]\u003c/h1\u003e\n```\n\n\n## Generate a Static Constants Keys File\nWe are currently working on a CLI but you can also create a static constants file using this method.\n\n```csharp\nvar keysProvider = new EmbeddedResourceKeysProvider(Assembly.GetExecutingAssembly());\nvar languageContainer = new LanguageContainer(CultureInfo.GetCultureInfo(\"en-US\"), keysProvider);\nvar createCodeLogic = new CreateCodeLogic(languageContainer);\nstring namespace = \"MyCompany.Project\";\nstring className = \"LanguageKeys\";\nstring filePath = @\"c:\\somedirectory\\LanguageKeys.cs\"\ncreateCodeLogic.CreateStaticConstantsKeysFile(namespaceName, className, filePath);\n```\n\nThis will produce a file like this.\n\n```C#\n//------------------------------------------------------------------------------\n// \u003cauto-generated\u003e\n//     This code was generated by a tool.\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n//      \n//     For more information see: https://github.com/aksoftware98/multilanguages\n// \u003c/auto-generated\u003e\n//------------------------------------------------------------------------------\nnamespace MyCompany.Project\n{\n\tpublic static class LanguageKeys\n\t{\n\t    public const HomePageTitle = \"HomePage:Title\";\n\t\tpublic const FirstName = \"FirstName\";\n\t}\n}\n```\n\nHere is an example of the usage.\n``` Razor\n\u003ch1\u003e@languageContainer.Keys[LanguageKeys.FirstName]\u003c/h1\u003e\n```\n\n## Generate an Enum Keys File\nWe are currently working on a CLI but you can also create an enum keys file using this method.\n\n```csharp\nvar keysProvider = new EmbeddedResourceKeysProvider(Assembly.GetExecutingAssembly());\nvar languageContainer = new LanguageContainer(CultureInfo.GetCultureInfo(\"en-US\"), keysProvider);\nvar codeGeneratorService= new GenerateStaticKeysService(languageContainer);\nstring namespace = \"MyCompany.Project\";\nstring enumName = \"LanguageKeys\";\nstring filePath = @\"c:\\somedirectory\\LanguageKeys.cs\"\ncodeGeneratorService.CreateEnumKeysFile(namespaceName, enumName, filePath);\n```\n\nThis will produce a file like this.\n\n```csharp\n//------------------------------------------------------------------------------\n// \u003cauto-generated\u003e\n//     This code was generated by a tool.\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n//      \n//     For more information see: https://github.com/aksoftware98/multilanguages\n// \u003c/auto-generated\u003e\n//------------------------------------------------------------------------------\nusing System.ComponentModel;\nnamespace MyCompany.Project\n{\n\tpublic enum LanguageKeys\n\t{\n        [Description(\"HomePage:Title\")]\n\t\tHomePageTitle,\n\t\tFirstName\n\t}\n}\n```\n\nHere is an example of the usage.\n``` Razor\n\u003ch1\u003e@languageContainer.Keys[LanguageKeys.FirstName]\u003c/h1\u003e\n```\n## Validation\nIn order to keep you project localized, there are several different tests that an be performed.  \n\n## Verify All Source Code Files Are Localized\nAs you are adding and changing Razor files in your your project, you can verify that all source code files have been localized. If the result is empty then everything has been localized.\n\nExample:\n\n```csharp\n/// \u003csummary\u003e\n/// If this test is failing it means that there are new strings in your razor file or in your model file Required Attribute that need to be localized.\n/// \u003c/summary\u003e\n[Fact]\npublic void VerifyAllSourceCodeFilesAreLocalized()\n{\n\t//Arrange\n\tParseParms parms = new ParseParms();\n\tstring solutionPath = TestHelper.GetSolutionPath();\n\tstring pagesPath = Path.Combine(solutionPath, \"BlazorServerLocalizationSample\", \"Pages\");\n\tstring sharedPath = Path.Combine(solutionPath, \"BlazorServerLocalizationSample\", \"Shared\");\n\tparms.SourceDirectories = new List\u003cstring\u003e { pagesPath, sharedPath };\n\tparms.WildcardPatterns = new List\u003cstring\u003e() { \"*.razor\" };\n\tparms.ExcludeDirectories = new List\u003cstring\u003e();\n\tparms.ExcludeFiles = new List\u003cstring\u003e();\n\tparms.ResourceFilePath = Path.Combine(solutionPath, \"BlazorServerLocalizationSample\", \"Resources\", \"en-US.yml\");\n\tparms.KeyReference = \"Language\";\n\n\t//Act   \n\tParseCodeLogic logic = new ParseCodeLogic();\n\tList\u003cParseResult\u003e parseResults = logic.GetLocalizableStrings(parms);\n\n\t//Assert\n\tif (parseResults.Any())\n\t{\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.AppendLine(\"Not all source code files are localized. See documentation here: https://github.com/aksoftware98/multilanguages\");\n\t\tforeach (var parseResult in parseResults)\n\t\t{\n\t\t\tsb.AppendLine($\"{Path.GetFileName(parseResult.FilePath)} | {parseResult.MatchValue} | {parseResult.LocalizableString}\");\n\t\t}\n\n\t\tAssert.Fail(sb.ToString());\n\t}\n}\n```\n\n## Verify All Keys Can Be Found\nYou can verify that there is not a typo in your Razor file for the localization key.  When the list is not blank there are typos.   \n\nExample:\n\n```csharp\n/// \u003csummary\u003e\n/// If this test is failing it means that you manually typed in a key in your razor file,\n/// and it does not exist in the en-US.yml file, or you deleted a key value pair in the en-Us.yml file that was in use.\n/// \u003c/summary\u003e\n[Fact]\npublic void VerifyAllKeysCanBeFound()\n{\n\t//Arrange\n\tParseParms parms = new ParseParms();\n\tstring solutionPath = TestHelper.GetSolutionPath();\n\tstring pagesPath = Path.Combine(solutionPath, \"BlazorServerLocalizationSample\", \"Pages\");\n\tstring sharedPath = Path.Combine(solutionPath, \"BlazorServerLocalizationSample\", \"Shared\");\n\tparms.SourceDirectories = new List\u003cstring\u003e { pagesPath, sharedPath };\n\tparms.WildcardPatterns = new List\u003cstring\u003e() { \"*.razor\" };\n\tparms.ExcludeDirectories = new List\u003cstring\u003e();\n\tparms.ExcludeFiles = new List\u003cstring\u003e();\n\tparms.ResourceFilePath = Path.Combine(solutionPath, \"BlazorServerLocalizationSample\", \"Resources\", \"en-US.yml\");\n\tparms.KeyReference = \"Language\";\n\n\t//Act\n\tParseCodeLogic logic = new ParseCodeLogic();\n\tIEnumerable\u003cParseResult\u003e parseResults = logic.GetExistingLocalizedStrings(parms).Where(o =\u003e String.IsNullOrEmpty(o.LocalizableString));\n\n\t//Assert\n\tif (parseResults.Any())\n\t{\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.AppendLine(\"Not all keys can be found in the resource file.  See documentation here: https://github.com/aksoftware98/multilanguages\");\n\t\tforeach (var parseResult in parseResults)\n\t\t{\n\t\t\tsb.AppendLine($\"{parseResult.FilePath} | {parseResult.MatchValue}\");\n\t\t}\n\n\t\tAssert.Fail(sb.ToString());\n\t}\n}\n```\n\n## Verify No Unused Keys\nDetect if you have keys in your en-US.yml file that are not being used in your razor files.\n\nExample:\n\n```csharp\n/// \u003csummary\u003e\n/// If this test is failing, it means that you have keys in your en-US.yml file that are not being used in your razor files.\n/// \u003c/summary\u003e\n[Fact]\npublic void VerifyNoUnusedKeys()\n{\n\t//Arrange\n\tParseParms parms = new ParseParms();\n\tstring solutionPath = TestHelper.GetSolutionPath();\n\tstring pagesPath = Path.Combine(solutionPath, \"BlazorServerLocalizationSample\", \"Pages\");\n\tstring sharedPath = Path.Combine(solutionPath, \"BlazorServerLocalizationSample\", \"Shared\");\n\tparms.SourceDirectories = new List\u003cstring\u003e { pagesPath, sharedPath };\n\tparms.WildcardPatterns = new List\u003cstring\u003e() { \"*.razor\" };\n\tparms.ExcludeDirectories = new List\u003cstring\u003e();\n\tparms.ExcludeFiles = new List\u003cstring\u003e();\n\tparms.ResourceFilePath = Path.Combine(solutionPath, \"BlazorServerLocalizationSample\",\n\t\t\"Resources\", \"en-US.yml\");\n\tparms.KeyReference = \"Language\";\n\n\t//Act\n\tParseCodeLogic logic = new ParseCodeLogic();\n\tList\u003cstring\u003e unusedKeys = logic.GetUnusedKeys(parms);\n\n\t//Assert\n\tif (unusedKeys.Any())\n\t{\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.AppendLine(\n\t\t\t\"There are unused keys in the resource file.  See documentation here: https://github.com/aksoftware98/multilanguages\");\n\t\tforeach (var unusedKey in unusedKeys)\n\t\t{\n\t\t\tsb.AppendLine(unusedKey);\n\t\t}\n\n\t\tAssert.Fail(sb.ToString());\n\t}\n}\n```\n\n## Verify No Duplicate Keys\nIn this situation, there are strings that need to be localized but it would result in duplicate keys if automatically created.  You might need to manually create the key and values.\n\nExample\n```csharp\n/// \u003csummary\u003e\n/// If this test is failing it means that there are new strings that need to be localized\n/// and if they were to be created automatically, there would be the same key that have different values\n/// \u003c/summary\u003e\n[Fact]\npublic void VerifyNoDuplicateKeys()\n{\n\t//Arrange\n\tParseParms parms = new ParseParms();\n\tstring solutionPath = TestHelper.GetSolutionPath();\n\tstring pagesPath = Path.Combine(solutionPath, \"BlazorServerLocalizationSample\", \"Pages\");\n\tstring sharedPath = Path.Combine(solutionPath, \"BlazorServerLocalizationSample\", \"Shared\");\n\tparms.SourceDirectories = new List\u003cstring\u003e { pagesPath, sharedPath };\n\tparms.WildcardPatterns = new List\u003cstring\u003e() { \"*.razor\" };\n\tparms.ExcludeDirectories = new List\u003cstring\u003e();\n\tparms.ExcludeFiles = new List\u003cstring\u003e();\n\tparms.ResourceFilePath = Path.Combine(solutionPath, \"BlazorServerLocalizationSample\",\n\t\t\"Resources\", \"en-US.yml\");\n\tparms.KeyReference = \"Language\";\n\n\t//Act   \n\tParseCodeLogic logic = new ParseCodeLogic();\n\tDictionary\u003cstring, List\u003cstring\u003e\u003e failedKeys = logic.GetDuplicateKeys(parms);\n\n\t//Assert\n\tif (failedKeys.Any())\n\t{\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.AppendLine(\n\t\t\t\"Missing localized values would have duplicate keys.  See documentation here: https://github.com/aksoftware98/multilanguages\");\n\t\tforeach (var failedKey in failedKeys)\n\t\t{\n\t\t\tforeach (var item in failedKey.Value)\n\t\t\t{\n\t\t\t\tsb.AppendLine($\"{failedKey.Key} : {item}\");\n\t\t\t}\n\t\t}\n\n\t\tAssert.Fail(sb.ToString());\n\t}\n}\n```\n\n## Create or Update Resource File from Localizable Strings\nInstead of manually creating all the key value pairs, you can parse your .razor, HTML, etc files and create your en-US.yml file\n\nExample\n```C#\npublic void CreateOrUpdateResourceFileFromLocalizableStringsExample()\n{\n\tParseParms parms = new ParseParms();\n\tstring solutionPath = TestHelper.GetSolutionPath();\n\tstring componentsPath = Path.Combine(solutionPath, \"BedBrigade.Client\", \"Components\");\n\tstring modelPath = Path.Combine(solutionPath, \"BedBrigade.Common\", \"Models\");\n\tparms.SourceDirectories = new List\u003cstring\u003e() { componentsPath, modelPath };\n\tparms.WildcardPatterns = new List\u003cstring\u003e() { \"*.razor\", \"*.cs\" };\n\tparms.ExcludeDirectories = new List\u003cstring\u003e();\n\tparms.ExcludeFiles = new List\u003cstring\u003e();\n\tparms.ResourceFilePath = Path.Combine(TestHelper.GetSolutionPath(), \"BedBrigade.Client\", \"Resources\", \"en-US.yml\");\n\tCreateCodeLogic logic = new CreateCodeLogic();\n\tlogic.CreateOrUpdateResourceFileFromLocalizableStrings(parms);\n}\n```\n## Replace Localizable Strings With Variables\nInstead of manually searching and replacing all of the hard coded localizable strings in your UI code, you can automatically create a resource file and then replace them with the keys.\n\nExample\n```C#\npublic void ReplaceLocalizableStringsWithVariablesExample()\n{\n\tParseParms parms = new ParseParms();\n\tstring solutionPath = TestHelper.GetSolutionPath();\n\tstring componentsPath = Path.Combine(solutionPath, \"BedBrigade.Client\", \"Components\");\n\tstring modelPath = Path.Combine(solutionPath, \"BedBrigade.Common\", \"Models\");\n\tparms.SourceDirectories = new List\u003cstring\u003e() { componentsPath, modelPath };\n\tparms.WildcardPatterns = new List\u003cstring\u003e() { \"*.razor\", \"*.cs\" };\n\tparms.ExcludeDirectories = new List\u003cstring\u003e();\n\tparms.ExcludeFiles = new List\u003cstring\u003e();\n\tparms.ResourceFilePath = Path.Combine(TestHelper.GetSolutionPath(), \"BedBrigade.Client\", \"Resources\", \"en-US.yml\");\n\tCreateCodeLogic logic = new CreateCodeLogic();\n\tlogic.ReplaceLocalizableStringsWithVariables(parms);\n}\n```\n\n## Data Attribute Localization Validation\nIn order to localize validation messages, use the Create or Update [Resource File from Localizable Strings](#create-or-update-resource-file-from-localizable-strings) feature.  Below is a full Blazor example using a Contact Us Page.\n\n```C#\npublic partial class ContactUsPage : ComponentBase\n{\n\t[Inject] private ILanguageContainerService Language { get; set; }\n\tprivate ContactUs _contactUs;\n\tprivate ValidationMessageStore _validationMessageStore;\n\tprivate EditContext? EC { get; set; }\n\t\n\tprotected override void OnInitialized()\n\t{\n\t\tLanguage.InitLocalizedComponent(this);\n\t\t_contactUs = new Common.Models.ContactUs();\n\t\tEC = new EditContext(_contactUs);\n\t\t_validationMessageStore = new ValidationMessageStore(EC);\n\t}\n\t\n\tprivate ClearValidationMessages()\n\t{\n\t\t_validationMessageStore.Clear();\n\t}\n\t\n\tprivate bool IsValid()\n        {\n\t\tClearValidationMessages();\n\t\tbool isValid = ValidationLocalization.ValidateModel(_contactUs, _validationMessageStore, Language);\n\t\tif (!isValid)\n\t\t{\n\t\t\tEC.NotifyValidationStateChanged();\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\treturn true;\n\t}\n\t\n\tprivate void HandleSubmitClick()\n\t{\n\t\tif (!IsValid())\n\t\t\treturn;\n\t\t\t\n\t\t//Handle Form Submission\n\t}\n}\n```\n\n# Thanks for the awesome contributors\n\n\u003ca  href=\"https://github.com/aksoftware98/multilanguages/graphs/contributors\"\u003e\n\n\u003cimg  src=\"https://contrib.rocks/image?repo=aksoftware98/multilanguages\"  /\u003e\n\n\u003c/a\u003e\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faksoftware98%2Fmultilanguages","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faksoftware98%2Fmultilanguages","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faksoftware98%2Fmultilanguages/lists"}