+9

Đa ngôn ngữ cho ứng dụng Android - Multi-Language Android App

Mở đầu

Mạng Internet đã rút ngắn khoảng cách giữa mọi người trên thế giới nên việc một ứng dụng của bạn được dùng bởi nhiều người ở các quốc giá khác nhau không phải là điều quá xa lạ. Để ứng dụng của bạn có thể cung cấp trải nghiệm tốt nhất cho người dùng thì một trong số đó là hỗ trợ đa ngôn ngữ (Supporting Different Languages).

Android hiện này đã hỗ trợ khá tốt cho việc này và hôm nay mình sẽ giới thiệu với các bạn một vài điều cơ bản nhất để hỗ trợ đa ngôn ngữ cho ứng dụng Android.

Phân loại

Theo mình thì đa ngôn ngữ cho ứng dụng Android sẽ được chia thành hai loại như sau:

  • Loại 1: Ứng dụng hỗ trợ đa ngôn ngữ, nhưng bản thân ứng dụng KHÔNG có chức năng chuyển ngôn ngữ. Ngôn ngữ của ứng dụng sẽ thay đổi theo ngôn ngữ của hệ thống.
  • Loại 2: Ứng dụng hỗ trợ đa ngôn ngữ và chức năng chuyển đổi ngôn ngữ.`Ngôn ngữ của ứng dụng sẽ độc lập với ngôn ngữ của hệ thống.

Về cơ bản, để xử lý loại 1 thì các bạn chỉ cần thêm các resources cho ngôn ngữ mà bạn hỗ trợ là xong. Khi ngôn ngữ hệ thống bị thay đổi thì ứng dụng cũng sẽ tự động cập nhật sang ngôn ngữ mới (nếu có).

Đối với loại 2, ngoài việc phải thêm các resources cho ngôn ngữ mà bạn hỗ trợ như loại 1, thì bạn còn phải xử lý ngôn ngữ trong ứng dụng để ngôn ngữ ứng dụng độc lập với ngôn ngữ hệ thống. Nên đánh giá khách quan thì loại 1 sẽ đơn giản hơn loại 2.

Một cách vui vẻ thì bạn có thể gọi loại 1 là đa ngôn ngữ bị động vì bạn sẽ không trực tiếp quản lý việc thay đổi ngôn ngữ, mà hệ thống sẽ làm việc này thay bạn. Và đương nhiên loại 2 là đa ngôn ngữ chủ động vì bạn cần phải tự xử lý việc thay đổi ngôn ngữ 😃.

Video

Demo change language PASSIVE

Demo change language ACTIVE

Android quản lý ngôn ngữ như thế nào?

Android sử dụng object Locale để quản lý ngôn ngữ. Các bạn có thể chạy debug đoạn code sau để có thể get được các thông tin của locale hiện tại.

Localce locale = context.getResources().getConfiguration().locale;

Default Resources là gì?

Khi bạn tạo một project Android thì Android Studio sẽ tự động tạo cho bạn một thư mục res có chứa file res/values/string.xml default, đây là một trong những Default Resources của ứng dụng để khai báo string. Có nhiều bạn lầm tưởng rằng file res/values/string.xml là dành cho English, điều này KHÔNG CHÍNH XÁC.

Để mình giải thích thêm, nếu ứng dụng của bạn chỉ có một ngôn ngữ duy nhất thì bạn có thể cho tất cả các string vào trong file res/values/string.xml default này. mà không vấn đề gì. Nếu ngôn ngữ là Englsih thì các string là English, nếu ngôn ngữ là Tiếng Việt thì các stirng trong đó có value là Tiếng Việt (còn key thì vẫn được khuyên là nên đặt theo English thì mình không bàn nữa).

Tuy nhiên, nếu ứng dụng của bạn có từ hai ngôn ngữ trở lên thì bạn cần phải xác định ngôn ngữ nào trong các ngôn ngữ ấy sẽ là ngôn ngữ mặc định của ứng dụng. Ví dụ nếu ứng dụng của bạn chỉ có hai ngôn ngữ là English và Tiếng Việt, vậy khi một người Nhật hay một người Nga dùng ứng dụng này thì bạn sẽ muốn nó hiển thị tiếng gì? Tôi đoán rằng bạn sẽ muốn cho hiển thị English phải không nào, vì ít nhất English cũng phổ biến hơn và khả năng có nhiều người biết đến sẽ cao hơn. Như vậy English ở đây sẽ là ngôn ngữ mặc định và bạn nên thêm các string English vào trong file res/values/string.xml default.

Tại sao Default Resources lại quan trọng?

Khi chạy ứng dụng Android với một locale mà bạn không hỗ trợ, Android sẽ load default string từ file res/values/strings.xml. Nếu file default này không tồn tại hoặc bị thiếu một string mà ứng dụng cần, thì ứng dụng của bạn sẽ bị lỗi. Ví dụ sau đây mô tả điều gì sẽ xảy ra nếu file default string không đầy đủ.

Ví dụ: Một ứng dụng Android sử dụng hai string là text_a và text_b. Ứng dụng có một file resource đã được localized (res/values-en/strings.xml) định nghĩa text_a và text_b trong English. Ứng dụng cũng có một file default resource (res/values/strings.xml) có định nghĩa text_a nhưng KHÔNG có text_b.

Khi mà ứng dụng này được bật lên ở device có Locale là English, ứng dụng sẽ chạy mà không có lỗi, bởi vì res/values-en/strings.xml có chứa cả hai string mà ứng dụng cần (text_a và text_b).

Tuy nhiên, người dùng sẽ không thấy được text_b khi mà ứng dụng được bật lên ở device có Localce khác English (ví dụ device có ngôn ngữ là Tiếng Việt).

Để tránh trường hợp này, bạn cần đảm bảo file default resource res/values/strings.xml tồn tại và có chứa tát cả các string mà ứng dụng cần.

Tóm lại, defailt resources cần phải đầy đủ nhất so với các localized resources.

Cách làm ứng dụng hỗ trợ đa ngôn ngữ

Ở trên mình đã giới thiệu một vài khái niệm cơ bản, còn giờ thì có lẽ chúng ta sẽ đi vào vọc cho các bạn dễ hình dung nhé. Mà trước khi nhảy vào vọc thì chúng ta cần xác định chúng ta sẽ làm gì đã?

Với chủ đề như này thì yêu cầu sẽ là làm một ứng dụng hỗ trợ đa ngôn ngữ. Ngoài ra có yêu cầu thêm là ứng dụng có ngôn ngữ mặc định là English, ngoài ra ứng dụng hỗ trợ thêm các ngôn ngữ sau là Tiếng Việt và 日本の.

Mình sẽ hướng dẫn tới các bạn cách làm với cả hai loại là đa ngôn ngữ bị động (loại 1) và đa ngôn ngữ chủ động (loại 2). Chúng ta sẽ tạo hai module passiveactive trong cùng một project.

Như mình đã giới thiệu ở trên thì với cả hai loại ứng dụng đa ngôn ngữ chủ động và bị động thì bạn đều cần thêm các resources cho ngôn ngữ mà bạn sẽ hỗ trợ, việc này gọi là localized resources. Do đó nên chúng ta sẽ cùng tìm hiểu cách 'localized resources' trước

Localized resources

Trước hết bạn cần tạo một project mới, do yêu cầu bài toán là dùng English làm mặc định nên chúng ta sẽ thêm các resources vào trong file res/values/strings.xml và được kết quả như trong hình dưới đây.

res/values/strings.xml

<resources>
    <string name="app_name">Multi Language Passive</string>
    <string name="content">This is a multi-language application</string>
    <string name="news">Google is adding Kotlin as an official programming language for Android development</string>
</resources>

Android Studio hiện có những công cụ hỗ trợ việc này rất tốt. Trong hình trên bạn ấn vào chữ Open editor để mở màn hình Translations Editor có giao diện như sau:

Mình sẽ giải thích công dụng của các button trong tab này:

  • Dấu "+" để thêm một key và value string mới
  • Dấu "-" để xóa một key-value đã có sẵn
  • Quả địa cầu: để thêm một ngôn ngữ mới
  • Dấu tích Show only keys needing translations chỉ hiện những key cần dịch hoặc hiện tất cả các key
  • Dấu "?" để mở document

Yêu cầu bài toán ban đầu là ngoài support English thì chúng ta cần support Tiếng Việt và 日本の* Vì thế nên các bạn chọn quả địa cầu và thêm hai ngôn ngữ là Japanese (ja)Vietnamese (vi) như sau. Lưu ý, danh sách có hỗ trợ tìm kiếm nên bạn chỉ cần gõ javie.

Sau khi thực hiện thêm hai ngôn ngữ mới Japanese (ja)Vietnamese (vi), và dịch các string sang từng ngôn ngữ tương ứng chúng ta sẽ có như hình dưới đây.

Việc localized resources đến đây là xong.

Loại 1: Đa ngôn ngữ bị động

Ở loại 1 này thì bạn chỉ cần làm xong bước localized resources, hiển thị các text view với string. Lưu ý rằng, layout editor của Android Studio có hỗ trợ xem preview layout với các ngôn ngữ khác, bạn chỉ cần chọn ngôn ngữ bạn muốn như con trỏ chuột trong hình.

Sau đó bạn chạy ứng dụng

  • Nếu ngôn ngữ của hệ thống là Tiếng Việt thì ứng dụng sẽ hiển thị như sau:

  • Nếu ngôn ngữ của hệ thống là 日本の thì ứng dụng sẽ hiển thị như sau:

  • Nếu ngôn ngữ của hệ thống không phải là hai tiếng trên, thì hệ thống sẽ hiển thị các string trong file default là English:

Như vậy tôi đã hướng dẫn cho các bạn xong loại 1 đa ngôn ngữ bị động, nghỉ ngơi chút làm tách coffe đã 😃.

Loại 2: Đa ngôn ngữ chủ động

Để sang loại 2 thì các bạn cần tạo thêm một module nữa cho project với tên là active. Dưới đây là cấu trúc tổng quan của module để các bạn tiện theo dõi:

Có vẻ khá nhiều nhỉ, nhưng yên tâm mình sẽ hướng dẫn các bạn lần lượt các bước, rồi cũng sẽ xong cả thôi 😃

Bước 1: Cập nhật build.gradle:

build.gradle (Module active)

android {
    ...
    dataBinding {
        enabled = true
    }
}

dependencies {
  ...
  compile 'com.android.support:design:26.+'
  compile 'com.android.support:recyclerview-v7:26.+'
  compile group: 'com.google.code.gson', name: 'gson', version: '2.7'
}

Bước 2: Các bạn thực hiện localized resources giống như ở loại 1

res/values/strings.xml

<resources>
    <string name="app_name">Multi Language Active</string>

    <!--main-->
    <string name="main_content">This is a multi-language application</string>
    <string name="main_news">Google is adding Kotlin as an official programming language for Android development</string>
    <string name="main_button_change_language">Change Language</string>

    <!--change language-->
    <string name="change_language_title">Language</string>

    <!--untranslatable-->
    <string name="language_english" translatable="false">English</string>
    <string name="language_japanese" translatable="false">日本の</string>
    <string name="language_vietnamese" translatable="false">Tiếng Việt</string>

    <string-array name="language_names">
        <item>@string/language_english</item>
        <item>@string/language_japanese</item>
        <item>@string/language_vietnamese</item>
    </string-array>

    <string name="language_english_code" translatable="false">en</string>
    <string name="language_japanese_code" translatable="false">ja</string>
    <string name="language_vietnamese_code" translatable="false">vi</string>

    <string-array name="language_codes">
        <item>@string/language_english_code</item>
        <item>@string/language_japanese_code</item>
        <item>@string/language_vietnamese_code</item>
    </string-array>
</resources>

res/values-ja/strings.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">マルチ言語パッシブ</string>

    <!--main-->
    <string name="main_content">これは多言語アプリケーションです</string>
    <string name="main_news">GoogleはAndroid開発の公式プログラミング言語としてKotlinを追加しています</string>
    <string name="main_button_change_language">言語の変更</string>

    <!--change language-->
    <string name="change_language_title">言語</string>
</resources>

res/values-vi/strings.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">Đa ngôn ngữ chủ động</string>

    <!--main-->
    <string name="main_content">Đây là một ứng dụng đa ngôn ngữ</string>
    <string name="main_news">Google đang bổ sung Kotlin như một ngôn ngữ lập trình chính thức cho sự phát triển của Android</string>
    <string name="main_button_change_language">Đổi Ngôn Ngữ</string>

    <!--change language-->
    <string name="change_language_title">Ngôn Ngữ</string>
</resources>

Các bạn sẽ để ý thấy rằng trong file res/values/strings.xml có một số string khác là các language_nameslanguage_codes

  • language_name dùng trong việc hiển thị các ngôn ngữ để hconj lựa ở màn hình change language.
  • language_code dùng trong việc quản lý ngôn ngữ, xử lý locale hiện tại của app. Danh sách language code các bạn có thể tham khảo thêm tại http://www.loc.gov/standards/iso639-2/php/code_list.php.

Bước 3: Code layout

Ở đây mình sẽ chỉ có 2 màn hình là mainchage language

main screen

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="main"
            type="com.quanda.active.main.MainActivity"
            />
    </data>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:padding="20dp"
        >

        <Button
            android:id="@+id/button_change_language"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="@{ () -> main.openLanguageScreen() }"
            android:text="@string/main_button_change_language"
            />

        <TextView
            android:id="@+id/text_content"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:text="@string/main_content"
            android:textColor="@android:color/black"
            android:textSize="20sp"
            />

        <TextView
            android:id="@+id/text_news"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:text="@string/main_news"
            android:textColor="@android:color/black"
            android:textSize="20sp"
            />
    </LinearLayout>
</layout>

Tiếp theo là change language screen

activity_change_language.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    >
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        >

        <android.support.design.widget.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            >

            <android.support.v7.widget.Toolbar
                android:layout_width="match_parent"
                android:layout_height="?android:actionBarSize"
                >

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="@string/change_language_title"
                    android:textColor="@android:color/white"
                    android:textSize="20sp"
                    />
            </android.support.v7.widget.Toolbar>
        </android.support.design.widget.AppBarLayout>

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recycler_view_language"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            tools:listitem="@layout/item_language"
            />
    </LinearLayout>
</layout>

item_language.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    >
    <data>
        <variable
            name="holder"
            type="com.quanda.active.changelanguage.LanguageAdapter.LanguageHolder"
            />
    </data>
    <FrameLayout
        android:id="@+id/frame_item_language"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="?android:attr/selectableItemBackground"
        android:clickable="true"
        >

        <RadioButton
            android:id="@+id/radio_item_language"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="15dp"
            android:background="@null"
            android:clickable="false"
            android:layoutDirection="rtl"
            android:text="@{ holder.name }"
            android:textAlignment="textStart"
            android:textColor="@android:color/black"
            android:textSize="14sp"
            tools:text="@string/language_english"
            />
    </FrameLayout>
</layout>

Bước 4: Chỉnh lại style một chút

Ở đây chúng ta sẽ sử dụng style không có ActionBar để tiện cho việc custom

styles.xml

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">

Bước 5: Giờ thì chúng ta đi vào code java nào

Bước 5.1: Thêm data và model

Model Language biểu thị cho từng ngôn ngữ với tên ngôn ngữ và mã (code) của ngôn ngữ.

mode/Language.java

package com.quanda.active.model;

public class Language {

    private int mId;
    private String mName;
    private String mCode;

    public Language(int id, String name, String code) {
        mId = id;
        mName = name;
        mCode = code;
    }

    public int getId() {
        return mId;
    }

    public String getName() {
        return mName;
    }

    public String getCode() {
        return mCode;
    }
}

data/Constants.java

package com.quanda.active.data;

public class Constants {

    public class Value {
        public static final int DEFAULT_LANGUAGE_ID = 0;
    }

    public class RequestCode {
        public static final int CHANGE_LANGUAGE = 10000;
    }
}

data/ItemClickListener.java

package com.quanda.active.data;

public interface ItemClickListener<T> {
    void onClickItem(int position, T item);
}

Bước 5.2: Thêm SharedPrefs để lưu ngôn ngữ

Ở bài viblo Tối ưu Android Shared Preference, tôi đã giới thiệu với các bạn về các thu gọn SharedPrefs, nên ở đây tôi chỉ gần như là dùng lại và sửa một vài key của SharedPrefs mà thôi.

manifests.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.quanda.active">

    <application
        android:name=".App"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name="com.quanda.active.main.MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <activity
            android:name=".changelanguage.ChangeLanguageActivity"
            android:screenOrientation="portrait"/>
    </application>
</manifest>

App.java

package com.quanda.active;

import android.app.Application;
import com.google.gson.Gson;

public class App extends Application {

    private static App sInstance;
    private Gson mGSon;

    public static App self() {
        return sInstance;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        sInstance = this;
        mGSon = new Gson();
    }

    public Gson getGSon() {
        return mGSon;
    }
}

utils/SharedPrefs.java

package com.quanda.active.utils;

import android.content.Context;
import android.content.SharedPreferences;
import com.quanda.active.App;

public class SharedPrefs {

    private static final String PREFS_NAME = "multi_language_active";
    public static final String LANGUAGE = "langauge";

    private static SharedPrefs mInstance;
    private SharedPreferences mSharedPreferences;

    private SharedPrefs() {
        mSharedPreferences = App.self().getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
    }

    public static SharedPrefs getInstance() {
        if (mInstance == null) {
            mInstance = new SharedPrefs();
        }
        return mInstance;
    }

    @SuppressWarnings("unchecked")
    public <T> T get(String key, Class<T> anonymousClass) {
        if (anonymousClass == String.class) {
            return (T) mSharedPreferences.getString(key, "");
        } else if (anonymousClass == Boolean.class) {
            return (T) Boolean.valueOf(mSharedPreferences.getBoolean(key, false));
        } else if (anonymousClass == Float.class) {
            return (T) Float.valueOf(mSharedPreferences.getFloat(key, 0));
        } else if (anonymousClass == Integer.class) {
            return (T) Integer.valueOf(mSharedPreferences.getInt(key, 0));
        } else if (anonymousClass == Long.class) {
            return (T) Long.valueOf(mSharedPreferences.getLong(key, 0));
        } else {
            return (T) App.self()
                    .getGSon()
                    .fromJson(mSharedPreferences.getString(key, ""), anonymousClass);
        }
    }

    public <T> void put(String key, T data) {
        SharedPreferences.Editor editor = mSharedPreferences.edit();
        if (data instanceof String) {
            editor.putString(key, (String) data);
        } else if (data instanceof Boolean) {
            editor.putBoolean(key, (Boolean) data);
        } else if (data instanceof Float) {
            editor.putFloat(key, (Float) data);
        } else if (data instanceof Integer) {
            editor.putInt(key, (Integer) data);
        } else if (data instanceof Long) {
            editor.putLong(key, (Long) data);
        } else {
            editor.putString(key, App.self().getGSon().toJson(data));
        }
        editor.apply();
    }

    public void clear() {
        mSharedPreferences.edit().clear().apply();
    }
}

Bước 5.3: Quản lý việc thay đổi ngôn ngữ

Để thay đổi ngôn ngữ chủ động thì bạn cần xác định code của ngôn ngữ mà bạn muốn chuyển tới. Tiếp đó là tạo locale tương ứng với code đó và thực hiện việc cập nhật locale. Các bạn hãy quan sát hàm changeLanguage().

utils/LanguageUtils

package com.quanda.active.utils;

import android.content.res.Configuration;
import android.content.res.Resources;
import com.quanda.active.App;
import com.quanda.active.data.Constants;
import com.quanda.active.R;
import com.quanda.active.model.Language;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;

public class LanguageUtils {

    private static Language sCurrentLanguage = null;

    public static Language getCurrentLanguage() {
        if (sCurrentLanguage == null) {
            sCurrentLanguage = initCurrentLanguage();
        }
        return sCurrentLanguage;
    }

    /**
     * check language exist in SharedPrefs, if not exist then default language is English
     */
    private static Language initCurrentLanguage() {
        Language currentLanguage =
                SharedPrefs.getInstance().get(SharedPrefs.LANGUAGE, Language.class);
        if (currentLanguage != null) {
            return currentLanguage;
        }
        currentLanguage = new Language(Constants.Value.DEFAULT_LANGUAGE_ID,
                App.self().getString(R.string.language_english),
                App.self().getString(R.string.language_english_code));
        SharedPrefs.getInstance().put(SharedPrefs.LANGUAGE, currentLanguage);
        return currentLanguage;
    }

    /**
     * return language list from string.xml
     */
    public static List<Language> getLanguageData() {
        List<Language> languageList = new ArrayList<>();
        List<String> languageNames =
                Arrays.asList(App.self().getResources().getStringArray(R.array.language_names));
        List<String> languageCodes =
                Arrays.asList(App.self().getResources().getStringArray(R.array.language_codes));
        if (languageNames.size() != languageCodes.size()) {
            // error, make sure these arrays are same size
            return languageList;
        }
        for (int i = 0, size = languageNames.size(); i < size; i++) {
            languageList.add(new Language(i, languageNames.get(i), languageCodes.get(i)));
        }
        return languageList;
    }

    /**
     * load current locale and change language
     */
    public static void loadLocale() {
        changeLanguage(initCurrentLanguage());
    }

    /**
     * change app language
     */
    @SuppressWarnings("deprecation")
    public static void changeLanguage(Language language) {
        SharedPrefs.getInstance().put(SharedPrefs.LANGUAGE, language);
        sCurrentLanguage = language;
        Locale locale = new Locale(language.getCode());
        Resources resources = App.self().getResources();
        Configuration configuration = resources.getConfiguration();
        configuration.setLocale(locale);
        resources.updateConfiguration(configuration, resources.getDisplayMetrics());
    }
}

Bước 5.4: Code 2 activity còn lại

Màn hình change language thực hiện việc hiển thị danh sách các ngôn ngữ mà ứng dụng sẽ hỗ trợ. Khi người dùng chọn một ngôn ngữ khác ngôn ngữ hiện tại thì chúng ta sẽ tiến hành cập nhật lại ngôn ngữ của ứng dụng. Ở đây mình thực hiện việc gọi LanguageUtils.changeLanguage(language) trong hàm onChangeLanguageSuccessfully()

changelanguage/ChangeLanguageActivity.java

package com.quanda.active.changelanguage;

import android.content.Intent;
import android.databinding.DataBindingUtil;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import com.quanda.active.data.ItemClickListener;
import com.quanda.active.R;
import com.quanda.active.databinding.ActivityChangeLanguageBinding;
import com.quanda.active.model.Language;
import com.quanda.active.utils.LanguageUtils;

public class ChangeLanguageActivity extends AppCompatActivity {

    private LanguageAdapter mLanguageAdapter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityChangeLanguageBinding binding =
                DataBindingUtil.setContentView(this, R.layout.activity_change_language);
        mLanguageAdapter = new LanguageAdapter(LanguageUtils.getLanguageData());
        mLanguageAdapter.setListener(new ItemClickListener<Language>() {
            @Override
            public void onClickItem(int position, Language language) {
                if (!language.getCode().equals(LanguageUtils.getCurrentLanguage().getCode())) {
                    onChangeLanguageSuccessfully(language);
                }
            }
        });
        LinearLayoutManager layoutManager = new LinearLayoutManager(ChangeLanguageActivity.this);
        binding.recyclerViewLanguage.setLayoutManager(layoutManager);
        binding.recyclerViewLanguage.setAdapter(mLanguageAdapter);
    }

    private void onChangeLanguageSuccessfully(final Language language) {
        mLanguageAdapter.setCurrentLanguage(language);
        LanguageUtils.changeLanguage(language);
        setResult(RESULT_OK, new Intent());
        finish();
    }
}

changelanguge/LanguageAdapter.java

package com.quanda.active.changelanguage;

import android.databinding.DataBindingUtil;
import android.databinding.ObservableField;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.quanda.active.data.ItemClickListener;
import com.quanda.active.R;
import com.quanda.active.databinding.ItemLanguageBinding;
import com.quanda.active.model.Language;
import com.quanda.active.utils.LanguageUtils;
import java.util.ArrayList;
import java.util.List;

public class LanguageAdapter extends RecyclerView.Adapter<LanguageAdapter.LanguageHolder> {

    private List<Language> mLanguageList = new ArrayList<>();
    private ItemClickListener<Language> mListener;
    private Language mCurrentLanguage = LanguageUtils.getCurrentLanguage();

    public LanguageAdapter(List<Language> languageList) {
        mLanguageList = languageList;
    }

    public void setListener(ItemClickListener<Language> listener) {
        mListener = listener;
    }

    public void setCurrentLanguage(Language language) {
        mCurrentLanguage = language;
        notifyDataSetChanged();
    }

    @Override
    public LanguageHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        ItemLanguageBinding binding =
                DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()),
                        R.layout.item_language, parent, false);
        return new LanguageHolder(binding, mListener);
    }

    @Override
    public void onBindViewHolder(LanguageHolder holder, int position) {
        holder.mBinding.radioItemLanguage.setChecked(mCurrentLanguage.getId() == position);
        holder.bindLanguage(mLanguageList.get(position));
    }

    @Override
    public int getItemCount() {
        return mLanguageList.size();
    }

    public class LanguageHolder extends RecyclerView.ViewHolder {
        public ObservableField<String> name = new ObservableField<>();
        private ItemLanguageBinding mBinding;
        private Language mLanguage;

        LanguageHolder(ItemLanguageBinding binding, final ItemClickListener<Language> listener) {
            super(binding.getRoot());
            mBinding = binding;
            mBinding.setHolder(this);
            mBinding.getRoot().setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    if (listener != null) {
                        listener.onClickItem(getAdapterPosition(), mLanguage);
                    }
                }
            });
        }

        void bindLanguage(Language language) {
            mLanguage = language;
            name.set(language.getName());
        }
    }
}

Sau khi mở sang màn hình change language từ màn hình main và thực hiện việc cập nhật ngôn ngữ, chúng ta cần cập nhật lại view cho màn hình main. Chúng ta chỉ cần cập nhật lại với các màn hình mà view đã được tạo (ở đây là màn hình main), còn với các màn hình sau đó chúng ta mới bật lên thì view được tạo mớitự động sử dụng locale mới luôn (ví dụ chúng ta mở lại màn hình change language, title của màn hình này sẽ được tự động chuyển sang ngôn ngữ mới). Việc view được tạo mới tự động sử dụng locale mới thì đã có trong bài viblo Đa ngôn ngữ cho application. Tuy nhiên với các view đã được khởi tạo thì chúng ta vẫn sẽ cần cập nhật thủ công.

Có một số cách để cập nhật view cho màn hình đã được khởi tạo được giới thiệu trong bài viblo Thay đổi Ngôn ngữ không cần restart activity trên android ?. Và ở đây mình sẽ chọn việc set lại value cho các view bằng hàm updateViewByLanguage().

Và một điều nữa đó là khi mở lại ứng dụng, chúng ta cần set lại locale của ứng dụng đã được lưu, chứ không phải dùng locale của hệ thống Android thông qua hàm LanguageUtils.loadLocale()

main/MainActivity.java

package com.quanda.active.main;

import android.content.Intent;
import android.databinding.DataBindingUtil;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import com.quanda.active.data.Constants;
import com.quanda.active.R;
import com.quanda.active.changelanguage.ChangeLanguageActivity;
import com.quanda.active.databinding.ActivityMainBinding;
import com.quanda.active.utils.LanguageUtils;

public class MainActivity extends AppCompatActivity {

    private ActivityMainBinding mBinding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        LanguageUtils.loadLocale();
        mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        mBinding.setMain(MainActivity.this);
    }

    public void openLanguageScreen() {
        Intent intent = new Intent(MainActivity.this, ChangeLanguageActivity.class);
        startActivityForResult(intent, Constants.RequestCode.CHANGE_LANGUAGE);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode) {
            case Constants.RequestCode.CHANGE_LANGUAGE:
                if (resultCode == RESULT_OK) {
                    updateViewByLanguage();
                }
                break;
        }
    }

    private void updateViewByLanguage() {
        mBinding.buttonChangeLanguage.setText(getString(R.string.main_button_change_language));
        mBinding.textContent.setText(getString(R.string.main_content));
        mBinding.textNews.setText(getString(R.string.main_news));
    }
}

Vậy là hết các bước của loại 2 rồi đó các bạn, giờ thì chạy app thôi 😄.

Kết luận

Lần này mình đã giới thiệu đến các bạn một số khái niệm về đa ngôn ngữ trong ứng dụng Android, cách để làm một ứng dụng đa ngôn ngữ và đi vào phân tích cụ thể 2 loại đa ngôn ngữ là thụ độngchủ động.

Các bạn có thắc mắc gì có thể chia sẻ lên đây để cùng bàn luận. Ngoài ra mình cũng có một số bài viết khác ở đây để các bạn có thể xem thêm.

Cảm ơn các bạn đã theo dõi và hẹn gặp lại các bạn trong các bài viết tiếp theo.

Code

https://github.com/dangquanuet/Multi-Language-App

Tài liệu tham khảo

Android docs https://developer.android.com/training/basics/supporting-devices/languages.html https://developer.android.com/guide/topics/resources/localization.html https://developer.android.com/studio/write/translations-editor.html https://developer.android.com/reference/java/util/Locale.html

Loại 1: đa ngôn ngữ bị động https://viblo.asia/p/da-ngon-ngu-trong-trinh-ung-dung-android-lxrRXNvVzeO

Loại 2: đa ngôn ngữ chủ động https://viblo.asia/p/da-ngon-ngu-cho-application-l5y8RrJoRob3 https://viblo.asia/p/thay-doi-ngon-ngu-khong-can-restart-activity-tren-android-gEmROxqAKpv


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.