Descriptive localizable using Swift's enums
Image from Pixabay
As stated previously Swift’s Enums are quite powerful; in this post we’ll cover one use case I’m really fond of: localizables 🇺🇸🇺🇾🇻🇪.
Current state of the union for localizables
Apple has provided us with many tools to work with localization. However there are still many associated pitfalls for handling non static localizable (a.k.a: you can’t use Xcode’s autocomplete in order to access your declared localizables) which is why it’s up to us as responsible developers don’t make a mess in the codebase while implementing multi-language support in our apps.
Let’s see what I mean in a short example: the safest way to use localizables is via localizedString
API, which requires you to have a set of Localizable.strings files with the desired key/values for the supported languages.
English strings | Spanish strings |
Afterwards you’d use any of said keys within code like so:
English caption | Spanish caption |
(disclaimer: you don’t necessarily have to set your localizable UI text via property observers, I did this way in order to compact the code snippet as much as possible)
Slippery rope
So, it seems pretty straight forward the localizable implementation. What’s the problem? The usual suspects:
- Breaking of SRP principle (VC starts knowing too much about UI configuration)
- Manually typed strings is always an error prone move
- Unless you’re working with a dedicated translator, the
comment
value might very well be omitted. IMHO a well defined variable is always better than a comment.
Enter enums to the rescue
Let’s clean things a little bit by introducing Enums to the mix. Let’s start by defining en enum with our key:
Ok, this solves the hardcoded string issue. Let’s keep moving on add the localizable support. We could, for instance, add a computed property and inside of it request the localized value for the current case.
However this leaves us with a couple of choices:
- Forever build this single enum with all localizables values add to it as the project grows.
- Include the same computed var snippet of code into each single Localizable enum in order to keep them separated by topic/view relationship.
The former breaks SRP and the later DRY. But, what if you could have our cake and eat it too? Enter option 3! 🥳🎂
Enum + Protocols = dangerous couple ⚔️
Let’s make a protocol that encapsulate the desired behavior
Now we can declare the desired behavior in the protocol’s extension, saving us that way the trouble to ⌘ + C / ⌘ + V stated before.
Nevertheless the beauty of protocols might be dangerous if not properly constraint since anything can now conform to it. I heard once a college say: “when you’re designing an API, you need to account for someone using it the worst way possible” and since possible is the key word here, let’s constraint that protocol in order to make it impossible to be used by anything else besides what it was intended for:
Now only enums conforming to String
protocol will be able to use it. Nice catch 🎣
All there is to do know is replaced the localized text in our ViewController for the enum abstraction like so:
Testing time! ❌ - ✅ - ♻️
You can test this manually going into the simulator configuration -> General -> Language & Region -> iPhone Language and selecting the proper one for each set of regression… Or you can let Xcode spares you the process 😄
1_ Create a new scheme |
2_ Set it up with the desired language for the tests (Spanish in our case 🇪🇸) |
3_ You should be good when you see this (I tweaked scheme’s names in order to avoid confusion) |
Testing localization is straight forward thanks to the default behavior of NSLocalizedString
API which states that in case the requested value doesn’t exist inside the table of localized values declared (.strings file created at the beginning of the post) then the same key will be returned as a string. Let me show you what I mean with a failing test:
Failed for Spanish scheme since the default value isn’t expected (remember we defined a value for said key earlier) |
Finally a single passing test for both scheme could be this one:
Apple introduced test plans in WWDC 19 which come really handy for our test schemes but that (alongside a more dynamically approach on testing multiple localized keys) is a topic for a later article.