DEV Community

jsakamoto
jsakamoto

Posted on • Edited on

How to localize texts in your client-side Blazor WebAssembly App?

Introduction: What's "Blazor" (client-side)?

"Blazor" (client-side) is a Single Page Web Application (a.k.a "SPA") framework.

Client-side Blazor allows you to run C# code and .NET Standard 2.0 native assembly (.dll) on a top of modern web browser's WebAssembly engine, without any .NET server processes.

If you are new to Blazor, I recommend watching this YouTube video.

NDC Oslo 2017 - "Web Apps can’t really do that, can they? - Steve Sanderson"

The video was about prototype of Blazor, and the current version when I wrote this post was v.0.9.0, which had some breaking changes. But that video is useful to understanding the concept and pros/cons of "Blazor".

Localization of client-side Blazor WebAssembly App

Scope of this article

This article will only discuss text localization scenarios.

Date, time, and currency formats are outside the scope of this article.

And also, server-side Blazor is out of this article, too.
[2019/11/09] "Blazor I18n Text" Library ver.7 started support Server-Side Blazor! 🎉

Are there already any solutions for text localization?

Yes, we can find great solutions and articles about it on the internet.

For example:

However, I couldn't be satisfied with the above solutions, because those solutions have consideration points below.

The 4 reasons why I wasn't satisfied with the above solutions

1. It requires Server-Side implementation.

Blazor is a client-side solution, therefore, a Blazor app can deploy on static contents only HTTP server, like GitHub pages.

However, the above localization solutions require a server side ASP.NET Core implementation, and of course it requires Web server which can run ASP.NET Core.

2. It requires .resx editor.

.resx file is a just an XML format file, and it is popular to be used in localization scenario in general C# programming.

However, to edit .resx file, you will need Visual Studio. It's hard work that editing .resx file with a text editor, I think.

3. It requires key access by "string".

A common text localization solution works as getting localized text from a "key".

In the above solutions, you must describe key by string in C#/Razor source code.

Writing a key as string often can be causing miss typing, and that miss can't detect at compile time.

It also can't get the help of the code completion feature of an editor. (ex. "IntelliSense")

I really want to get static typing solutions rather than late binding solutions such as key accessing by string

4. It doesn't consider Blazor components libraries.

Blazor support creating and reusing component library as NuGet package.

However, the above solutions couldn't resolve the localization texts in Blazor component library package.

I decided to make a new solution.

As mentioned above, I couldn't be satisfied any solutions which I found on the internet, therefore, I decided to make a new solution.

My approach is...

The most important point of my approach is, not only writing library code but writing build script for processing some tasks (such as generate static typing files, generate localized text files as static contents) at build time.

  • Use the simple JSON/CSV format instead of the .resx file format to writing the key-localized text corresponds table (I call it "localized text source file").
  • Generate static typed C# class (I call it as "text table class") source code files from "localized text source files" on build automatically.
    • "text table class" has fields associated with each key in "localized test source file".
  • And also generate JSON format files (I call it as "localized text resource JSON file") in under the "wwwroot" content folder from "localized text source files" on build automatically.
    • "localized text resource JSON files" are deployed as static content files on a Web server, and those are fetched from Blazor client process at run time.
  • Include an "msbuild" script that makes "localized text resource JSON files" to be contained in a NuGet package of Blazor component library.

... and, I did it!

The result of my work is here:

No .resx, simple JSON format, and also CSV format is available!

fig

It works on GitHub pages. (static contents only HTTP server)

movie.1

IntelliSense works well.

movie.2

How to use it?

Step.1 - Add "Toolbelt.Blazor.I18nText" Package

Add Toolbelt.Blazor.I18nText NuGet package to your Blazor app project.

If you are using dotnet CLI, you can do it by command line bellow.



$ dotnet add package Toolbelt.Blazor.I18nText


Enter fullscreen mode Exit fullscreen mode

You can also do it in Package Manager Console of Visual Studio, if you are using Visual Studio in Windows OS.



PM> Install-Package Toolbelt.Blazor.I18nText


Enter fullscreen mode Exit fullscreen mode

Step.2 - Create localized text source files as JSON or CSV

Add localized text source files for each language in a i18ntext folder under your Blazor app project folder.

The localized text source files must be simple key-value only JSON file like a bellow example,



