23.04.11

Нигерийские принцы совсем обнаглели. Даже уже не пытаются придумать истории про переворот, побег, чемоданы с золотом партии, перестрелки в джунглях и т.п.

Вчера получил это:
Congrats! Be Informed that, £1,500,000 British Pounds has been Awarded to your Email held in April, 2011;
Fill Form;
Name....
Address...
Country...
Tel...
We await your Response

А сегодня этот шедевр лаконичности:
Your email address have won 2.5 million pounds.Names:Address:Country:Age:
Измельчали, измельчали

10.04.11

Андроид: Поддержка нескольких языков и переключение их из настроек приложения.

Нелегкая судьба андроид-программиста заставила меня принять участие в Контесте андроид-разработчиков, проводимом HTC.

По условиям конкурса приложения должны либо быть на украинском языке, либо поддерживать несколько языков, включая русский и украинский. Поскольку я собирался публиковать приложение на маркете, и предполагал, что пользоваться программой будут не только пользователи из Украины, то вариант только с локализацией на украинский мне не подходил.

До сих пор, я в своих приложениях ограничивался только двумя языками - английским, который устанавливал, как язык по умолчанию, и русским. Андроид до сих пор не поддерживает украинскую локаль, что, безусловно, является полным аллес капутом.

А еще хотелось, чтобы пользователь мог переключать язык из настроек приложения.

Cyanogenmod поддерживает возможность установки украинской локали. Однако, по понятным причинам, не стоит ожидать, что жюри в конкурсе HTC будет использовать устройства с цианогеном.

Я остался перед выбором:

  • в качестве языка по умолчанию использовать украинский. После окончания конкурса заменить его на английский, а русский и украинский оставить тем, у кого включена соответствующая локаль.
  • забыть про системные средства локализации, сделать свой менеджер локализации. 

Надо ли говорить, что второй вариант мне не нравился совсем, особенно учитывая, что андроид позволяет хранить в ресурсах массивы строк, которые очень легко локализуются стандартными средствами, но для реализации их собственноручно, пришлось бы усложнять свой паровоз.

Поэтому я остановился на 1-м варианте. Но не сразу. Я таки сделал маленький mock-менеджер локализации и попробовал как он работает. Получилось отвратительно. Но! Благодаря этому я получил в настройках приложения опцию Language, которую мне не хотелось выбрасывать.

И я стал думать дальше. В процессе размышлений я дошел до класса Locale и его метода setDefault().  Сам по себе он не помог, поскольку менеджер ресурсов андроида использует локаль из своих настроек. Но и эти настройки можно изменить.

Итак, что я сделал.
В ресурсы я добавил два массива для окна настроек:

<string-array name="languages">
    <item>English</item>
    <item>Українська</item>  
    <item>Русский</item>
</string-array>
 
<string-array name="language_values">
    <item>en</item>
    <item>uk</item>
    <item>ru</item>
</string-array>

GUI для выбора языка я поместил в стандартное PreferenceActivity.
Моя PreferenceActivity реализует интерфейс OnSharedPreferenceChangeListener . В метод onSharedPreferenceChanged помещаем следующий код:

String lang = prefs.getString(key, Consts.DEFAULT_LOCALE);
Utils.setLocale(this, lang);
Const.DEFAULT_LOCALE - это просто строка "en".
И вот в методе Utils.setLocale() и происходит волшебство, размером всего в 5 строчек кода.

public static void setLocale(Activity a, String lang) {
    Locale locale2 = new Locale(lang);
    Locale.setDefault(locale2);
    Configuration c = new Configuration(a.getBaseContext().getResources().getConfiguration());
    c.locale = locale2;
    a.getBaseContext().getResources().updateConfiguration(c, a.getBaseContext().getResources().getDisplayMetrics());
}
locale2 - это наша новая локаль, создаваемая с переданным кодом языка. Код берется из массива language_values. Сперва мы устанавливаем ее как локаль по умолчанию для Java, а затем изменяем конфигурацию менеджера ресурсов андроида. Благодаря этому при загрузке ресурсов будут использоваться ресурсы, соответствующие новой локали. Все активити, созданные после этого будут использовать ресурсы в только-что установленной локали. Правда, все активити, которые уже созданы не поменяются.

На данный момент, я просто предлагаю пользователю перезагрузить приложение, если он изменил язык. Если он не желает этого делать, то по мере того, как система будет убивать и создавать активити, они будут переключаться на новый язык.

Ах, да. Самое главное. В основной активити, которая запускается первой (ну и вообще в любой активити, которая может быть запущена интентом) я вызываю следующий метод в onCreate(), сразу после super(): Utils.setLocale(this);. Это перегруженный метод setLocale, который берет текущий язык приложения из SharePreferences. Выглядит он так:

public static void setLocale(Activity a) {
    SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(a);
    setLocale(a, prefs.getString(Consts.PREF_LOCALE, Consts.DEFAULT_LOCALE));
}
Теперь мое приложение поддерживает локализацию на три языка - английский, украинский и русский, использует только стандартные средства системы андроид и позволяет пользователю менять язык приложения на лету через окно настроек. PROFIT!!!