Comment internationaliser une application Android – Partie 2

En continuité avec la première partie publiée récemment qui affichait la date selon la langue du système Android, voici la deuxième partie du tutoriel qui va combler ses limites. Elle va essentiellement ajouter la fonctionnalité auprès de l’utilisateur de changer la langue de l’application. En plus de ça, comme plusieurs fabricants de smartphones ne supportent pas certaines langues locales comme le swahili ou le lingala, l’application que nous allons développer aura, en plus de l’anglais et du français, les langues swahili et lingala.

Comment nous abordons le concept d’internationalisation d’une application Android et que nous allons ajouter deux langues supplémentaires, nous nous intéressons encore au fichier res/values-{codeLangue}/strings.xml. Le codeLangue pour notre cas sera représenté par :

  • sw pour le Swahili : res/values-sw/strings.xml
  • ln pour le Lingala : res/values-ln/strings.xml

A l’intérieur de ces fichiers, nous mettrons la traduction, respectivement en Swahili et en Lingala, de toutes les chaînes de caractère se trouvant dans le fichier principal res/values/strings.xml que nous avions créé dans la première partie de ce tutoriel. Les sources utilisées pour la traduction sont Google Traduction, Webtrans et Wiktionary.

Mise en pratique

J’ai modifié la première version de l’application dont voici les codes sources. Vous pouvez créer un nouveau fichier comme bon vous semble.

  • Préparons les couleurs que nous allons utiliser en modifiant le fichier res/values/color.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorAccent">#a6043d</color>
    <color name="gray">#eee</color>
</resources>
  • Changeons le style de l’application. C’est le fichier res/values/styles.xml
<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="colorAccent">@color/colorAccent</item>
    </style>

</resources>
  • Comme l’application affichera la date du jour à son démarrage, nous allons mettre les mois, les jours et les textes des boutons de sélections de langue en anglais dans le fichier res/values/strings.xml
<resources>
    <string name="app_name">Multi language App</string>

    <!-- Months -->
    <string name="january">January</string>
    <string name="february">February</string>
    <string name="march">March</string>
    <string name="april">April</string>
    <string name="may">May</string>
    <string name="june">June</string>
    <string name="july">July</string>
    <string name="august">August</string>
    <string name="september">September</string>
    <string name="october">October</string>
    <string name="november">November</string>
    <string name="december">December</string>

    <!-- Days -->
    <string name="monday">Monday</string>
    <string name="tuesday">Tuesday</string>
    <string name="wednesday">Wednesday</string>
    <string name="thursday">Thursday</string>
    <string name="friday">Friday</string>
    <string name="saturday">Saturday</string>
    <string name="sunday">Sunday</string>

    <string name="to_english">English</string>
    <string name="to_french">French</string>
    <string name="to_swahili">Swahili</string>
    <string name="to_lingala">Lingala</string>
</resources>
  • Créons un nouveau fichier xml dans un nouveau dossier values-fr/ pour y mettre les mois, les jours et les textes des buttons en français. Le chemin du fichier est res/values-fr/strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>

    <!-- Months -->
    <string name="january">Janvier</string>
    <string name="february">Février</string>
    <string name="march">Mars</string>
    <string name="april">Avril</string>
    <string name="may">Mai</string>
    <string name="june">Juin</string>
    <string name="july">Juillet</string>
    <string name="august">Août</string>
    <string name="september">Septembre</string>
    <string name="october">Octobre</string>
    <string name="november">Novembre</string>
    <string name="december">Décembre</string>

    <!-- Days -->
    <string name="monday">Lundi</string>
    <string name="tuesday">Mardi</string>
    <string name="wednesday">Mercredi</string>
    <string name="thursday">Jeudi</string>
    <string name="friday">Vendredi</string>
    <string name="saturday">Samedi</string>
    <string name="sunday">Dimanche</string>

    <string name="choose_language">Langue</string>
    <string name="to_english">Anglais</string>
    <string name="to_french">Français</string>
    <string name="to_swahili">Swahili</string>
    <string name="to_lingala">Lingala</string>
