The user speaks a different language.
The Problem
Now that you are an expert in Angular Universal (assuming you read this), you wanna make things better. You want to make sure that the user gets the app (or website, however you wanna call your brain child) in their own preferred language, if possible.
For example: you support English 🇺🇸 and French 🇫🇷, but you default to English and allow the user to change it if needed. While this works, it would be even nicer to figure out what the user wants, and give them that from the get-go if supported.
Disclaimer
Now, your Product Owner does not know about this(let’s call him Robin for example). So, you don’t wanna bother Robin with these details, and you’re not even sure he would prioritise this over the pile of other stuff that needs to be built, yesterday. Long story short: you wanna build this, and you want to build it fast, under the radar.
The Solution
The solution has 3 parts:
- The Contract for the service that will figure out the language
- The service that figures out the language on the Browser
- The service that figures out the language on the Server
Part 1
The contract needs to be an abstract class because Angular Dependency Injection does not work with interfaces. This is pretty straight forward:
Now, this is the Abstract class. It also has a list of supported languages and it needs implementations for the getLanguage
method.
Part 2
The Browser implementation. Here, we need to ask the browser, what is the user preferred language, and see if we support that. If not, use some default value. This would look something like this:
This one uses the window object to ask what is the browser language. We only use the first 2 characters. Next, we check if this language is supported, if not, we return the default language.
Small note: the default language is an input for this method. This way, the place that needs it, has full control.
Part 3
The Server implementation. This is where things get tricky, or so I thought. Since on the server we don’t really have a browser, we can’t get the user preferred language from there. What can we do though?
After a bit of (soul)searching I found that the browser adds a header named accept-language
to every request it makes. This header has as value a comma separated list of all the user preferred languages, in the preferred order. VERY USEFUL! Armed with that knowledge, we can implement the Server version of the language service. It looks something like this:
As you can see, here we inject the Request, get the accept-language
header, split it, get first value badabing, badabum and Bob’s your uncle(who’s Bob?). You have the user preferred language on the server.
Part 4
Yes, there is a part 4. Tying everything together. The only thing we need to do now is make sure the correct service is provided on the correct platform. Something like this:
Of course, this is the stripped down version of the AppModule
and the AppServerModule
, but you get the idea…(if not, let me know and I’ll try to explain how this works in more detail).
Small notes
- You can of course make the service not need a default language, and use the first item in the
SUPPORTED_LANGUAGES
array. - Everything that’s common between the implementation should live in the Abstract Language Service. Great place for that!
- When you need the language service, don’t inject the
WebLanguageService
, or theServerLanguageService
. Always inject theLanguageService
because Angular will figure out which implementation to use. Something like this:
Want more?
Let me know if this is not clear, you need more examples, more use cases etc.
Also feel free to flag what’s wrong with my approach, that’s how we get better at this.
Be kind to each other! 🧡
Over and out
Fresh out of the oven! #Angular #AngularUniversal