{"id":7383,"date":"2021-07-24T16:18:56","date_gmt":"2021-07-24T20:18:56","guid":{"rendered":"https:\/\/springframework.guru\/?p=7383"},"modified":"2021-09-10T08:56:52","modified_gmt":"2021-09-10T12:56:52","slug":"internationalization-with-spring-boot","status":"publish","type":"post","link":"https:\/\/springframework.guru\/internationalization-with-spring-boot\/","title":{"rendered":"Internationalization with Spring Boot"},"content":{"rendered":"<p>Internationalization or I18N is a process that makes your application adaptable to different languages and regions without engineering changes on the source code. You can display messages, currencies, date, time etc. according to the specific region or language, likewise you can say internationalization is a readiness of localization.<\/p>\n<h2>Maven dependency<\/h2>\n<p>You will only require the basic spring boot dependencies along with <a href=\"https:\/\/mvnrepository.com\/artifact\/org.projectlombok\/lombok\/1.18.20\" target=\"_blank\" rel=\"noopener\"><code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">lombok<\/code><\/a> dependency in your <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">pom.xml.<\/code><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"xml\">&lt;dependency&gt;\r\n    &lt;groupId&gt;org.projectlombok&lt;\/groupId&gt;\r\n    &lt;artifactId&gt;lombok&lt;\/artifactId&gt;\r\n    &lt;optional&gt;true&lt;\/optional&gt;\r\n&lt;\/dependency&gt;\r\n<\/pre>\n<p>You will also require <a href=\"https:\/\/mvnrepository.com\/artifact\/org.springframework.boot\/spring-boot-starter-thymeleaf\" target=\"_blank\" rel=\"noopener\">thymeleaf<\/a> dependency to be added in as a front-end templating engine.<\/p>\n<p><strong>Note: <\/strong> In our example, we are only testing the internationalization with Postman and hence, we do not require to add thymeleaf dependency.<\/p>\n<h2>Spring Boot Internationalization Example<\/h2>\n<p>It is a simple application to greet a user in different languages.<\/p>\n<p>Internationalization requires you to define the locale configuration in a configuration class.<\/p>\n<p>This is the code for <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">LocaleConfig<\/code> class.<\/p>\n<p><strong>LocaleConfig.java<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\">package guru.springframework.configuration;\r\n\r\nimport org.springframework.context.annotation.Bean;\r\nimport org.springframework.context.annotation.Configuration;\r\nimport org.springframework.context.support.ResourceBundleMessageSource;\r\nimport org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver;\r\nimport java.util.Locale;\r\n\r\n@Configuration\r\npublic class LocaleConfig {\r\n\r\n    @Bean\r\n    public AcceptHeaderLocaleResolver localeResolver() {\r\n        final AcceptHeaderLocaleResolver resolver = new AcceptHeaderLocaleResolver();\r\n        resolver.setDefaultLocale(Locale.US);\r\n        return resolver;\r\n    }\r\n\r\n    @Bean\r\n    public ResourceBundleMessageSource messageSource() {\r\n        final ResourceBundleMessageSource source = new ResourceBundleMessageSource();\r\n        source.setBasename(\"internationalization\/lang\");\r\n        return source;\r\n    }\r\n}\r\n<\/pre>\n<p>To determine which locale is currently being used by the application, we need to add a <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">LocaleResolver<\/code> bean.<\/p>\n<p>The <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">LocaleResolver<\/code> interface has implementations that determine the current locale based on the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">session<\/code>, <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">cookies<\/code>, the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">Accept-Language<\/code> header, or a fixed value.<\/p>\n<p>In our example, we have used the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">AcceptHeaderLocaleResolver<\/code> to retrieve locale based on the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">Accept-Language<\/code> passed as a parameter.<\/p>\n<p>I have set a default locale with the value <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">US<\/code>.<\/p>\n<p>The <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">ResourceBundleMessageSource<\/code> bean in Line 20 is used here to resolve text messages from properties file based on different locales.<\/p>\n<p>You will now require a controller class to accept the incoming request and return a response to the user.<\/p>\n<p>The code for the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">GreetingsController<\/code> class is this.<\/p>\n<p><strong>GreetingsController.java<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\">package guru.springframework.controller;\r\n\r\nimport lombok.extern.slf4j.Slf4j;\r\nimport org.springframework.beans.factory.annotation.Autowired;\r\nimport org.springframework.beans.factory.annotation.Value;\r\nimport org.springframework.context.support.ResourceBundleMessageSource;\r\nimport org.springframework.web.bind.annotation.*;\r\n\r\nimport java.util.Locale;\r\n\r\n@Slf4j\r\n@RestController\r\n@RequestMapping(\"\/api\")\r\npublic class GreetingsController {\r\n\r\n    @Autowired\r\n    private ResourceBundleMessageSource source;\r\n    @Value(\"${placeholder.greetings}\")\r\n    private String greetings;\r\n\r\n    @GetMapping(\"\/message\")\r\n    public String getLocaleMessage(\r\n            @RequestHeader(name = \"Accept-Language\", required = false) final Locale locale,\r\n            @RequestParam(name = \"username\", defaultValue = \"Albert Xin\", required = false) final String username) {\r\n\r\n        log.info(\"Returning greetings for locale = {}\", locale);\r\n        return source.getMessage(greetings, new Object[]{username}, locale);\r\n    }\r\n}\r\n<\/pre>\n<p>Here, we have just defined a simple get method which accepts <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">Accept-Language<\/code> as a <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">RequestHeader<\/code> parameter and <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">username<\/code> as the query parameter.<\/p>\n<p>In Line 26, we have added a log message to display the locale chosen in the console.<\/p>\n<h2>Defining the Message Sources<\/h2>\n<p>Spring Boot application by default takes the message sources from <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">src\/main\/resources<\/code> folder under the classpath. The default locale message file name should be <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">message.properties<\/code> and files for each locale should name as <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">messages_XX.properties<\/code>. The \u201cXX\u201d represents the locale code.<\/p>\n<p>The message properties are in key pair values. If any properties are not found on the locale, the application uses the default property from <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">messages.properties<\/code> file.<\/p>\n<p><strong>application.yml<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"xml\">placeholder:\r\n  greetings: welcome.message\r\nserver:\r\n  port: 8080\r\nspring:\r\n  application:\r\n    name: internationalization-with-springboot\r\n<\/pre>\n<p>Here, I have defined the spring configuration and a placeholder key for internationalization.<\/p>\n<p><strong>message.properties<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"xml\">welcome.message=Greetings {0}\r\n<\/pre>\n<p>In English, the value of the key is specified here.<\/p>\n<p><strong>message_fr.properties<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"xml\">welcome.message=Bonjour {0}\r\n<\/pre>\n<p><strong>Hello<\/strong> in French translates to <strong>Bonjour<\/strong>.<\/p>\n<p><strong>message_de.properties<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"xml\">welcome.message=Hallo {0}\r\n<\/pre>\n<p><strong>Hello<\/strong> translates to <strong>Hallo<\/strong> in German.<\/p>\n<h2>Build and Run the Application<\/h2>\n<p>Execute the Main Application.<\/p>\n<p>After that, open the Postman tool to hit the application endpoints to persist the data into the database or fetch from it.<\/p>\n<p>Here, each incoming request contains the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">Accept-Language<\/code> header, where we will specify the locale.\u00a0 Based on this locale, the appropriate message will be returned to the user.<\/p>\n<p>The header value is set to <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">fr<\/code>.\u00a0 As a result, the message will be displayed in the French language.<\/p>\n<p><a href=\"http:\/\/springframework.guru\/wp-content\/uploads\/2021\/07\/i18n_with_fr.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-7384\" src=\"http:\/\/springframework.guru\/wp-content\/uploads\/2021\/07\/i18n_with_fr.png\" alt=\"\" width=\"936\" height=\"370\" srcset=\"https:\/\/springframework.guru\/wp-content\/uploads\/2021\/07\/i18n_with_fr.png 936w, https:\/\/springframework.guru\/wp-content\/uploads\/2021\/07\/i18n_with_fr-300x119.png 300w, https:\/\/springframework.guru\/wp-content\/uploads\/2021\/07\/i18n_with_fr-768x304.png 768w, https:\/\/springframework.guru\/wp-content\/uploads\/2021\/07\/i18n_with_fr-848x335.png 848w, https:\/\/springframework.guru\/wp-content\/uploads\/2021\/07\/i18n_with_fr-410x162.png 410w\" sizes=\"(max-width: 936px) 100vw, 936px\" \/><\/a><\/p>\n<p>Here, I have sent the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">Accept-Language<\/code> header value as <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">de<\/code> and the message will be displayed in the german language.<\/p>\n<p><a href=\"http:\/\/springframework.guru\/wp-content\/uploads\/2021\/07\/i18n_with_de.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-7385\" src=\"http:\/\/springframework.guru\/wp-content\/uploads\/2021\/07\/i18n_with_de.png\" alt=\"\" width=\"930\" height=\"364\" srcset=\"https:\/\/springframework.guru\/wp-content\/uploads\/2021\/07\/i18n_with_de.png 930w, https:\/\/springframework.guru\/wp-content\/uploads\/2021\/07\/i18n_with_de-300x117.png 300w, https:\/\/springframework.guru\/wp-content\/uploads\/2021\/07\/i18n_with_de-768x301.png 768w, https:\/\/springframework.guru\/wp-content\/uploads\/2021\/07\/i18n_with_de-848x332.png 848w, https:\/\/springframework.guru\/wp-content\/uploads\/2021\/07\/i18n_with_de-410x160.png 410w\" sizes=\"(max-width: 930px) 100vw, 930px\" \/><\/a><\/p>\n<p>And, when you do not specify any language, the output is this.<\/p>\n<p><a href=\"http:\/\/springframework.guru\/wp-content\/uploads\/2021\/07\/i18n_with_default.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-7386\" src=\"http:\/\/springframework.guru\/wp-content\/uploads\/2021\/07\/i18n_with_default.png\" alt=\"\" width=\"936\" height=\"401\" srcset=\"https:\/\/springframework.guru\/wp-content\/uploads\/2021\/07\/i18n_with_default.png 936w, https:\/\/springframework.guru\/wp-content\/uploads\/2021\/07\/i18n_with_default-300x129.png 300w, https:\/\/springframework.guru\/wp-content\/uploads\/2021\/07\/i18n_with_default-768x329.png 768w, https:\/\/springframework.guru\/wp-content\/uploads\/2021\/07\/i18n_with_default-848x363.png 848w, https:\/\/springframework.guru\/wp-content\/uploads\/2021\/07\/i18n_with_default-410x176.png 410w\" sizes=\"(max-width: 936px) 100vw, 936px\" \/><\/a><\/p>\n<p>This is the console output.<\/p>\n<p><a href=\"http:\/\/springframework.guru\/wp-content\/uploads\/2021\/07\/i18n_console_output.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-7387\" src=\"http:\/\/springframework.guru\/wp-content\/uploads\/2021\/07\/i18n_console_output-1024x180.png\" alt=\"\" width=\"1024\" height=\"180\" srcset=\"https:\/\/springframework.guru\/wp-content\/uploads\/2021\/07\/i18n_console_output-1024x180.png 1024w, https:\/\/springframework.guru\/wp-content\/uploads\/2021\/07\/i18n_console_output-300x53.png 300w, https:\/\/springframework.guru\/wp-content\/uploads\/2021\/07\/i18n_console_output-768x135.png 768w, https:\/\/springframework.guru\/wp-content\/uploads\/2021\/07\/i18n_console_output-848x149.png 848w, https:\/\/springframework.guru\/wp-content\/uploads\/2021\/07\/i18n_console_output-410x72.png 410w, https:\/\/springframework.guru\/wp-content\/uploads\/2021\/07\/i18n_console_output.png 1319w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/a><\/p>\n<h2>Summary<\/h2>\n<p>In this post, we covered integrating Internationalization with a Spring Boot Project. That is to say, performing simple translations using <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">MessageSource<\/code> implementations and <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">LocaleResolver<\/code>,using the details of incoming HTTP requests is a very simple task now. <code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\">Internationalization<\/code> helps us to make our web applications reach as wide an audience as possible. Consequently, it can be adapted and localized to different cultures, regions, and languages. On the other hand,<code class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"git\" data-enlighter-linenumbers=\"false\"> Localization<\/code> is the adaptation of any software or mobile application product to meet the language, culture, and other requirements of each locale.<\/p>\n<p>You can find the source code of this post on <a href=\"https:\/\/github.com\/spring-framework-guru\/sfg-blog-posts\/tree\/master\/springboot-internationalization\" target=\"_blank\" rel=\"noopener\">Github<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Internationalization or I18N is a process that makes your application adaptable to different languages and regions without engineering changes on the source code. You can display messages, currencies, date, time etc. according to the specific region or language, likewise you can say internationalization is a readiness of localization. Maven dependency You will only require the [&hellip;]<a href=\"https:\/\/springframework.guru\/internationalization-with-spring-boot\/\" class=\"df-link-excerpt\">Continue reading<\/a><\/p>\n","protected":false},"author":111,"featured_media":4586,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_lmt_disableupdate":"","_lmt_disable":"","_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_uf_show_specific_survey":0,"_uf_disable_surveys":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","enabled":false},"version":2}},"categories":[21,104],"tags":[365,366,29],"class_list":["post-7383","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-spring","category-spring-boot","tag-internationalization","tag-locale","tag-spring-boot"],"jetpack_publicize_connections":[],"aioseo_notices":[],"modified_by":"jt","jetpack_sharing_enabled":true,"jetpack_featured_media_url":"https:\/\/springframework.guru\/wp-content\/uploads\/2015\/03\/Banner560x292_03web.jpg","jetpack_shortlink":"https:\/\/wp.me\/p5BZrZ-1V5","_links":{"self":[{"href":"https:\/\/springframework.guru\/wp-json\/wp\/v2\/posts\/7383"}],"collection":[{"href":"https:\/\/springframework.guru\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/springframework.guru\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/springframework.guru\/wp-json\/wp\/v2\/users\/111"}],"replies":[{"embeddable":true,"href":"https:\/\/springframework.guru\/wp-json\/wp\/v2\/comments?post=7383"}],"version-history":[{"count":23,"href":"https:\/\/springframework.guru\/wp-json\/wp\/v2\/posts\/7383\/revisions"}],"predecessor-version":[{"id":7607,"href":"https:\/\/springframework.guru\/wp-json\/wp\/v2\/posts\/7383\/revisions\/7607"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/springframework.guru\/wp-json\/wp\/v2\/media\/4586"}],"wp:attachment":[{"href":"https:\/\/springframework.guru\/wp-json\/wp\/v2\/media?parent=7383"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/springframework.guru\/wp-json\/wp\/v2\/categories?post=7383"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/springframework.guru\/wp-json\/wp\/v2\/tags?post=7383"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}