When building a public website, Search Engine Optimization (SEO) plays an important role. If you need internationalization (i18n) support on your site as well, Spring’s default handling is not really optimal for SEO. This blog is about how to deal with i18n in Spring in a way optimized for SEO.
Spring MVC has native support for internationalization, the feature of making your website available in multiple languages (and use regional formatting as well). The preferred language (and optionally country) is called a Locale and basically looks something like ‘en’ or ‘en-GB’, indicating respectively ‘any English’ and ‘English as spoken in the United Kingdom’.
Out of the box it is based on reading the browser’s accept-header which makes sense: the browser is probably configured by the end-user to work in the language that user prefers. With each page request this header is send by the browser. This header is detected by Spring’s default
AcceptHeaderLocaleResolver which checks that header and determines the preferred locale. We can retrieve the current locale by accessing
LocaleContextHolder.getLocale() and based on that we can render the page. You might even offer some language selection box somewhere and store that in a cookie by configuring the
CookieLocaleResolver. The internet is full of examples on how to configure these, so I won’t repeat it here.
However, as so many things on the internet, things are slight more complicated in practice. In this case, Search Engine Optimization (SEO) makes our life a lot more complicated. The problem with Spring’s out of the box solution is that the url for each of the different languages (or locales if you prefer) is exactly the same. Thus, Google’s spider will index probably only one language. Even worse, that language might not be the same as what the user actually gets to see when he visits your website. For SEO purposes, we need unique url’s per language. One way of doing it is having separate top level domains (TLD) for each locale, e.g. first8.nl and first8.co.uk. That would work (and for SEO purposes actually also helps to segment your audience per country), it is quite something to claim all those top level domains. Also, it doesn’t help to pick a specific locale per se, since some TLD’s represent multiple languages (e.g. in Belgium people speak either Dutch or French). Another solution would be to use subdomains (e.g. nl.first8.nl and en.first8.nl) but this often clashes with SSL certificates and is a maintenance hassle.
Path based locale
The most common solution is probably to use the url path for indicating the locale. Thus something like ‘first8.nl/en/actual-content’. This is something that is not yet out-of-the-box available in Spring MVC but is easily done. I’ll give a quick run down here, but for more details, see the links below.
- create a Filter which rewrites the incoming url by stripping off the locale and putting it in the HttpRequest as a request parameter
- create an Interceptor that checks for this request parameter and if detected, updates the LocaleResolver
- create a LocaleResolver which stores the locale
There are a couple of things to consider using this with regards to SEO. One question is how strictly do you require the presence of a locale in the URL? What should be the behaviour when no locale is present. Should there be a fallback mechanism, e.g. the accept-header? Or maybe if a locale has been indicated, we’d like to keep that as a default. In those cases, Spring provides
SessionLocaleResolver‘s. If you have logged in users, their accounts might have a locale configured as well. So you might provide your own resolver if the standard one’s don’t fit. Another concern is how to indicate which languages are available and to provide search engines with links to translations of the current page. If you have a list of supported locales available, it is quite easy to generate these
hreflang links in the header of your page. It should look something like this:
- Google documentation about Multi-regional and multilingual sites
- Google documentation about hreflang for language and regional URLs