</resources>
  • Créons encore une fois un nouveau fichier xml. Cette fois-ci dans un nouveau dossier values-sw/ pour y mettre les mois, les jours et les textes des boutons en Swahili. Le chemin du fichier est res/values-sw/strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>

    <!-- Months -->
    <string name="january">Januari</string>
    <string name="february">Februari</string>
    <string name="march">Machi</string>
    <string name="april">Aprili</string>
    <string name="may">Mei</string>
    <string name="june">Juni</string>
    <string name="july">Julai</string>
    <string name="august">Agosti</string>
    <string name="september">Septemba</string>
    <string name="october">Octoba</string>
    <string name="november">Novemba</string>
    <string name="december">Decemba</string>

    <!-- Days -->
    <string name="monday">Jumatatu</string>
    <string name="tuesday">Jumanne</string>
    <string name="wednesday">Jumatano</string>
    <string name="thursday">Alhamisi</string>
    <string name="friday">Idjuma</string>
    <string name="saturday">Jumamosi</string>
    <string name="sunday">Jumapili</string>

    <string name="to_english">Kingereza</string>
    <string name="to_french">Kifaransa</string>
    <string name="to_swahili">Kiswahili</string>
    <string name="to_lingala">Lingala</string>
</resources>
  • Enfin pour finir avec la préparation de l’internationalisation, créons ce fichier res/values-ln/strings.xml pour le Lingala
<?xml version="1.0" encoding="utf-8"?>
<resources>

    <!-- Months -->
    <string name="january">Sanza ya yambo</string>
    <string name="february">Sanza ya ibale</string>
    <string name="march">Sanza ya isato</string>
    <string name="april">Sanza ya inei</string>
    <string name="may">Sanza ya itano</string>
    <string name="june">Sanza ya motoba</string>
    <string name="july">Sanza ya nsambo</string>
    <string name="august">Sanza ya mwambe</string>
    <string name="september">Sanza ya libwa</string>
    <string name="october">Sanza ya zomi</string>
    <string name="november">Sanza ya zomi na yako</string>
    <string name="december">Sanza ya zomi na ibale</string>

    <!-- Days -->
    <string name="monday">Mokolo mwa mosala moko</string>
    <string name="tuesday">Mokolo mwa misala mibale</string>
    <string name="wednesday">Mokolo mwa misala misato</string>
    <string name="thursday">Mokolo mwa misala minei</string>
    <string name="friday">Mokolo mwa misala mitano</string>
    <string name="saturday">Mokolo mwa mposo</string>
    <string name="sunday">Mokolo mwa eyenga</string>

    <string name="to_english">Anglais</string>
    <string name="to_french">Français</string>
    <string name="to_swahili">Swahili</string>
    <string name="to_lingala">Lingala</string>
</resources>
  • Modifions la présentation de l’application. C’est le fichier res/layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/gray"
    tools:context="sergekishiko.rootandadmin.applicationmultilangue.MainActivity">

    <TextView
        android:id="@+id/today"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:textColor="@color/colorAccent"
        android:textSize="20sp"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_alignParentBottom="true">

        <Button
            android:id="@+id/btn_toEnglish"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="@string/to_english"/>

        <Button
            android:id="@+id/btn_toFrench"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="@string/to_french"/>

        <Button
            android:id="@+id/btn_toSwahili"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="@string/to_swahili"/>

        <Button
            android:id="@+id/btn_toLingala"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="@string/to_lingala"/>

    </LinearLayout>
</RelativeLayout>
  • En se référant à l’exemple de Gunhan Sancar expliquant la même thématique, voici sa classe qui permet d’effectuer le changement de la langue à partir de l’application que nous allons placée dans le package helper
package sergekishiko.rootandadmin.applicationmultilangue.helper;

import android.annotation.TargetApi;
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;

/**
 * This class is used to change your application locale and persist this change for the next time
 * that your app is going to be used.
 * <p/>
 * You can also change the locale of your application on the fly by using the setLocale method.
 * <p/>
 * Created by gunhansancar on 07/10/15.
 */
public class LocaleHelper {

