Language Change UI Sample Overview

Mobile native

The [UI Sample] LanguageChange sample application demonstrates how to implement basic GNU internationalization and localization as well as the Elementary translatable text APIs family.

Basically, Tizen native applications are supposed to support multiple languages for worldwide users. This sample demonstrates how to support multiple languages on your application. Basically, the text translation is handled with GNU internationalization and localization (gettext()). Elementary provides additional utility functions for dynamic language change, which means your application texts can be easily updated with the current locale at runtime.

The following Elementary functions are supported. For more information on the translatable text functions, see the Elementary documentation.

  • Elementary UI component functions:
    elm_object_translatable_text_set();
    elm_object_translatable_part_text_set();
    elm_object_translatable_part_text_get();
    elm_object_translatable_text_get();
    elm_object_part_text_translatable_set();
    
  • Elementary UI component item functions:
    elm_object_item_translatable_text_set();
    elm_object_item_translatable_part_text_set();
    elm_object_item_translatable_part_text_get();
    elm_object_item_translatable_text_get();
    elm_object_item_part_text_translatable_set();
    

If your application is supposed to support multiple languages, prepare text data for the languages with po files.

Basically, every text in the po must consist of the msgid and msgstr fields. The msgid represents the value to be translated to the msgstr value. The i18n_get_text() function gets the actual msgstr value from the msgid field. So if your application displays a multi-language text, write the text with the msgid value and the actual translated text. In the meantime, set the texts with the Elementary translatable text APIs in your application.

Figure: Using translatable text APIs

Using translatable text APIs

Implementation

The app_create() function constructs the base GUIs for the application layout by going through 2 functions: create_base_gui() and create_view().

static bool
app_create(void *data)
{
   // Hook to take necessary actions before main event loop starts
   // Initialize UI resources and application data
   // If this function returns true, the main loop of application starts
   // If this function returns false, the application is terminated
   appdata_s *ad = data;

   create_base_gui(ad);
   create_view(ad);

   ecore_timer_add(1, timer_cb, ad);

   return true;
}

Additionally, the app_create() function calls the ecore_timer_add() function to simply increase a value every second.

static Eina_Bool
timer_cb(void *data)
{
   appdata_s *ad = data;
   ad->count++;

   return ECORE_CALLBACK_RENEW;
}

The application creates the elm_window, elm_conformant, and elm_naviframe components for the basic layouts in the create_base_gui() function.