{
  "Key1": "Localized text 1",
  "Key2": "Localized text 2",
  ...
}


Enter fullscreen mode Exit fullscreen mode

or, 2 column only CSV file without header row like a bellow example.



Key1,Localized text 1
Key2,Localized text 2


Enter fullscreen mode Exit fullscreen mode

NOTICE - The encoding of CSV file must be UTF-8.

And, the naming rule of localized text source files must be bellow.



<Text Table Name>.<Language Code>.{json|csv}


Enter fullscreen mode Exit fullscreen mode

fig.1

Step.3 - Build the project always when localized text source files are created or updated.

After creating or updating those localized text source files, you have to build your Blazor app project.

After building the project, "Typed Text Table class" C# files will be generated in the i18ntext/@types folder, by the building process.

And also, "Localized Text Resource JSON" files will be generated at the wwwroot/content/i18ntext folder, too.

fig.2

NOTE - If you want to do this automatically whenever those localized text source files (.json or .csv) are changed, you can use dotnet watch command with following arguments.



$ dotnet watch msbuild -t:CompileI18nText


Enter fullscreen mode Exit fullscreen mode

After entry this dotnet CLI command, the dotnet CLI process keep running and watch the changing of localized text source files.
When the dotnet CLI which keep running detect the changing of localized text source files, the dotnet CLI re-compile localized text source files into "Typed Text Table class" files and "Localized Text Resource JSON" files.

fig.2-2

Step.4 - Configure your app to use I18nText service

Open the C# source file of the "Startup" class of your Blazor app in your editor, and add using clause for opening Toolbelt.Blazor.Extensions.DependencyInjection namespace, and add following code in ConfigureServices() method of the startup class.



services.AddI18nText<Startup>();


Enter fullscreen mode Exit fullscreen mode

fig.3

Step.5 - Get the "Text Table" object in your Blazor component

Open your Blazor component file (.cshtml) in your editor, and do this:

  1. Inject Toolbelt.Blazor.I18nText.I18nText service into the component.


@inject Toolbelt.Blazor.I18nText.I18nText I18nText


Enter fullscreen mode Exit fullscreen mode
  1. Add a filed of the Text Table class generated from localized text source files, and assign the default instance.


