W pewnym momencie tworzenia aplikacji na system android zachodzi potrzeba rozbudowania pewnych funkcji. W systemie android nie każdą funkcję da się w łatwy sposób zaimplementować. Jedną z ważniejszych funkcji jest obsługa kilku języków w aplikacji. Niestety API androida nie daje łatwego sposobu na przełączanie się między językami. Android domyślnie korzysta z ustawień regionalnych urządzenia aby wybrać odpowiednie zasoby zależne od języka. Przeważnie takie zachowanie wystarcza do powszechnych zastosowań, jednak czasami zachodzą potrzeby zmiany języka niezależnie od ustawień systemu. Sama zmiana języka w Android jest łatwa z punktu użytkownika, ale niekoniecznie od strony programisty. Ten wpis pokarze Ci w jaki sposób zaprogramować tę funkcjonalność.
1. Zmiana języka w Android
Na potrzeby wpisu przyjmiemy, że mamy dwa języki Angielski i Polski. API >= 21. Generalnie mamy dwa problemy do rozwiązania:
- Aplikacja musi pamiętać który język wybraliśmy po zamknięciu (zniszczenie procesu) aplikacji.
- Po zmianie języka UI musi zostać zaktualizowany.
2. LocaleManager
Pierwsze co potrzebujemy to klasę która nam będzie w odpowiedni sposób zmieniała język (Locale) i zapisywała go do SharedPreferences.
import android.content.Context; import android.content.SharedPreferences; import android.content.res.Configuration; import android.content.res.Resources; import android.os.Build; import android.preference.PreferenceManager; import java.util.Locale; public class LocaleManager { public static final String LANGUAGE_ENGLISH = "en"; public static final String LANGUAGE_POLISH = "pl"; private static final String LANGUAGE_KEY = "language_key"; public static Context setLocale(Context c) { return updateResources(c, getLanguage(c)); } public static Context setNewLocale(Context c, String language) { saveLanguage(c, language); return updateResources(c, language); } public static String getLanguage(Context c) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(c); return prefs.getString(LANGUAGE_KEY, LANGUAGE_ENGLISH); } private static void saveLanguage(Context c, String language) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(c); prefs.edit().putString(LANGUAGE_KEY, language).apply(); } private static Context updateResources(Context context, String language) { Locale locale = new Locale(language); Locale.setDefault(locale); Resources res = context.getResources(); Configuration config = new Configuration(res.getConfiguration()); config.setLocale(locale); context = context.createConfigurationContext(config); return context; } }
3. BaseActivity
Tą klasę rozszerzamy o Activity (jeżeli nie korzystamy z support library) i będzie ona klasą podstawową która zmienia nam język aktywności – metoda attachBaseContext.
import android.app.Activity; import android.content.Context; import android.content.pm.PackageManager; import android.os.Bundle; import static android.content.pm.PackageManager.GET_META_DATA; public class BaseActivity extends Activity { @Override protected void attachBaseContext(Context base) { super.attachBaseContext(LocaleManager.setLocale(base)); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setTitleActionBar(); } private void setTitleActionBar() { try { int label = getPackageManager().getActivityInfo(getComponentName(), GET_META_DATA).labelRes; if (label != 0) { setTitle(label); } } catch (PackageManager.NameNotFoundException e) { return; } } }
4. MainActivity
import android.content.Context; import android.content.Intent; import android.content.res.Resources; import android.os.Bundle; import android.widget.Button; import android.widget.TextView; public class MainActivity extends BaseActivity { Button btnEN, btnPL; TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btnEN = findViewById(R.id.btnEn); btnPL = findViewById(R.id.btnPl); textView = findViewById(R.id.text); btnEN.setOnClickListener(view -> refreshAuto(LocaleManager.LANGUAGE_ENGLISH)); btnPL.setOnClickListener(view -> refreshAuto(LocaleManager.LANGUAGE_POLISH)); btnEN.setOnLongClickListener(view -> refreshManual(LocaleManager.LANGUAGE_ENGLISH)); btnPL.setOnLongClickListener(view -> refreshManual(LocaleManager.LANGUAGE_POLISH)); textView.setText(R.string.hello); } private boolean refreshManual(String lang) { Context context = LocaleManager.setNewLocale(this,lang); Resources resources = context.getResources(); textView.setText(resources.getString(R.string.hello)); return true; } public void refreshAuto(String lang) { LocaleManager.setNewLocale(this,lang); Intent refresh = new Intent(this, MainActivity.class); startActivity(refresh); finish(); } }
Prosty przykład aktywności która jest właśnie rozszerzona o BaseActivity w której to następuje zmiana języka. W niej mamy dwie metody w zależności od reakcji na przycisk. Jeżeli korzystamy z metody refreshManual tutaj zmieniamy ręcznie wszystkie pola które muszą zostać przetłumaczone. Więc musimy się sami martwić o „nadpisywanie” wszystkich pól które występują w aktywności. Natomiast metoda refreshAuto restartuje aktywność. W niej nie musimy się martwić o zmianę pól ponieważ automatycznie to nastąpi.
5. Jak wymusić język aplikacji?
No własnie co zrobić? Co jeśli system jest po polsku, a aplikację chcemy po angielsku mimo, że jest dostępne tłumaczenie po polsku? Jak już wiesz system po polsku to i aplikacja po polsku (jeśli jest tłumaczenie). Z różnych przyczyn chcesz mieć cały czas w wersji angielskiej. Masz dwa rozwiązania:
- Usunąć tłumaczenie po polsku (najszybsza metoda pod względem wydajności).
- Wymusić to programowo.
Jeżeli chcesz wymusić to programowo stwórz klasę:
import android.app.Application; import android.content.Context; import android.content.res.Configuration; public class App extends Application { @Override protected void attachBaseContext(Context base) { super.attachBaseContext(LocaleManager.setNewLocale(base,LocaleManager.LANGUAGE_ENGLISH)); } }
W pliku manifest dodaj:
android:name=".App"
w sekcji <application … />
6. Podsumowanie
Jak widzisz zmiana języka programowo nie jest taką łatwą sprawą, ponieważ trzeba zmierzyć się z kilkoma problemami. Jest wiele sposobów na zmianę języka. Osobiście uważam, że ten sposób jest najbardziej optymalny, ponieważ nie trzeba martwić się o zmianę konfiguracji (np.: gdy obrócimy ekran). Cały projekt może znaleźć tutaj.