    private static final String SELECTED_LANGUAGE = "Locale.Helper.Selected.Language";

    public static Context onAttach(Context context) {
        String lang = getPersistedData(context, Locale.getDefault().getLanguage());
        return setLocale(context, lang);
    }

    public static Context onAttach(Context context, String defaultLanguage) {
        String lang = getPersistedData(context, defaultLanguage);
        return setLocale(context, lang);
    }

    public static String getLanguage(Context context) {
        return getPersistedData(context, Locale.getDefault().getLanguage());
    }

    public static Context setLocale(Context context, String language) {
        persist(context, language);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            return updateResources(context, language);
        }

        return updateResourcesLegacy(context, language);
    }

    private static String getPersistedData(Context context, String defaultLanguage) {
        SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
        return preferences.getString(SELECTED_LANGUAGE, defaultLanguage);
    }

    private static void persist(Context context, String language) {
        SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
        SharedPreferences.Editor editor = preferences.edit();

        editor.putString(SELECTED_LANGUAGE, language);
        editor.apply();
    }

    @TargetApi(Build.VERSION_CODES.N)
    private static Context updateResources(Context context, String language) {
        Locale locale = new Locale(language);
        Locale.setDefault(locale);

        Configuration configuration = context.getResources().getConfiguration();
        configuration.setLocale(locale);

        return context.createConfigurationContext(configuration);
    }

    @SuppressWarnings("deprecation")
    private static Context updateResourcesLegacy(Context context, String language) {
        Locale locale = new Locale(language);
        Locale.setDefault(locale);

        Resources resources = context.getResources();

        Configuration configuration = resources.getConfiguration();
        configuration.locale = locale;

        resources.updateConfiguration(configuration, resources.getDisplayMetrics());

        return context;
    }
}
  • Dans le fichier java, mettons ces codes assurant le fonctionnement de l’application
package sergekishiko.rootandadmin.applicationmultilangue;

import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import java.util.Calendar;

import sergekishiko.rootandadmin.applicationmultilangue.helper.LocaleHelper;

public class MainActivity extends Activity implements View.OnClickListener {
    private TextView today;
    private Button toEnglish, toFrench, toSwahili, toLingala;
    private static final String LOCALE_ENGLISH = "en";
    private static final String LOCALE_FRENCH = "fr";
    private static final String LOCALE_SWAHILI = "sw";
    private static final String LOCALE_LINGALA = "ln";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        toEnglish = (Button) findViewById(R.id.btn_toEnglish);
        toFrench = (Button) findViewById(R.id.btn_toFrench);
        toSwahili = (Button) findViewById(R.id.btn_toSwahili);
        toLingala = (Button) findViewById(R.id.btn_toLingala);

        toEnglish.setOnClickListener(this);
        toFrench.setOnClickListener(this);
        toSwahili.setOnClickListener(this);
        toLingala.setOnClickListener(this);

