The Internationalization sample application demonstrates how you can provide multilanguage support to the application. There are 2 different translation mechanisms applied: global and local. The global mechanism implements the text string translation in a language change callback using the Settings application. The local mechanism demonstrates how to obtain the list of available languages and how to apply the selected one within the application's scope only using the i18n functions.
The following figure illustrates the view of the application with different languages applied.
Figure: Internationalization application screens
The application has one screen only, in which you can apply text translations in 2 ways:
- To use a global mechanism, go to Settings > Language and input > Display language and select a new display language.
- To use a local mechanism, select a new display language from the drop-down list on the screen and click Apply.
The user interface is divided into 3 sections:
- Top section provides information on the global mechanism.
- Middle section displays the text which is subject to translation.
- Bottom section provides information and components for handling the local mechanism.
The following figure shows the structure of the user interface. EDJE layout scripts are used.
Figure: UI layout structure
The final application view is created by embedding the proper UI components into the layout. The following figure shoes the resulting UI view.
Figure: Application UI view
The application workflow can be divided into the following logical pipelines:
- Application startup
- Text translation on a global language change
- Text translation on a local language change
The workflow is described in the following figure.
Figure: Application workflow
Prerequisites
To ensure proper application execution, the string translation files must be created using the Localization Tool.
Localization Tool
To provide multilanguage support to the application, use the Localization tool:
- To invoke the tool, select the String Localize ... option from the project's context menu. The following figure shows the Localization tool view for this project.
Add a new language by opening the Add languages window with the button, and selecting all the needed languages.
The selected language appear in the Localization tool view.
- Use the Add string key button () to create a new text translation identification key. In case of this application, the following keys are created:
- IDS_BODY_EXAMPLE_TEXT
- IDS_BODY_HELLO_WORLD
For the created keys, you can provide text translations in all selected languages.
Within the application source code, the translation keys must be used instead of the plain texts.
Implementation
Type Definitions
The general structure is used as the application data storage:
struct __appdata { viewdata_s view; }; typedef struct __appdata appdata_s;
The callback function of the get_display_language_cb type is called when a display language is selected from the list of all available languages. The callback function of the get_available_locale_cb type is called within the Model module while enumerating all available languages.
typedef bool (*get_display_language_cb)(const char *locale, char **display_language); typedef bool (*get_available_locale_cb)(const char *locale, void *data);
The Evas_Object objects represent the UI components in the main view:
// All the Evas_Object objects represent UI components struct __viewdata { Evas_Object *win; Evas_Object *conform; Evas_Object *layout_main_panel; Evas_Object *layout_language_top; Evas_Object *layout_text_middle; Evas_Object *layout_language_bottom; Evas_Object *hoversel; Evas_Object *button; get_display_language_cb get_language_cb; }; typedef struct __viewdata viewdata_s;
Application Initialization
To initialize the application:
-
Implement the application life-cycle in the main source file using a common Tizen application structure:
int main(int argc, char *argv[]) { appdata_s ad = {{0,},}; int ret = 0; ui_app_lifecycle_callback_s event_callback; app_event_handler_h handlers[5] = {NULL,}; event_callback.create = __create_app; event_callback.terminate = __terminate_app; event_callback.pause = __pause_app; event_callback.resume = __resume_app; event_callback.app_control = __control_app; ui_app_add_event_handler(&handlers[APP_EVENT_LOW_BATTERY], APP_EVENT_LOW_BATTERY, __ui_app_low_battery, &ad); ui_app_add_event_handler(&handlers[APP_EVENT_LOW_MEMORY], APP_EVENT_LOW_MEMORY, __ui_app_low_memory, &ad); ui_app_add_event_handler(&handlers[APP_EVENT_DEVICE_ORIENTATION_CHANGED], APP_EVENT_DEVICE_ORIENTATION_CHANGED, __ui_app_orient_changed, &ad); ui_app_add_event_handler(&handlers[APP_EVENT_LANGUAGE_CHANGED], APP_EVENT_LANGUAGE_CHANGED, __ui_app_lang_changed, &ad); ui_app_add_event_handler(&handlers[APP_EVENT_REGION_FORMAT_CHANGED], APP_EVENT_REGION_FORMAT_CHANGED, __ui_app_region_changed, &ad); ret = ui_app_main(argc, argv, &event_callback, &ad); if (ret != APP_ERROR_NONE) controller_log(DLOG_ERROR, "Function ui_app_main() failed with error = %d", ret); return ret; }
-
The application initialization procedure is executed in the __create_app() callback function, which is invoked on startup:
static bool __create_app(void *data) { appdata_s *ad = (appdata_s *)data; return controller_init(&ad->view); }
-
The controller_init() function controls the entire initialization process. This function is responsible for creating the user interface with the view_create_base_gui() function, and populating the relevant component with available languages.
bool controller_init(viewdata_s *vd) { if (!view_create_base_gui(vd, __get_display_language_cb)) return false; if (!model_get_available_locale(__get_available_locale_cb, (void *)vd)) return false; return true; }
-
The model_get_available_locale() function enumerates all languages, and the __get_available_locale_cb() callback function is invoked for each language.
For information on the model-related functions that are not listed here, see Model.
static bool __get_available_locale_cb(const char *locale, void *data) { viewdata_s *vd = (viewdata_s *)data; char fpath[512] = {0,}; char *res_path = app_get_resource_path(); // Limit the number of languages to those which are supported by this application // To do that, the existence of relevant translation file is checked snprintf(fpath, sizeof(fpath), "%slocale/%s/LC_MESSAGES/$(appName).mo", res_path, locale); free(res_path); if (access(fpath, F_OK) != -1) { controller_log(DLOG_INFO, "Language supported: %s", locale); view_add_locale(vd, locale); } return true; }
Within the __get_available_locale_cb() callback function, the relevant component is populated using the view_add_locale() function.
The view-related source code is not listed here, as it is not a subject of this topic.
Text Translation
The application demonstrates 2 different approaches to the text string translation:
- Hooking the application APP_EVENT_LANGUAGE_CHANGED event.
The event's relevant callback function is invoked when the display language is changed in Settings > Language and input > Display language. This is a global language change procedure. It affects the entire mobile platform and all of its applications.
- Selecting a language in a list.
Once the language change is confirmed using the Apply button, the designated text strings are translated. This is a local language change procedure, as it affects only this application and does not influence any other component on the mobile platform.
Both methods require that the string translation files are available.
Handling Global Language Changes
To change the language globally:
-
Once the display language is globally changed, the __ui_app_lang_changed() callback function is invoked (for event hooking with the __ui_app_lang_changed() callback function, see Application Initialization).
static void __ui_app_lang_changed(app_event_info_h event_info, void *user_data) { appdata_s *ad = (appdata_s *)user_data; char *language = NULL; if (!model_get_locale_language(&language)) return; view_set_language(&ad->view, language); free(language); return; }
-
When the __ui_app_lang_changed() callback function is invoked, the current language has to be obtained from the system settings, where the Settings application stores the user selection regarding the display language. This operation is wrapped by the model_get_locale_language() function.
The display language setting is stored as a key-value pair, where the valid key is identified as the SYSTEM_SETTINGS_KEY_LOCALE_LANGUAGE constant value. As a result, the valid localization code is returned, such as en_US for US English, and ko_KR for Korean.
bool model_get_locale_language(char **language) { int ret = system_settings_get_value_string(SYSTEM_SETTINGS_KEY_LOCALE_LANGUAGE, language); if (ret != SYSTEM_SETTINGS_ERROR_NONE) { controller_log(DLOG_ERROR, "Function system_settings_get_value_string() failed with error %d.", ret); return false; } return true; }
-
Once the valid localization code is obtained, it is passed to the view_set_language() function, where the elm_language_set() function applies the language to the application's text strings.
bool view_set_language(viewdata_s *vd, const char *locale) { if (!locale) { controller_log(DLOG_ERROR, "Invalid input arguments."); return false; } // This portion of code appends the ".UTF-8" string to the localization code which is required by the elm_language_set() function int str_len = strlen(locale) + strlen(".UTF-8") + 1; char *localization_string = (char *)calloc(str_len, sizeof(char)); snprintf(localization_string, str_len, "%s.UTF-8", locale); controller_log(DLOG_INFO, "Language changed to: %s (%s)", locale, localization_string); elm_language_set(localization_string); free(localization_string); // Text strings are translated using the i18n functions __view_display_text_translation(vd); return true; }
-
The final text translation and display is performed by the __view_display_text_translation() function, where the text IDs (IDS_BODY_EXAMPLE_TEXT and IDS_BODY_HELLO_WORLD) are used together with the i18n_get_text() function.
static void __view_display_text_translation(viewdata_s *vd) { elm_object_part_text_set(vd->layout_text_middle, PART_TEXT_MIDDLE_LABEL_TOP, i18n_get_text("IDS_BODY_EXAMPLE_TEXT")); elm_object_part_text_set(vd->layout_text_middle, PART_TEXT_MIDDLE_LABEL_BOTTOM, i18n_get_text("IDS_BODY_HELLO_WORLD")); }
The i18n_get_text() function is responsible for the proper text string selection from the .po files according to the current language setting.
Handling Local Language Changes
Once the language is changed locally, the __view_apply_button_click_cb() callback function is called within the View module.
static void __view_apply_button_click_cb(void *data, Evas_Object *obj, void *event_info) { // Localization code is obtained from the language selection component const char *locale = elm_object_text_get(__viewdata->hoversel); viewdata_s *vd = (viewdata_s *)data; if (locale) view_set_language(vd, locale); }
Localization code obtained from the language selection component is passed to the view_set_language() function as a parameter. Further processing is the same as described in the global language change procedure.
Model
The Model module operates directly on the i18n (localization) API. The additional benefit of this module is the simplification of the API function calling, error checking, and message logging.
The model_get_locale_language() function implemented within the Model module has been briefly described in Handling Global Language Changes. Other functions are presented here:
-
The model_get_available_locale() function enumerates all languages with respect to their localization information. For each localization code, the callback function of the get_available_locale_cb type is called.
bool model_get_available_locale(get_available_locale_cb func_cb, void *data) { if (!func_cb) { controller_log(DLOG_ERROR, "Wrong argument provided."); return false; } // Number of available locales is acquired int i; int32_t count = i18n_ulocale_count_available(); for (i = 0; i < count; i++) { // Localization code is obtained by its index const char *locale = i18n_ulocale_get_available(i); // For each localization code obtained, the provided callback function is called if (locale && !func_cb(locale, data)) break; } return true; }
-
The model_get_locale_display_language() function obtains the readable display language name based on its localization code:
bool model_get_locale_display_language(const char *locale, char **display_language) { i18n_uchar i18n_language[256] = {0,}; if (!display_language) { controller_log(DLOG_ERROR, "Wrong argument provided."); return false; } // Readable display language string is obtained from the localization code i18n_ulocale_get_display_language(locale, NULL, i18n_language, sizeof(i18n_language)); int ret = get_last_result(); if (ret != I18N_ERROR_NONE) { controller_log(DLOG_ERROR, "Function i18n_ulocale_get_display_language() failed with error %d.", ret); return false; } // Returned display language string is stored in an array of i18n_uchar, // which is represented by the 16-bit unsigned int value, so it can not be simply // typecasted to the char string. The transformation to the char string // requires the i18n relevant function application. To store // the display language in a char string, a new memory area is reserved int32_t str_len = i18n_ustring_get_length(i18n_language); *display_language = (char *)calloc(str_len + 1, sizeof(char)); if (!(*display_language)) { controller_log(DLOG_ERROR, "Function calloc() failed."); return false; } // Display language string of type i18n_uchar is copied to the char string // while the 8 higher bits are truncated from each of the i18n_uchar character i18n_ustring_copy_au_n(*display_language, i18n_language, str_len); ret = get_last_result(); if (ret != I18N_ERROR_NONE) { free(*display_language); *display_language = NULL; controller_log(DLOG_ERROR, "Function i18n_ustring_copy_au_n() failed with error %d.", ret); return false; } return true; }