static void
create_base_gui(appdata_s *ad)
{
   // Window
   ad->win = elm_win_util_standard_add(PACKAGE, PACKAGE);
   elm_win_conformant_set(ad->win, EINA_TRUE);
   elm_win_autodel_set(ad->>win, EINA_TRUE);

   if (elm_win_wm_rotation_supported_get(ad->win)) 
   {
      int rots[4] = {0, 90, 180, 270};
      elm_win_wm_rotation_available_rotations_set(ad->win, (const int *)(&rots), 4);
   }

   evas_object_smart_callback_add(ad->win, "delete,request", win_delete_request_cb, NULL);

   // Conformant
   ad->conform = elm_conformant_add(ad->win);
   evas_object_size_hint_weight_set(ad->conform, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
   elm_win_resize_object_add(ad->win, ad->conform);
   evas_object_show(ad->conform);

   // Naviframe
   ad->naviframe = elm_naviframe_add(ad->conform);
   eext_object_event_callback_add(ad->naviframe, EEXT_CALLBACK_BACK, nf_back_cb, ad);
   elm_object_content_set(ad->conform, ad->naviframe);

   // Show the window after the base GUI is set up
   evas_object_show(ad->win);
}

At this moment, the elm_naviframe component has no views. In the create_view() function, it creates one view as the naviframe view item. The view consists of the entry, genlist and button. A box packs them in its own container. The following figure illustrates the final layout structure.

Figure: Layout structure

Layout structure

Setting Translatable Texts

You can set the translatable texts to each UI component – elm_entry, elm_genlist, elm_button, and elm_naviframe.

static void
create_view(appdata_s *ad)
{
   // Entry
   entry = elm_entry_add(box);
   evas_object_smart_callback_add(entry, "language,changed", entry_language_changed_cb, ad);
   evas_object_size_hint_align_set(entry, EVAS_HINT_FILL, 0);
   // Basically, use i18n_get_text() to get the current language text
   snprintf(buf, sizeof(buf), "%s: %d", i18n_get_text("IDS_CURRENT"), ad->count);
   elm_entry_entry_set(entry, buf);
   elm_entry_editable_set(entry, EINA_FALSE);
   evas_object_show(entry);
}

elm_entry Component

First, work on the elm_entry set. The sample shows the basic entry usage, and 2 additional issues: the i18n_get_text() function and ad->count.

The application calls the evas_object_smart_callback_add() function with the language,changed event type to get the callback call when the entry has a notice - the language is going change. Next, it sets the text with the elm_entry_entry_set() function, but constructs a string with the i18n_get_text() function and ad->count.

The ad->count is not noticeable, because it is only intended to display a text that is composed with a number. However, the i18n_get_text() function is important, since it is used to get the translated text based on the current locale. If the current locale is set to English, the i18n_get_text() function takes it's localized data.

When the system language is changed, the elm_entry handles the change with the entry_language_changed_cb() callback, where you can reset the text with the i18n_get_text() function based on the changed locale information.

static void
entry_language_changed_cb(void *data, Evas_Object *obj, void *event_info)
{
   // Get the language changed callback call by UI component
   // If you need to apply some markups to text,
   // use the i18n_get_text() in the language,changed smart callback
   appdata_s *ad = data;
   char buf[128];
   snprintf(buf, sizeof(buf), "%s: %d", i18n_get_text("IDS_CURRENT"), ad->count);
   elm_entry_entry_set(obj, buf);
}

Since the elm_entry component can reset a text with the current locale info every time the language is changed, you can make the text update.

elm_genlist Component

The elm_genlist is constructed as usual.

static void
create_view(appdata_s *ad)
{
   // Genlist
   genlist = elm_genlist_add(box);

   itc = elm_genlist_item_class_new();
   itc->item_style = "default";
   itc->func.text_get = gl_text_get_cb;

   for (i = 0; i < 100; i++) 
   {
      elm_genlist_item_append(genlist, itc, NULL, NULL, ELM_GENLIST_ITEM_NONE, NULL, NULL);
   }

   elm_genlist_item_class_free(itc);

   evas_object_size_hint_weight_set(genlist, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
   evas_object_size_hint_align_set(genlist, EVAS_HINT_FILL, EVAS_HINT_FILL);
   evas_object_show(genlist);
}

Focus on the gl_text_get_cb() callback function that is called on the elm_genlist item realization.

static char *
gl_text_get_cb(void *data, Evas_Object *obj, const char *part)
{
   // In case of genlist, the text is updated in real time when the language is changed
   // No need to use any Elementary translatable_text_set() functions
   return strdup(i18n_get_text("IDS_SELECT_ITEM"));
}

The basic mechanism is the same as with the elm_entry component. Whenever items are realized, you get the current language texts by using the i18n_get_text() function. Since the elm_genlist items are re-realized when the system language is changed, you can keep the current language text in the elm_genlist items.

elm_button Component

In the elm_button component, use the elm_object_translatable_text_set() function to support the dynamic language change.

static void
create_view(appdata_s *ad)
{
   // Button
   btn = elm_button_add(box);
   // Basically, you can use elm_object_translatable_part_text_set() API
   // to support dynamic language change
   elm_object_translatable_text_set(btn, "IDS_DONE");
   evas_object_size_hint_align_set(btn, EVAS_HINT_FILL, 1);
   evas_object_show(btn);
}

Other Items

The Elementary provides basic text functions (elm_object_part_text_set() and elm_object_text_set()). However, if you want the text to be translated, call the elm_object_translatable_part_text_set() and elm_object_translatable_text_set() functions instead. Once you set the text with those functions, the text is translated automatically whenever the system language is changed. Remember that you need to pass the msgid value instead of the actual text.

Some Elementary functions are provided for adding items, such as elm_list_item_append(), elm_ctxpopup_item_append(), elm_toolbar_item_append(), and elm_naviframe_item_push(). Since these functions do not fit to the translatable text functions, Elementary supports additional functions for their item texts to be translated.

void elm_object_item_part_text_translatable_set(Elm_Object_Item *it, const char *part, Eina_Bool translatable);

After creating the item, you can make the item text translatable with the above function. Remember to pass the msgid value when you create the item. For example, to set the elm_naviframe title to be translated:

static void
create_view(appdata_s *ad)
{
   // Push a view to naviframe
   nf_it = elm_naviframe_item_push(ad->naviframe, "IDS_LANGUAGE", NULL, NULL, box, NULL);
   // Basically, you can use
   // elm_object_item_part_text_translatable_set() or
   // elm_object_item_translatable_part_text_set() API 
   // to support dynamic language change
   elm_object_item_part_text_translatable_set(nf_it, NULL, EINA_TRUE);
}

The following example shows the entire code of the create_view() function.

static void
create_view(appdata_s *ad)
{
   Evas_Object *box, *btn, *entry, *genlist;
   Elm_Object_Item *nf_it;
   Elm_Genlist_Item_Class *itc;
   char buf[128];
   int i;

   // Box
   box = elm_box_add(ad->naviframe);

   // Entry
   entry = elm_entry_add(box);
   evas_object_smart_callback_add(entry, "language,changed", entry_language_changed_cb, ad);
   evas_object_size_hint_align_set(entry, EVAS_HINT_FILL, 0);
   // Use i18n_get_text() to get the current language text
   snprintf(buf, sizeof(buf), "%s: %d", i18n_get_text("IDS_CURRENT"), ad->count);
   elm_entry_entry_set(entry, buf);
   elm_entry_editable_set(entry, EINA_FALSE);
   evas_object_show(entry);
   elm_box_pack_end(box, entry);

   // Genlist
   genlist = elm_genlist_add(box);

   itc = elm_genlist_item_class_new();
   itc->item_style = "default";
   itc->func.text_get = gl_text_get_cb;

   for (i = 0; i < 100; i++) 
   {
      elm_genlist_item_append(genlist, itc, NULL, NULL, ELM_GENLIST_ITEM_NONE, NULL, NULL);
   }

   elm_genlist_item_class_free(itc);

   evas_object_size_hint_weight_set(genlist, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
   evas_object_size_hint_align_set(genlist, EVAS_HINT_FILL, EVAS_HINT_FILL);
   evas_object_show(genlist);
   elm_box_pack_end(box, genlist);

   // Button
   // Basically, you can use elm_object_translatable_part_text_set() API
   // to support dynamic language change
   btn = elm_button_add(box);
   elm_object_translatable_text_set(btn, "IDS_DONE");
   evas_object_size_hint_align_set(btn, EVAS_HINT_FILL, 1);
   evas_object_show(btn);
   elm_box_pack_end(box, btn);

   // Push a view to naviframe
   nf_it = elm_naviframe_item_push(ad->naviframe, "IDS_LANGUAGE", NULL, NULL, box, NULL);
   // Basically, you can use
   // elm_object_item_part_text_translatable_set() or
   // elm_object_item_translatable_part_text_set() API 
   // to support dynamic language change
   elm_object_item_part_text_translatable_set(nf_it, NULL, EINA_TRUE);
}

However, you must check one more issue. Whenever the system language is changed, the application language changed callback is called.

static void
app_language_changed(void *data)
{
   // Set the current language
   char *locale = NULL;
   runtime_info_get_value_string(RUNTIME_INFO_KEY_LANGUAGE, &locale);

   // Notify Elementary to update UI component text with new language info
   elm_language_set(locale);
   free(locale);
}

In the callback, the current locale information is retrieved using the runtime_info_get_value_string() function and the locale value is passed to the elm_language_set() function directly so that the Elementary UI components texts are translated with the current locale information at the right moment.

Now everything is ready. If the user chooses another language in the setting application, you can see the sample application texts changed to the current language texts.