        // Liaison de la variable au composant xml et définition de la date du jour
        today = (TextView) findViewById(R.id.today);
        today.setText(format(getResources(), Calendar.getInstance(), LocaleHelper.getLanguage(this)));
    }

    /**
     * Récuperer la chaine de caractere du fichier strings.xml en fonction du jour
     * @param resources les ressources mises a jour
     * @param day constante de la classe Calendar représentant un jour
     * @return un jour de la semaine
     */
    public String dayToString(Resources resources, int day) {
        switch (day) {
            case Calendar.MONDAY :
                return resources.getString(R.string.monday);
            case Calendar.TUESDAY :
                return resources.getString(R.string.tuesday);
            case Calendar.WEDNESDAY :
                return resources.getString(R.string.wednesday);
            case Calendar.THURSDAY :
                return resources.getString(R.string.thursday);
            case Calendar.FRIDAY :
                return resources.getString(R.string.friday);
            case Calendar.SATURDAY :
                return resources.getString(R.string.saturday);
            default:
                return resources.getString(R.string.sunday);
        }
    }

    /**
     * Récuperer la chaine de caractere du fichier strings.xml en fonction du mois
     * @param resources les ressources mises a jour
     * @param month constante de la classe Calendar représentant un mois
     * @return un mois de l'année
     */
    public String monthToString(Resources resources, int month) {
        switch (month) {
            case Calendar.JANUARY :
                return resources.getString(R.string.january);
            case Calendar.FEBRUARY :
                return resources.getString(R.string.february);
            case Calendar.MARCH :
                return resources.getString(R.string.march);
            case Calendar.APRIL :
                return resources.getString(R.string.april);
            case Calendar.MAY :
                return resources.getString(R.string.may);
            case Calendar.JUNE :
                return resources.getString(R.string.june);
            case Calendar.JULY :
                return resources.getString(R.string.july);
            case Calendar.AUGUST :
                return resources.getString(R.string.august);
            case Calendar.SEPTEMBER :
                return resources.getString(R.string.september);
            case Calendar.OCTOBER :
                return resources.getString(R.string.october);
            case Calendar.NOVEMBER :
                return resources.getString(R.string.november);
            default:
                return resources.getString(R.string.december);
        }
    }

    /**
     * Formatter la date en fonction du code de la langue
     * @param resources les ressources mises a jour
     * @param calendar L'instance de la classe Calendar
     * @param f Le code de la langue "fr", "en" ...
     * @return Date formattée
     */
    public String format(Resources resources, Calendar calendar, String f) {
        int dayOfWeek = calendar.DAY_OF_WEEK;
        int dayOfMonth = calendar.DAY_OF_MONTH;
        int month = calendar.MONTH;
        int year = calendar.YEAR;

        String date;

        // En considerant que seul l'Anglais a un format particulier d'affichage de la date
        if (f.equals("en")) {
            date = dayToString(resources, calendar.get(dayOfWeek)) + ", " + monthToString(resources, calendar.get(month)) + " " + calendar.get(dayOfMonth) + ", " + calendar.get(year);
        }
        else {
            date = dayToString(resources, calendar.get(dayOfWeek)) + " " + calendar.get(dayOfMonth) + " " + monthToString(resources, calendar.get(month)) + " " + calendar.get(year);
        }

        return date;
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.btn_toEnglish:
                updateStringValues(LOCALE_ENGLISH);
                break;
            case R.id.btn_toFrench:
                updateStringValues(LOCALE_FRENCH);
                break;
            case R.id.btn_toSwahili:
                updateStringValues(LOCALE_SWAHILI);
                break;
            case R.id.btn_toLingala:
                updateStringValues(LOCALE_LINGALA);
                break;
            default:
                break;
        }
    }

    /**
     * Mettre a jour les ressources et l'affichage
     * @param languageCode Le code de la langue
     */
    private void updateStringValues(String languageCode) {
        Resources newResources = getUpdatedContext(languageCode).getResources();

        today.setText(format(newResources, Calendar.getInstance(), LocaleHelper.getLanguage(this)));
    }

    /**
     * Recuperer le contexte mis a jour en changeant la langue
     * @param lang Le code de la langue a changer
     * @return
     */
    private Context getUpdatedContext(String lang) {
        return LocaleHelper.setLocale(this, lang);
    }

    @Override
    protected void attachBaseContext(Context newBase) {
        super.attachBaseContext(LocaleHelper.onAttach(newBase));
    }
}

Exécutez et observez le résultat.

Nous venons de faire une application si simple mais vraiment utile, supportant quatre langues différentes : Anglais, Français, Swahili et Lingala. En appliquant le principe, vous pouvez ajouter n’importe quelles langues, à condition que cette dernière soit prise en charge par le SDK Android et pourquoi pas l’intégrer à votre grand projet si vous souhaitez que l’application que vous voulez réaliser supporte plusieurs langues.

Vos feedbacks sont les bienvenus et voici les projets de deux parties du tutoriel :

About Serge Kishiko

Développeur web et mobile fullstack. Langages préférés : Java, PHP, HTML & CSS et bientôt Python !

Check Also

Bye Bye ApocalypseVM Ransomware

Vous étés victime d’un RANSOMWARE appeler ApocalypseVM, Comment faire pour decrypter vos fichiers? La firme …

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *