Internationalizing your Enumerations and Domain Model

If you’ve ever been tasked with implementing internalization within a codebase (especially a legacy one), you’ve probably had to make some tough decisions. How can I get localized values for all of my domain objects? Am I going to have to spend hours manually adding keys to enums?

Before we answer this, let’s go back to the root definitions of internationalization and localization.

“Internationalization (I18n) is the structuring your software in a way that makes it possible for them to be localized. Localization (L10n) means making an application work for a particular market”.

In summary, localization utilizes internationalization.

An example of poor localization.

Implementing these concepts has become easier over the years. We now have various thirdparty services to solve these problems. But, developers still have tendencies to make a few pitfalls with their domain model.

Domain internationalization does not belong in your data API.

Internationalization is a separate concern when dealing with your domain model. Because your data API…

is utilized by many clients with different presentation concerns.

is called often. The localized values of your domain model do not change as often, reducing the effectiveness of short cache strategies.

is complex enough already.

Here’s a few examples of APIs that miss the mark.

{"sauce":{"value":"Barbecue","short":"BBQ","long":"Barbecue"}}

This leaves us with just two interpretations. We (and other clients) are locked into these localizations. One of us could ask for them to be changed, but the other clients would be effected.

Slightly better, but it falls under the same fallacy. For each call to this API, the system has to deal with the burden of localizing this enum Timeframe.Estimated. Sure, it can pull from a cache, but now you have another layer of caching to deal with. You’re still locked to these three interpretations.

So the domain model data here in question is transmissionType. How do we know this is a domain enum as a client? Hopefully the API utilizes something like Swagger, providing some sort of documentation. But how can we get a localized value?

We call a different API whose only concern is localization. This allows the API to handle caching in a simple way. You give it a key, an Accept-Language header, and it returns a localized value.

But how do we know what key to give?

Solution: Loosely Typed Keys

Well, this is somewhat up to you, but it needs to be communicated across your project/company/documentation.

One example implementation would be to simply prefix all localization keys related to the attribute with the programatic value (ex: transmissionType). Any variations would have appended “tags”.

Example localization keys for TransmissionType.SemiAutomatic:

transmissionType_SemiAutomaticMobileApp

transmissionType_SemiAutomaticDesktop

transmissionType_SemiAutomaticTablet

transmissionType_SemiAutomaticShortCode

This allows us to implement our needed interpretation of the domain object/enum. One drawback is that you cannot sling drastically different interpretations across your product/software. This usually isn’t a technical problem, and is more so at fault of product not being aligned across departments. Even with strongly typed localization keys, if product is not on the same page, teams will find ways to break/bypass localization and provide their own misaligned representation.

Conclusion

This thought process is bound to increase productivity without sacrificing performance or maintainability. You’re not out to create a standard that works for the world, but just for your company. It can be “wrong” to the world, but right for your company or community.