@functions {

    I18nText.MyText MyText = new I18nText.MyText();


Enter fullscreen mode Exit fullscreen mode

NOTE - The namespace of the Text Table class is <default namespace of your Blazor project> + "I18nText".

  1. Override OnInitAsync() method of the Blazor component, and assign a Text Table object that's a return value of GetTextTableAsync<T>() method of I18nText service instance to the Text Table field.


protected override async Task OnInitAsync()
{
  MyText = await I18nText.GetTextTableAsync<I18nText.MyText>(this);


Enter fullscreen mode Exit fullscreen mode

fig.4

Step.6 - Use the Text Table

After doing the these steps, you can reference a field of the Text Table object to get localized text.

If you are using Visual Studio in Windows OS and Blazor extensions is installed in that Visual Studio, you can get "IntelliSense" and "Document comment" support.

movie.2

Note: Text Table object allows you to get localized text by key string dynamically, with indexer syntax, like this.



<h1>@MyText["HelloWorld"]</h1>


Enter fullscreen mode Exit fullscreen mode

This way is sometimes called "late binding".

This feature is very useful in some cases.

However, if you make some mistakes that typo of key string, these mistakes will not be found at compile time.
In this case, it will just return the key string as is without any runtime exceptions.

Step.7 - Run it!

Build and run your Blazor app.

The I18nText service detects the language settings of the Web browser, and reads the localized text resource JSON which is most suitable for the language detected.

fig.5

Learn more

You can find more detail of this my work on GitHub.


Happy coding with Blazor :)

Top comments (29)

Collapse
 
5argon profile image
5argon • Edited

Hi, thanks for the article. Just to add that resx could also do code generation for static access point, if you prefer the resource file way. The resource file could be compiled to local dll sent to the client side and then you use ResourceManager or the static generated code to access them.

static

Collapse
 
j_sakamoto profile image
jsakamoto

Thank you for your comment!

Yes, that's right, .resx file could be generated C# class as you said.

I know this feature, and I often use this feature for ASP.NET MVC server programming.

However, .resx editor strongly depends on Visual Studio, and .resx solution is hard to use for static contents only HTTP server scenario, I think.

Anyway, your comment very important for the readers of this article.
Thanks!

Collapse
 
5argon profile image
5argon • Edited

In past few days I have been trying to make .resx localization works client side, because I love its generated class but unfortunately the generated class keeps falling back to the neutral language and not any other localized ones even with CultureInfo override specified.

I found that the main problem is that Blazor would include only main dll which contains your main localization resx. Other languages were separated by Visual Studio to a different satellite assembly and wouldn't be sent to client side, so it only works from server where all those dll are available.

What I did is to avoid naming the resx as AAA.resx, AAA.ja.resx but instead use AAA_ja.resx so Visual Studio don't see it as localized version and put them together in the main dll and ensure client get all languages on loading the SPA web app. Then I have to make a modified version of ResourceManager that knows how to look for _ separated resource according to incoming CultureInfo. It was very hacky but somehow I could get it to work. I have summarized the steps here if anyone interested in following : gametorrahod.com/workaround-for-cl...

The dependency of Visual Studio to edit .resx file still exists, but my website wouldn't be that large for now so I could put up with the difficulties :P Still I like that .resx could pack images into a binary which reads as byte[] from the generated static class, so I could make localized images bytes instead of localized image paths.

Collapse
 
taalkathiri profile image
Tarik Alkathiri

Thank you sooooo much for your solution
it is really helped me and works great with me
but when i use arabic character in the localization (I am using Json) the characters displayed as ��� ������.
Could you please help me in this.
I am sorry for asking too much but i am windows developer and completely new to Core 3 and Blazor Server-Side.

Collapse
 
j_sakamoto profile image
jsakamoto

Did you save the json file with utf-8 encoding?

If you upload the json file that can reproduce your problem to somewhere public cloud storage, then I can investigate it.

Collapse
 
taalkathiri profile image
Tarik Alkathiri • Edited

Dear jsakamoto
Thank you very much for your reply.
I just opened the json file in notepad and saved it as text with utf-8 encoding
then i renamed it in the solution to .json and it works fine and shows the arabic character.
Thank you very much for your effort, and sorry for troubling you.
P.S. Do I have to do that with every json file or there is a better way.

Thread Thread
 
j_sakamoto profile image
jsakamoto

P.S. Do I have to do that with every json file or there is a better way.

How about using Visual Studio Code for?

Thread Thread
 
taalkathiri profile image
Tarik Alkathiri

Thank you very much.
The Visual Studio Code did the job well, it shows the right characters.

Collapse
 
parad74 profile image
Marina Reva • Edited

jsakamoto, thank you so much. it is so easy to localize for blazor.

I want to add a little. If language suggests right-to-left. For example Hebrew. To Blazar-Clien need

1) copy site.css => site-rtl.css

2) in site-rtl.css add in app 2 line
app {
...
text-align: right;
direction: rtl;
}

3) MainLayout.razor start so

@inherits LayoutComponentBase
@inject Toolbelt.Blazor.I18nText.I18nText I18nText

@if (CurrentLang == "he")
{
<link href="css/site-rtl.css"rel="stylesheet"/>
}
else
{
<link href="css/site.css" rel="stylesheet"/>
}

4) in index.html - delete
<link href="css/site.css" rel="stylesheet" />

5) "he" - in my case Hebrew -language suggests right-to-left GUI

Collapse
 
taalkathiri profile image
Tarik Alkathiri

Thank you very much for your this addition
Can you please post or send a small example showing how to change the language on at run time from within a Blazer server-side component, because i could not understand how to wire it with the API.
Thank you very much.

Collapse
 
peastew profile image
PeaStew

"Build" doesn't generate anything at all, is this compatible with preview 8?

Collapse
 
j_sakamoto profile image
jsakamoto

If you use "Toolbelt.Blazor.I18nText" ver.5.0.0 (nuget.org/packages/Toolbelt.Blazor...), it should be compatible with Client-Side Blazor Wasm App v.3.0.0-preview8.

Please see also: github.com/jsakamoto/Toolbelt.Blaz...

If it looks like bug of "Toolbelt.Blazor.I18nText", could you provide your source code & project files to me?

Collapse
 
peastew profile image
PeaStew

Was my mistake I hadn't seen it was client side only, looking for a server side solution now, I assume basically anything that is MVC core compatible will work, thanks for the library, I've bookmarked it for future client side only work.

