Cross-Platform C++ Localization of Mobile Apps

Igor Pener
Aug 9, 2017 · 3 min read

You’ve managed to write a cross-platform app — great! Presumably it’s in English and you’re looking into localizing it. If not, then you should because both the App Store as well as Google Play are more likely to feature well localized apps. Let’s look into how to create a little cross-platform localization framework using C++. I have developed this technique during the development of Shnips for iOS and Android and it’s proven to be a lot more extensible and faster to work with than having to maintain two implementations. I assume you know how to interact between native and C++ code on mobile platforms. If not, check out the post about Cross-Platform App Design.
On iOS, Localizable.string files are in the following format:

"key" = "value";

On Android, the key-value pairs are in the strings.xml files:

<string name="key">value</string>

Although these formats are pretty differen it’s not a good idea to discard these files and come up with one alternative. Why? Well, not only your app but also the App Store as well as Google Play rely on these files so better don’t remove them. But what we can do instead is to abstract the functionality of retrieving the strings from these files.

// IStringLoader.hpp
#include <codecvt>
#include <locale>
#include <string>

class IStringLoader {
public:
virtual std::wstring localizedString(const char *key) = 0;

protected:
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
};


//StringLoader.h — Objective-C++ code
#include "IStringLoader.hpp"

class StringLoader : public IStringLoader {
public:
std::wstring localizedString(const char *key) override {
NSString *nskey = [NSString stringWithUTF8String:key];
NSString *str = [NSBundle.mainBundle
localizedStringForKey:nskey
value:nil
table:nil];
return converter.from_bytes([str UTF8String]);
}
};

By using the std::wstring_convert we ensure that the strings can contain unicode characters and will still be correctly read.
It’s probably a good idea to have a singleton (or global) StringLoader object. Then, you can simply call loader->localizedString("key") to load your string.
Well, so far we didn’t really achieve much apart from loading a localized string but what about parameterized strings? That’s where we can make use of templates and streams — two powerful C++ features. While the token formats for parameters differ on iOS and Android, we can create our own and it doesn’t even have to be bound to a type like an integer or a string. The token character really doesn’t matter as long as it’s parsed correctly so how about we use the following one %_, i.e. we’ll have strings like:

"You scored %_ points"

Now we need to write a little bit of code to parse it and fill it with the respective arguments.

// IStringLoader.hpp
#include <codecvt>
#include <locale>
#include <sstream>
#include <string>

class IStringLoader {
public:
virtual std::wstring localizedString(const char *key) = 0;

template<typename... Args>
std::wstring localizedString(const char *key, Args... args) {
const auto str = localizedString(key);
std::wostringstream os;
tprintf(os, str.c_str(), args...);
return os.str();
}

protected:
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;

private:
void tprintf(std::wostringstream &os, const wchar_t *format) {
os << format;
}

template<typename Arg, typename... Args>
void tprintf(std::wostringstream &os, const wchar_t *format,
Arg arg, Args... args) {
for (; *format != L'\0'; ++format) {
if (*format == L'%' && *++format == L'_') {
os << arg;
return tprintf(os, format + 1, args...);
}
os << *format;
}
}
};

Now you can call loader->localizedString(“score”, 42, 1.23, “Buck”) assuming that the value of the key string is:

"You scored %_ points in %_ minutes with %_"

And the returned string will be:

"You scored 42 points in 1.23 minutes with Buck"

You can even pass all kinds of objects to the function as long as the stream insertion operator << is defined for that type. Note that the intention of this post is mainly to inspire. For that reason, error handling and the singleton implementation have been left out. Have fun localizing!

 by the author.

Igor Pener

Written by

Founder / CEO of techOS. Built Shnips, a mobile game for iOS and Android. Computer graphics 👾, AR 👓, AI 🧠.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade