Bonjour. Hola. Hallo and welcome to
this video, you've guessed it, about localizing your .NET MAUI
localization. There can be a lot of reasons why you
want to localize your .NET MAUI app. Maybe it's because you want to be
more inclusive so that when someone doesn't speak a foreign
language, they can still use your app in their own language. Or maybe you just
want to increase the reach of your app, right? Because people are more likely
to maybe download the app if it's also available in Dutch or
Spanish or French. So that can definitely be a couple of reasons.
But whatever your reason is, it is, fortunately for us, relatively
easy to do and it's not that different from
localizing any other .NET projects. So let's just dive into Visual Studio
and see what it's all about. I'm going to show you kind of like three ways to
start with localization and each one is going to be gradually
easier or a nicer solution. Well, let's just find out.
So here I have a File, New .NET MAUI application. You can see
it here running in Visual Studio 2022. The XAML is here on the background and
here in the foreground you can see it running on Android. I'm going to
show you on Android today, but this all works on all the platforms. So
this is just your typical .NET MAUI application. I'm
going to stop running here for a little bit and let's first what you
first want to do is add a resource file, which is kind of like the heart, the core of this
localization stuff. So this is something that you can
already know from other net projects because this is something
that exists in every dotnet project basically. So let's go over to
our Solution Explorer. And here under Resources, I already created a folder
that's called Languages. You can also call it localization if
you want. You can put it in a different spot, whatever you want, but
just put it here. I named it Languages and what
I'm going to do here is say add new item and then in this new item
dialogue, then you want to find the resources.
So let's find a resource. I'm going to look for it
here because I don't know in which category it is. Resources file.
There we are. So here you can name it whatever you want. You can
even divide your resources up per section of your app if that's what you want
that's more advanced stuff, I'm just going to focus on the basics for now. So
let's just call this AppResources. Let's call it something like that. So
now we get a ResX file and it's going to automatically open
the graphical editor in which you can do a couple of things. And
while this is loading, it's good to know how this actually works. So this
ResX file, you're going to enter data here and Visual Studio, or rather
the compiler is going to compile that into code for
you. So you can just have code references that you can use for these
resources but we'll see that in a little bit. So
this is kind of like your key value pair dictionary that you're
going to have here and that's it. So that's the way
it works in all your resources you're going to
have the same keys. So this is going to be my neutral one. So whenever a
person has not specified a language or the language they
specified is not available then it's going to fall back to this
resource which is typically probably going to be English. And then you can add
actual additional resources for Dutch, for Spanish, for French,
for whatever you want. And then you are going to specify the
same key names in all of those resource files but with
different values, namely the localized values. Right? So
that's how this is going to work. So let's start with a HelloWorld
thing here and I'm going to give it the value of
Hello, World! So this is our HelloWorld key and
value. So if I now do this I can save this. I can
go back to my main page example. We actually have a label
here which has the text Hello, World! So let's just
replace that with a static. You can see here I have Static
or I can use x:Static. I think both are the same thing. So
let's just use this one and I can here now find my AppResources. It's probably not updated yet. App
resources. Oh no, I know what I would need to do.
Right, so I have this one but what I need to do here is
this AppResources is it's being generated as code? So this
is a new class basically and I need to add an XML namespace for it.
So let's add this xmlns and I'm going to call it lang for
language. Or you can make a localization again whatever
you want and I'm going to make this app. Does it know about
it already? No, not yet. So what does it need to be
here? The languages. So I'm going to add the clr-namespace. IntelliSense is not really helping me
right now. This is the MauiLocalizationSample. Oh here we
are. MauiLocalizationSample.Resources. Languages. That's the one that we want
to have. So now I can use this lang and whenever I put the lang in
here it's going to find that AppResources, still
nothing... Oh here we go. Now it starts kicking
in, AppResources. HelloWorld. You can also get the
culture from it or the resource manager. But I want to have the actual key of
hello world. So now that I get that in place I can
run it on Android and we shouldn't see any difference right?
We should still see hello world in this string which is really cool. But if I
now add a separate resource file next to this and we're
going to do that in a minute and I'm going to put the
Dutch Hello, world in there, which is hello world,
and I'm going to set the device to Dutch. Then
it's automatically going to pick up that Dutch resources file and it's
going to show me this thing in Dutch. So here we go, see,
still hello world. Okay, so we got this far. This is great.
It's still working. We didn't break anything yet. So we got that one. Now let's see
how we can add the Dutch one. So I'm going to go back
to my solution Explorer and the languages and I can
add another resource file here. So I'm going to do add new item
and it still selected the resources file, so
that's good. And the trick is to name it the same, right? So this
works based on naming convention. So I want to name this app resources NL ResX. You can include kind of like the
local Identifier. So you can also do NLNL or what is it? Enus. So you have these differences
based on like a region, right? You can also
have en, I don't know, GB for British Dutch, so you can
definitely include that as well. You have these
kind of like things. But if you just want to do it on the global scale,
which is typically what I've done, mostly you just want to do NL or Es
for Spanish or F air fr, that was a little bit of Dutch
fr for French. So you can just do that.
I'm going to stick with NL because that's what I know.
Apparently I know Dutch. And here the important thing, is that
you take this same key, right? So I take this hello world thing, I go back
to my resources NL, I put hello world in here as well. But
now the value is going to be different. So it's going to be
hello vehicle, right? So it's a slight difference in Dutch, but
you'll get the hang of it. So whenever I'm going to do this,
because this is also the downside, I need to go into my Android device. Actually, let's just load it up again.
And what is going to happen because this is static resources, right? It's
not going to actually show the difference right now because
firstly, this device is not set to Dutch. But even if I
would change the culture at Runtime, so if I would go
here into my settings app for Android and I would set this to Dutch,
I already installed it, I already set it up and if I switch this to the
top one, then you can see the whole interface is going to
Dutch. But now when I go back to my application, you would
expect that it also flips, right? But that doesn't happen. It still says
hello world. And now I need to kind of stop the application. I need
to run it again. It was deploying very fast, so I kind of doubt that it
picked up on my new Dutch resources file, but we'll see.
But then it should be translated in Dutch, right? It's going
to pick up my resources NL ResX. And it's going to show you that here.
Well, actually it did. So here you can see hello wheels, right? So this
is my Dutch localization. So the downside to this is that you
have to restart your application. So that brings us to method number
two. We can do better than this, right? We want to make this show
up dynamically. So let's go over to that. And for
that, I'm going to stop running this code for a little bit,
and I'm going to close the resources files.
Actually, we don't really need those anymore. What I do, before we actually
leave them, what you can do is you can also add
images in here. So you can add images, you
can add text files, you can add multiple things here. So
definitely that's something that you might want to do if you also want
to localize your images. What you can also of course do is save
paths to images from here, right? So you can just have the
same key in here with your image key. And then the value can
be a localized image that lives inside of your application, right? So
there's a different path that you can do here. So that's that. Now what we
can do is we can go and implement our
localization resource manager. So I've already set
that up here. I've added the class, which is just right click on your
project, add new class, and you can add this code. So let me
uncomment this. And now we have a localization
resource manager which implements the I notify property
changed. Now, if you know a little bit about I notified property
changed, let me format this properly. No, I can do that. All right, then
I'll leave it like this. And if you know about the I notified
property change that is needed to update the UI whenever something changed,
right? So we're going to use data binding and we're going to update the UI
whenever something has changed here. So if we inspect what this does, we
have this new localization resource manager. We're going to set
the app resources, which is my app resources, the
resource file that I created, right? We're going to set the culture
to the current culture. We can get an instance, a static instance, so
that we can reference it from everywhere without having to create
new localization resource managers. And this is kind of a funny thing,
right? So this means this is kind of like I don't know the formal name
for it, but we can say this. And then we can say string resource
key. And what enables this enables us to do is say then localization
Resource Manager instance and we can specify Identifier the
index as a string and then we will get the
value out of it, right? So we'll see how to use that in a
little bit. But this is like a little bit of syntactic sugar so
that it looks a little bit nicer. And then we have the plumbing for our
property changed, right? So whenever our property changed then we're going to
have this event handler so that the UI, the XAML
can hook into this and will automatically update the UI with our localized UI
things. And here we have a thing for setting the
culture, right? So we're going to set the new culture because if we want to
switch languages, we want to set the culture and we're going to raise that property
changed. And whenever you do property changed event arcs with a null then
it's going to update it for all the properties in there. So it's going to
go through our whole interface and update all the things. Now of course there's
a couple of ways that how you can use this. The most obvious thing is in
XAML. That's probably where the most of your localization is going to happen.
So let's go back to our main page XAML and I don't know, let's just copy this label again and I'm going to show
you the Hello World in a couple of different things, right? So
let me just do this here and this is not going to be the X
static but this is going to be a binding to my localization Resource Manager. Localization
Resource Manager and I'm going to here put the string in there. So this is going to
be Hello World as well. And this looks a bit funny, right?
This looks a bit different than the XAML syntax that you probably know from
earlier and then you can save mode one way, right? So
just to be sure that we're going to do it one way that we
don't have any overhead there with the performance and whatnot. So now
this label is also going to show it but now through the
localization manager and we want to see it actually switch and runtime, right? So let's
take this button right here. And this already has a counter event here in
the background. So let's just add to this a little bit
and let me bring in some code to actually do
that. So here what I want to do is check if our culture is actually the one that is Dutch and if it is, then we're going to set it
to English and whenever it's English then we're going to set it to Dutch,
right? So this is a little toggle to switch between them. We're going to
use IntelliSense to add the right using here using Maui
localization sample Resources languages. This app resources is again
a thing that is generated by my ResX files and this culture info
is a built in.NET thing for the system globalization. So
import that as well. And then we're going to set our
localization resource manager instance, set culture to the one that
we switched it to, right? And now because of the I notify
property change, it's going to switch back and forth between the
translations. Now the weird kind of like operator here with
this, this, that's exactly what you see here at play, right? So we're going to
set this localization resource manager with these brackets here. And this is
going to be the key that you actually want to see. But this is an
all example. What we obviously also want to do is like here you can see the
example actually in this counter button. You're going to have to set this in
like the code behind, right? So how are we going to do that? Well,
actually I'm not going to do the clicks so many times. I'm just
going to do that once. So actually, let me add a new
thing in the resource files for this so that you can also see how to
work with this count format right here. So let's go back to this
one and we're going to say the counter. So let's
just name the key. We're going to name that counter and this is the English
one. So let's just take the English text
from here. And I'm not going to worry about the time
versus times. You can definitely implement that, of course by specifying a
different key, but not going to do that for now. So let's just input
that here. And instead of the count, you're going to do zero here, right?
So it's going to set zero. And if you have multiple, then it can
be zero, one, two, et cetera. We'll see that in a minute. So and for
the Dutch one, let's just do this one. I'm
going to open the Dutch one and I'm going to also have to add here
counter. And I'm going to have to say, well, I'm starting with this one
clicked. That's the Dutch word for something
has been clicked, right? So let's just do that and we have this. So let's just go back to
our main page example, CS. And then whenever this is
happening, then we're going to say our counterbutton text is going to be our localization localization resource manager. And here we can again, no, we
need the instance, sorry, the instance. And here we can again then specify
that Identifier, right, that key, that index, that is
again, that trick that I showed you with the resource manager,
that weird kind of looking thing. And now I can say here counter, right?
So it's going to get that counter and we have that zero still in there
and we're going to have to do the two string, right, because there
can be images and whatnot in there. So we have to know that a string is
coming out of there and we're going to do the two string. But we
have to do a string dot format to actually replace that value, right?
So we're going to say count and we're going to do it like
this. And if you have like 00:12, et cetera, then you're going to have
to put in more parameters here, right? And it's going to match
up to the index that you put in here. But now we have just one, so
it's going to be count and then whenever I run this now there's two
things going to happen. The first label that we still have
here is going to be the static one, so that's not going to change. The
second one is going to change whenever I toggle the button, right? And then
this button is going to do the same, but now it's going to get the actual
count in there. So we have a little variable in there. And also
this is getting it through code rather than through XAML, right? So
let's see what is going on here. We have. Hello, world. The
other one is not working and actually things are crashing. So
that's interesting. Why is this happening? Let's see. So we have this app Resources culture
and the culture is actually nothing. So I forgot something, I
guess. And that's probably because I forgot to set the yeah, I forgot a
very important thing. I forgot to set the binding context
here to this because we are using that
binding, right? It's using that binding right here, binding Localization Resource
Manager and I actually need to have that property with it's
not going to understand what I'm trying to do
automatically. It can't read my mind, unfortunately. And I'm going to have
to add this property for the Localization Resource Manager, which
is going to point to a Localization Resource Manager instance, right? So
now I can also reuse this one. Basically, I could use this
and use it as the thing here automatically
instead of having that instance again. It's going to be
the same thing, but now we have our syntax a little bit easier. So now
this main example is going to go to our property and we can
use this index again, but it's going to look at this thing.
So it's going to point at this thing and now we can do our thing. So
let's try this again actually. And now it should hopefully actually work, else I'm going to try. So let's see what's happening here. We
should actually see the two hello wheedles. There we go. So
our binding is working and now whenever I click the button you can
see it flips around. Now it's Hello World and we have clicked one time and if I
click it again it's going to be hollow. Whittle. And two clicked
right over clicked. So now it's switching around dynamically. And of course this
is just a button, but imagine that this is your preferences page
inside of your application and you have a picker for all the
languages that you support and you pick the language and suddenly your whole UI
updates, right? So that's what is going on here. Now I promised you a
third way to do this because this is going to be a lot of localization
resource managers everywhere in properties and you can wrap it of
course in a base view model if that's what you want. So a lot is going on
here, but you can make it even easier with an XAML markup
extension. So let's stop running this again and go back to my
solution Explorer. And I have this translate extension. So
let's just uncommon this and this is pretty cool. So what
we are doing is actually if you look at
this main page, XAML, this binding thing is actually a
markup extension. It's just a class that is
executed whenever the XAML parser reads through this and it
sees that binding and it picks up on all the rest. You can write those
things yourself as well. So we can create a translate extension to make this all
easier. So that's exactly what I've done here.
We have this translate extension which is implementing the I markup extension
of a binding base. So there we have it, that
binding for the data binding so that we can update dynamically. We can put a
name in here so that we have the name for
that key, right? That's going to be the key that we're putting in here.
And then we're going to have to provide a value. So whenever
this translate extension is called upon, this is what we're going to do
to actually provide a value for whatever is supposed to be
filled in at that place in the example, right? It
sounds kind of abstract but hopefully it will click in a minute here. So we're
going to return a new binding, which is basically what you're doing
with data binding. We're going to set it to one way. That's what we did earlier in
the example, right? We're going to set it to the path which is the property
that you want to bind to is the name and that's the key of our
resources file and the source is going to be our localization
resource manager instance. So we're actually creating a binding
to our localization resource Manager, getting the key from that and
giving that back to our XAML, to our UI to actually show our thing right
here. Now I'm saying XAML all the time. You can also use this
from inside of your code, which might not be that makes it nicer or not because this is very
much geared towards using it in example because
it's a markup extension. But you can probably find ways to make it
easier for you in code as well. Now, if we do that, go back to
our main page and I'm just going to copy this label
one more time so that you have the full picture. By the way, like
with all of my videos. The GitHub code, the sample repository is found below
in the video description. So go check that out so you can review it at
your own pace and see how this all fits together. And now that
we have this, what we can also do is say the translate extension. So we have that
in our namespace right here. Oh, I forgot one
more thing. So this content property is actually very important.
That means that we can not have to specify name is and then the
key. But we can just put the key in there. Sounds
a little abstract again, I'll show you in a little bit. But that basically
means that it will automatically whatever you put the value in there it will
automatically put it in this property right here. But this lives in our
MauiLocalizationSample namespace. So we need to add that namespace right
here xmlns Let's name it local, right? You
can again name that whatever you want and we're going to say
MauiLocalizationSample. There we have it. And now we can here
go to the last label and we can say not binding.
We can remove this whole thing. We can say local
translate and here is name. So typically you would
say you have to say name is and then we have to say hello world,
right? But here that's the same thing as you would do
here with the binding. You would actually have to say path is. No one does that
because that has that same thing. And here we
can also remove the name and just say hello world because it's implied
by this thing that we're saying like hey, everything that
we put in there that doesn't have that Identifier is going to automatically
go into the name so that hooks that up. Now if I do that and I run it
again, now we will have this translate
extension and it will basically do the exact same thing as what you can see above
here. But now this is a little bit nicer because you
don't have to repeat the mode one way all the time, you don't have to repeat
all the things constantly and it still works all the same. It flips
around. So that is how you can get started with
localization in your .NET MAUI application. So now you know all the
mechanics on how to localize your .NET MAUI application but
there is still a couple of problems, right? Like how are you actually going to get
the translations? Well there is a couple of ways to do that but there is this
amazing plugin for Visual Studio, it's called ResXManager. I'll put the
link down in the video description which has an easy way to manage your ResX files because if there is a
lot of languages then that can become messy
as well. But B it has a built in way where you can go to a
couple of APIs. You put in the API key. I think one of
them is in Azure. And you can get the machine translated
stuff right. So it's not perfect, but it's something right and that will
help you get there 90% of the way. Maybe have someone check
the translation if it actually makes sense. But that can help you with
automatically translating your app. That is really amazing. And there's much more
solutions with AI and stuff that you can leverage there as well. So that's
really cool. And another thing, my friend Johan Svensson, I think from Sweden, he
has created an amazing plug in that will help you with all of
this as well. That will make it much, much easier so that you don't
have to do all of this stuff yourself. If that's something
that you want to see as a follow up on this video, please let me know down in
the description and I will set that up as well so that you know how to do
it with that plug in for now. Thank you so much for
watching this video. Click the like button if you've actually liked it so that more
people can translate their apps. And we will have a wonderful world of apps
that can be used by anyone in any language. Subscribe to this
channel if you haven't done so already. And don't leave yet because there is a
full playlist with .NET MAUI videos that you can find right
here. This video is recommended for you, dear user. Yes, just for you.
And of course, double check that you did subscribe on
this button. I'll see you for the next one.