Zmiana języka w Androidzie

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:

  1. Aplikacja musi pamiętać który język wybraliśmy po zamknięciu (zniszczenie procesu) aplikacji.
  2. Po zmianie języka UI musi zostać zaktualizowany.

zmiana języka w android

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:

  1. Usunąć  tłumaczenie po polsku (najszybsza metoda pod względem wydajności).
  2. 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.

Co dalej?

  • Polub stronę MYENV na Facebooku oraz śledź mnie na Twitterze
  • Zachęcam do komentowania i pisania propozycji tematów, o których chcesz przeczytać
  • Poleć ten wpis za pomocą poniższych przycisków. Będę Ci za to bardzo wdzięczny 🙂
  • Życzę Ci miłego dnia i miłego kodowania 🙂