By definition, good applications must look good for users. Besides many other things, that means good applications must talk with users in their language. But just translation of words and phrases to another language is not enough - many other elements differs from one culture to another, including the format of dates, money values, rules for capitalization, etc. All such specific information in computing defined through locales.
The way locale-specific input and output should be handled is standardized by ISO C and ISO C++ standards, so usually it's enough to just follow standards to properly localize your application. Unfortunately, this approach doesn't work on Android for software written in C/C++. Android libc (Bionic) has no native support for locales, so the only way to use localized input/output in native code is to implement localization in Java and refer to it through JNI. Obviously, such an approach adds significant run-time overhead, but it's the only choice if you're using Google's Android NDK.
In CrystaX NDK, we've taken care of that regrettable omission and added full support for locales as described in ISO C and ISO C++ standards. This means that by using CrystaX NDK, you can use the standard approach for localization, on Android too.
Let's see a couple of examples how this can be done.
Imagine we have a full-screen application that used OpenGL for drawing everything on screen and it needs to draw the current date and time on the top of the screen. Obviously, that date and time should be formatted according to user's locale settings. To achieve that, we simply need to call the standard setlocale() function at the beginning of the application, and then all calls of the standard strftime() function will use the local date and time settings:
#include <locale.h> #include <time.h> int fulldatetime(char *buf, size_t bufsize, time_t t) { return strftime(buf, bufsize, "%c", localtime(&t)); } /* Somewhere at app initialization code */ setlocale(LC_TIME, "en_US.UTF-8"); /* Locale is set, now time formatting would go in that locale */ fulldatetime(buf, sizeof(buf), tm); /*=> Sun Sep 28 02:04:04 2014 */ /* Or */ setlocale(LC_TIME, "fi_FI.UTF-8"); fulldatetime(buf, sizeof(buf), tm); /*=> Su 28 Syy 02:04:04 2014 */ /* Or */ setlocale(LC_TIME, "sv_SE.UTF-8"); fulldatetime(buf, sizeof(buf), tm); /*=> Sön 28 Sep 02:04:04 2014 */ /* Or any other standard locale such as de_DE.UTF-8, ru_RU.UTF-8 etc */
This is how it works. Simple, eh? And, wonderfully, that code will work on other platforms, too! In other words, no need to separate Android implementation from others now; just write such code in the core of your project and compile it for all platforms - Android, iOS, OS X, Windows, etc.
However, some applications need to display the same information in different languages at the same time. Calling of setlocale() is not a good choice for such cases, just because setlocale() sets global locale settings, which affect the whole application. Fortunately, CrystaX NDK supports so-called "extended locales", which allow you to specify the desired locale as a parameter for formatting functions:
#include <locale.h> #include <time.h> int fulldatetime(char *buf, size_t bufsize, time_t t, locale_t l) { strftime_l(buf, sizeof(buf), "%c", localtime(&t), l); } /* Somewhere at app initialization code */ en_us_l = newlocale(LC_TIME_MASK, "en_US.UTF-8", (locale_t)0); ru_ru_l = newlocale(LC_TIME_MASK, "ru_RU.UTF-8", (locale_t)0); /* Now, at any place */ fulldatetime(buf, sizeof(buf), tm, en_us_l); /*=> Sun Sep 28 02:04:04 2014 */ fulldatetime(buf, sizeof(buf), tm, ru_ru_l); /*=> воскресенье, 28 сентября 2014 г. 02:04:04 */
The same would apply for formatting monetary values. For example, many mobile games uses in-app purchases and specify prices in local currency. These prices should be displayed properly, too:
#include <locale.h> #include <monetary.h> int price(char *buf, size_t bufsize, double value, locale_t l) { return strfmon(buf, bufsize, l, "%.2n", value); } /* Somewhere at app initialization code */ en_us_l = newlocale(LC_TIME_MASK | LC_MONETARY_MASK, "en_US.UTF-8", (locale_t)0); ru_ru_l = newlocale(LC_TIME_MASK | LC_MONETARY_MASK, "ru_RU.UTF-8", (locale_t)0); /* Now, at any place */ price(buf, sizeof(buf), 4.99, en_us_l); /*=> $4.99 */ price(buf, sizeof(buf), 299.0, ru_ru_l); /*=> 299.00 руб. */
Look interesting? Start using CrystaX NDK right now!