Thread Thread
 
j_sakamoto profile image
jsakamoto

Thank you for your reply!

"Toolbelt.Blazor.I18nText" doesn't work on server side at this time, however, make "Toolbelt.Blazor.I18nText" to be available on server side is my important concern.

Because, I think, if "Toolbelt.Blazor.I18next" become working on server-side fine, it will have some advantages bellow.

  • It is not required .resx editor
  • It will work fine both client-side and server-side, on same implementation.

I'll try to improve it.

Thanks!

Thread Thread
 
j_sakamoto profile image
jsakamoto

P.S. "Toolbelt.Blazor.I18next" ver.7 started support for Server-side Blazor.

github.com/jsakamoto/Toolbelt.Blaz...

Collapse
 
taalkathiri profile image
Tarik Alkathiri

Dear sir,
Thank you very much for this great solution.
If you please post a small example on how to use the api's with this solution will be very appreciated.
Again thank you very much for that.

Collapse
 
j_sakamoto profile image
jsakamoto

Is the sample code bellow helpful for you?

github.com/jsakamoto/Toolbelt.Blaz...

The sample code above describes how to retrieve the current language, and how to change the current language.

Collapse
 
taalkathiri profile image
Tarik Alkathiri • Edited

Thank you sooooo much for your reply
it is really helped me and works great with me
but when i use arabic character in the localization (I am using Json) the characters displayed as ��� ������.
Could you please help me in this.
I am sorry for asking too much but i am windows developer and completely new to Core 3 and Blazor Server-Side.

Collapse
 
krchome profile image
Kaushik Roy Chowdhury

Hi @j_sakamoto ,
Thanks for the package and the article.
It will be great if you could give your valued inputs on the following issues I faced while trying the package as below:

I am using Visual Studio Community 2019 and ASP.Net Core 3.1 with Blazor templates. As suggested this package would work with the Blazor WASM app, but where do you register your service as there is no startup class in WASM(without ASP.Net Core hosted)
Also when I followed all the steps ditto (Blazor Server Project with Startup class), the project did not generate "Localized Text Resource JSON" files at the wwwroot/content/i18ntext folder.
In fact, no content folder was generated under wwwroot. Do you have any idea?

Anyway, when I went ahead despite the above issues and completed the rest of the steps and ran the application, it went to the fall-back "English" language.

It may be due to the absence of local text resource json files, but that's another point.
Can you help please?

Collapse
 
fededim profile image
Federico Di Marco

Just a question about step 4. in a Blazor WebAssembly project there is no startup class how can I add your i18nText service ?

Collapse
 
j_sakamoto profile image
jsakamoto

I'm sorry too late.
You can add i18ntext service in your Program.cs.

Please see also "(If) Your project is Blazor WebAssembly v.3.2+, you should edit Program class to do this" section in Setp.4, the README document on the GitHub repository.

Collapse
 
fededim profile image
Federico Di Marco

Nevermind, how is written in your GitHub project page github.com/jsakamoto/Toolbelt.Blaz...

Collapse
 
taalkathiri profile image
Tarik Alkathiri

Mr.jsakamoto
Thank you very much for this excellent project.

are you planning to Localize Data Annotation and validation message in the near future?
Thank you in advance for your effort.

Collapse
 
j_sakamoto profile image
jsakamoto

Yes, I am.

Please see: github.com/jsakamoto/Toolbelt.Blaz...

Collapse
 
taalkathiri profile image
Tarik Alkathiri

Thank you So much.

Collapse
 
paulstringerdb profile image
paulstringerdb • Edited

Great utility, but please update the generated code to use the following comment line so it plays nicely with code analysis.

// <auto-generated />

Collapse
 
fabianus76 profile image
fabianus76

Hey Jsakamoto, great work !
What about translation of ValidationAttribute.ErrorMessage ?

Best regards,
Fabianus

Collapse
 
j_sakamoto profile image
jsakamoto

Sorry too late!
Yes, you can do it with combinations NuGet packages below.

Unfortunately, there are no good documents at this time.

Could you try to figure out the sample program below?

github.com/jsakamoto/Toolbelt.Blaz...

P.S.
Blazor WebAssembly looks like it will be started to official support for localization.

See also: youtu.be/RsPXkgOL2gI?t=3010

Please keep watching it too!