A demo project that illustrates the content of this article is available on my GitHub.
For a user, being able to interact with an app that is localized with a language that he can understand holds a tremendous importance. Consesiently iOS has done a great job at providing this behavior at very little cost for the developer.
Unfortunately, it only provides it in a static way, meaning that the app is localized using the locale of the device, but the SDK offers us no way to dynamically update the localization once the user is inside the app.
Still such a feature can bear great business value. For instance let us take the example of a salesman that uses an iOS app to present its products to potential customers at an exhibition. Not all the people he meets will be speaking the same language, thus being able to switch on-the-fly the localization of his app will be a nice help.
In this article I will show you how it is possible to leverage the specificities of the Objective-C runtime in order to achieve this goal, without the need to create a new localization API.
How does iOS deals with localization?
In an iOS app, Localizable.strings files are treated as resources, and are thus stored and accessed using the Bundle class. To get a better sense of the structure structure at hand, we can inspect the content of an app:

We see that for each localization, there is a corresponding .lproj directory that contains the localized resources.
Consequently, our strategy to achieve dynamic localization will be to find a way to make the app swap dynamically between those directories.
Diving into the Objective-C runtime
As of iOS 11, all apps are running using the Objective-C runtime, which is an environment that hosts the Objective-C compatible classes and allows them to interact as intended. Fortunately for us, this runtime exposes an API that allows our apps to mangle with it. And while it needs to be used with care, it definitely holds great power.
Let’s consider this function:
What it does is basically allowing us to change, at runtime, the class of an existing object. (To understand how it works, you can refer to this part of the documentation)
Through this function, it becomes possible for us to create a subclass of Bundle that handles the localization swapping logic described above, and then set the class of Bundle.main to this new class.
The solution
Here is the implementation for the Bundle subclass:
And here is the code that actually performs the swapping to the new localization:
Using this new API, we can write a simple controller as follows:
And, voilà, our goal is achieved:

Conclusion
In this article, I’ve focused on localizable strings, because it is by far the most common kind of localized resources you will find in a given app. But the solution I’ve described can, of course, be applied to any kind of localized resources, such as audio files, videos, etc.
Originally published at gist.github.com.
