 Hello, my name is Matt Raebel and today I'd like to show you how to internationalize a simple Java application, a Spring Boot application with TimeLeaf, and a JavaScript widget. You might be interested in knowing what IE-TNN and L10N are. Those are basically abbreviations for internationalization and localization. And basically internationalization is a process of making your application capable of rendering its text in multiple languages. And then localization is your application has been coded in such a way that it has language, cultural, and other requirements of a particular local, for instance, date, time, currency, all that kind of stuff. So this blog post shows you how to do everything I'm going to do today. It has all the code in it. It's got some caveats that you'll see, shows you some errors. I won't show you all the errors, but I'll show you most of them that you might run into. And then there's a GitHub repository here that has all the code that we'll be writing. And in this repository, there's a demo.adoch. So if you look at that file, this is an ASCII doc file that basically condenses the blog post down into a series of steps that I can follow to make this simple and easy. Maybe not simple and easy, but at least understandable. And you don't have to read through a whole bunch of text. So let's get started. So I'll open a new terminal window and create a Java 18n directory, if I can spell, and then cd into there. And then I'll open it up in IntelliJ. And then I'll create this hello.java file. It'll have a simple public static void main and just simply print out hello world. So in this tutorial, I'm going to be using a number of live templates. This is a feature of IntelliJ that basically has a bunch of code snippets prerecorded and allows you to recall them with a few keystrokes. So public class hello. And then one of these live templates that's built into IntelliJ is public static void main. So psvm tab. And there you go. It writes all that for you. Another one is system out print line, SOUT. So it makes it so you can write code a lot faster. So hello.world. And then in a terminal, we'll run java hello.java. So you'll see you get an error if you're doing this with Java 8. And that's because it's a new feature of Java 11 that allows you to have single source Java files. You could actually have them as scripts if you wanted. But as long as everything self-contained within that Java file, you can actually run it with the Java command without compiling. So I'm going to use SDKman to list all the Java options that are available. And you can see I've already installed a few. For instance, Java 12 is here, Java 11 is here, and a few other ones. So if I use SDK default Java 11.0.2.open, now we'll be using Java 11. And now we can go java hello.java and actually print out hello world. So now we will create some internationalization bundles or resource bundles as they're called in the Java world. Messages.en.us. So the first part is the language, the second part is the country. And we'll just do hello equals hello and world equals world. And then we'll create one for Spanish as well. Messages. And in this one, we're just going to put the country or the language, not the country. And then we'll go ahead and copy paste. And now we can modify this hello.java so it actually retrieves the translations from those bundles. So I have a shortcut for this and you can see the default is language en country is US. And then it basically says, hey, if something's passed in, you know, two arguments, then go ahead and use that instead. And then it prints out messages got get string for hello and for world. So now if we run this, oh, we need the imports. So it says, hey, I don't know what resource bundle is. I don't know what locale is. Both of those are in the Java util package. So we can just use Java util dot star. And then we run it again. And now it works. So it says hello world. And we can improve the parsing of the arguments a bit here and say and allow basically only one instead of two. So if there's only one passed in and set the language. And this makes it easier because we don't really need both language and country to create a locale object. So now what we can do is run Java hello dot Java and pass in Spanish. And now we're getting internationalized app in Spanish. So pretty cool, huh? It's all built into Java. The next thing I'm going to want to do is create a spring boot app with web and time leaf. So spring NBC and time leaf and spring initializer start dot spring dot IO has a rest API that you can actually download files directly from. So if I just copy and paste this guy, copy, paste, you can see it hits that starter dot zip endpoint, specifies the dependencies, and it basically pipes it and unzips it right in this directory. And so now what we can do is add it as a Maven project. And then in source main Java, we'll add a home controller to begin. Shortcut it at 18 and home. And all this does is return a string that indicates the template to use. And we'll need to create the template under resources templates. We'll create a home dot HTML. We'll go ahead and copy and paste this HTML in there and do a bit of formatting. And then we'll create two properties files. These are resource bundles, one in English, which doesn't have an extension like EN or EN US. It's just the default one that'll be used if none others or no others are found. So messages dot properties. Here's our title and message. Hello. I hope you're having a great day. And then a Spanish one, messages dot ES properties. And you can see IntelliJ is smart enough to actually group those as a resource bundle and even allow you to manipulate both properties at the same time. So that's a pretty slick feature of IntelliJ. And so now we can go into Chrome and actually change the default language that's used. So you can search for language, not there. And then you can see it's preferring United States in English. And we can add Spanish, Espanol, not specifying the country. And then move it to the top. And now basically it'll default to Spanish. That's what it's going to try to render if it gets a chance. So we can run this application, Spring Boot Run. And Spring Boot 2.1 added Java 11 support. That's why I'm able to start it and everything works. And if we go to localhost 8080, you'll see it's rendered in Spanish. So you might have seen those websites where actually allow you to choose between English and Spanish or French. And so let's add that capability. You can do that by defining an MBC Configurer in here. We'll basically define a locale resolver that's based on cookies. And also a locale change interceptor that'll look for a parameter name of Lang. And then we'll add that interceptor to the MBC Configurer. So now we can restart. And we'll be able to pass in that parameter Lang equals En. And it'll actually render in English. So now we have that working. And if we were to take off that parameter, since we're using a cookie locale resolver, it keeps it and it knows that that's what we want to render the page in. And so you might be pleased with everything that's happening so far. But if you were to go into messages.properties and you change this, I hope you're having a nice day. And then you go here and refresh. Nothing changes. Similarly, if you go into your templates and say, hey, I'm going to change this to be emphasized, refresh doesn't change. So that's kind of a bad developer experience. Let's fix that. Two things you can do. The first thing is DevTools from Spring Boot. Dependency, add a new dependency called Spring Boot DevTools. And what this will do is it'll basically detect if you've changed any classes, if you recompiled any classes, and it will auto reload Spring. To do that with IntelliJ, you actually have to go in here and say, on frame deactivation, update the resources. So this will work for both I18n and TimeLeaf template. So that only works if you start it from here, but let's just see it in action. So the first thing was the template, right? So this should be emphasized now. And if we change it to back to just a paragraph and restart, you can see there was a brief flash, but that's because it restarted everything. And so you can switch between those pretty easily and you can switch this back to have a great day. And once that frame deactivates, it restarts and you'll see your changes. So now you're getting hot reloading on both of those. And the only other thing I would say is if you're doing a complete command line and you're not using IntelliJ, there is a property that you have to define for TimeLeaf. So this says, don't look in the class path, look actually in the source tree. And so in application.properties, Spring TimeLeaf prefix, basically point it there. And then if you're doing it from the command line, it'll allow you to hot reload your TimeLeaf template. So going back here, I hope you're having a great day. And if we were to change this to, for instance, an H2 and refresh, you can see that works. If we were to go to our messages and change that. It actually doesn't work. And that's because there's nothing that compiles the resource bundles into the class path. So if you dump another terminal and an MVM compile, since it has DevTools installed over here, it'll restart everything and then you'll get that change. So just revert everything back to what it was. You can see that. And then of course you would have to recompile if you wanted the resource bundle changes. All right, so now we've seen how to do hot reloading. We've seen how to use IntelliJ to update the resources and how to add internationalization to a Spring Boot TimeLeaf application. The next thing I want to show you is how to customize the language used by Okta's sign-in widget. So this is a JavaScript widget that has the ability of internationalization built in. So I'm going to export this custom logging example we have. I'll cd into that and I'll open that one up in IntelliJ. And so to begin, I'm going to create an OpenID Connect application in Okta. Make this a little bigger. So we go to Applications and then Add Application, choose Web. And then we'll just call it Java 18n. Click Done. So we have our application here. We have a client ID and a client secret. And back in our tutorial, it says basically to create an Okta.env file. So go ahead and create that. Call it Okta.env. Also create a gitignore so that's not included in source control. So gitignore and addstar.env. In our Okta.env, we're going to define an issue or a client ID and a client secret. This will be used by the widget to basically log in. So in our app, we can grab the client ID and the client secret. And then for the issue where you can go to API Authorization Servers, and it's right there. So that should all be set up. And we'll start this application up. You can actually just do it right from here. Oh no, that's not going to work. We have to do it from the command line because we have to source that file we created. So source.okta.env. And then that'll set the environment variables and it'll make everything work. So if we go to... Oh, we already got something on 8080. So fkill8080. It's one of my favorite commands. So npmi-gfkill.cli is how you get that and allows you to kill ports or processes. And it's pretty nice. So now we can run spring boot run again. So it's up and running. We can go to localhost8080. And you'll see it basically is just an example and you click on login. And you'll notice this is actually in Spanish and that's because the login widget is smart enough to know and read from the locale header or accept languages header. And also spring auto enables that accept header locale resolver. And if you add a locale interceptor, the widget won't change languages. So first of all, let's fix that. So I'm going to go and create that MVC configure again. MVC configure. And this will allow us to change locales with that parameter just like we had before. And then there's a login controller. And this needs a language parameter. We'll basically read from spring what it's been set to. So add an argument called locale. And spring MVC will resolve this for us. Pretty handy. And then we can just do language and specify the locale here. And that's all we need to do on the Java side of things. On the template side of things, we'll need to go in here. This is what configures the login widget. And we'll have to add a configuration parameter called language. And so this is what time leaf we'll get from spring MVC. And this is the default. And then we can restart. And now if we set that language to EN, the widget will actually render in English. So we'll go back here. Say Lang equals EN. And then click login. And now you see it renders in English. So that's great. One other thing I want to do is show how to read from octas locale. So if you actually signed in and you had a locale set on the user, what does that look like? And how do you convert the whole application to use what's been set there? So first thing is to make a couple resource bundles. Messages.properties. And we'll just do welcome and hello. And then similarly for Spanish, which is es.properties. And then in home.html, there's this welcome right here or this hello. So we'll do thtext equals that hello string. And then down in the welcome message, instead of welcome home, Joe Coder will read the actual person's name. So Joe Coder is just a placeholder here. And this welcome, you can see that you can pass arguments into it. And that's what gets substituted for this zero right here. So arguments will be zero, one, two, or many have. And then you can pass them in just like this. So now we can restart. And you'll be able to see the locale that we're beginning with, at least. So this one says hello. If we were to change it to es. All right, it says hola. And then it'll set it appropriately for the sign in widget. So all that's working. We're going to say hello. Then we have English. And then I could put in my credentials and sign in. And that all works, right? We came back here. Welcome home, Matt Rabel. But if we were to do it, if we were to log out and do it in Spanish, Lang es, you'll notice this part's in Spanish, but my locale is actually English. So you can see that in the user right there. Locale is enus. So what we'd like to do is actually read from octas data that comes back and change it to match what the user has. So to do that, I'm going to create an OIDC resolver, OIDC locale resolver. If you look at this, you'll see that this resolves the locale, extends cookie locale resolver. So once it's been resolved, it won't, or it'll keep it in your browser. And then it uses Spring Security to get the context, to get the authentication and the principle, and then get it from the OIDC user, the actual locale, and then it returns that language tag. And if it doesn't find it, then it just returns the default one from the request. And then in the MVC Configurer, you have to change this cookie one to OIDC locale resolver. And now if we were to log out or restart the app and log in with Spanish, or set it to Spanish to begin, right? Hola, this is all in Spanish. And now it should be English when it comes back. So you can see it says welcome home, mad dot rabble. So that's all working. And that's how you basically get the locale from the OIDC user with Spring Security. If you want to do internationalization with other JavaScript apps, you can check out J-Hipster. So J-Hipster integrates Angular, React, and Vue as UI frameworks. And if you want to learn more about J-Hipster, click on that link there. This was just published last week. But basically it uses a number of internationalization libraries to make it possible. So with Angular is ngxtranslate and all the translations are in JSON files. And then in React, there's a translate component that's based off React translate. And in Vue, we use Vue, I-18-N. So the same translation files are used for all three frameworks. It's a pretty slick way of doing things. If you want to learn more, hit the GitHub repo, hit the blog post. I also have a number of links for you here. So GitHub repo again is at octadeveloper octa-java-18-N example. You can find me on Twitter at mrable. You can find my team at octadev on Twitter. If you're interested in learning more about octa or our sign-in widget, there's a great page right here in developer.octa.com that allows you to customize it. You can click here to sign up, or you can read all of our API reference and documentation. We also have a YouTube channel, and I would encourage you to subscribe to it. And I hope you have a wonderful day.