12 October 2013

มาทำให้แอปพลิเคชันรองรับหลายภาษากันเถอะ

Updated on
        การจะเปลี่ยนภาษาไปตามการตั้งค่าของเครื่องนั้นๆ ถ้าเครื่องนั้นตั้งค่าเป็นภาษาไทยก็จะแสดงภาษาไทย ทีนี้ถ้าอยากให้เลือกภาษาในแอปพลิเคชันได้ล่ะ? โดยไม่ต้องเข้าไปทำการตั้งค่าที่ Settings ของเครื่อง ถึงคราวบทความนี้แล้วแหละที่จะอธิบายให้เข้าใจกัน
        สำหรับการสร้าง String ให้กับภาษาต่างๆคงไม่บอกแล้วเนอะ ถ้ายังทำไม่เป็นก็ให้ไปอ่านบทความตามลิ้งข้างบนก่อนนะ โดยบทความนี้จะเตรียม String ไว้สามภาษาด้วยกันดังนี้


        ก็จะมี English (ในที่นี้ขอหมายถึง US), Italy และ ไทย ซึ่งในแต่ละไฟล์ก็จะกำหนด String ไว้ดังนี้


res/values-en/string.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string name="text_welcome">Welcome to my application</string>
    <string name="button_text">Press me</string>

</resources>

res/values-it/string.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string name="text_welcome">Benvenuti nella mia applicazione</string>
    <string name="button_text">mi premere</string>

</resources>

res/values-th/string.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string name="text_welcome">ยินดีต้อนรับสู่แอปพลิเคชันของผม</string>
    <string name="button_text">กดดูสิ</string>

</resources>

        หัวใจสำคัญหลักก็คือชื่อของ String ที่กำหนดไว้ใน string.xml นั่นเอง จะเห็นว่าคนละไฟล์แต่กำหนดชื่อ String เหมือนกัน เพื่อให้โปรแกรมรู้ โดยที่ข้อมูลใน String ก็จะแตกต่างกันไปตามภาษากำหนด

        เมื่อเตรียม String เสร็จแล้ว ทีนี้มาดูที่ Layout กันต่อ เจ้าของบล็อกสร้างหน้าแอปพลิเคชันแบบลวกๆตามนี้

main.xml
<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:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".Main" >

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true" >

        <Button
            android:id="@+id/buttonEn"
            android:layout_width="90dp"
            android:layout_height="60dp"
            android:layout_margin="5dp" 
            android:background="@drawable/us_flag" />

        <Button
            android:id="@+id/butonIt"
            android:layout_width="90dp"
            android:layout_height="60dp"
            android:layout_margin="5dp" 
            android:background="@drawable/italy_flag" />

        <Button
            android:id="@+id/butonTh"
            android:layout_width="90dp"
            android:layout_height="60dp"
            android:layout_margin="5dp" 
            android:background="@drawable/thai_flag" />

    </LinearLayout>

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:orientation="vertical"
        android:gravity="center" >

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/text_welcome"
            android:textSize="18sp" />

        <Button
            android:id="@+id/button1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/button_text"
            android:layout_margin="10dp" />

    </LinearLayout>

</RelativeLayout>


        สังเกตดีๆว่าภาพธงชาติที่ใช้เป็นปุ่มนั้นใช้เป็น Button แล้วกำหนดภาพพื้นหลังให้เป็นภาพธงชาติแทน ซึ่งไม่จำเป็นต้องใช้ Image Button ก็ได้ ไม่ต่างกัน

        Button ทั้งสามปุ่มก็แทนการเปลี่ยนภาษาแต่ละภาษา โดยมี Text View และ Button ตรงกลางที่เอาไว้แสดงข้อความ ที่เจ้าของบล็อกจะให้เปลี่ยนเป็นภาษาต่างๆ (เรียกง่ายๆว่าหนูทดลองนั่นแหละครับ)

        ให้สังเกตที่โค๊ด XML ดูครับ ตรงตัวหนังสือสีแดงจะเห็นว่าข้อความบน Text View และ Button เป็น String ที่ดึงมาจาก string.xml นั่นเอง ทีนี้ก็เหลือแค่ว่าสั่งให้แอปพลิเคชันเปลี่ยนภาษาแล้ว

        สำหรับคำสั่งในการเปลี่ยนภาษาให้กับแอปพลิเคชัน (ไม่ใช่เปลี่ยนทั้งเครื่องนะ อันนี้แค่ในแอปพลิเคชันนี้เท่านั้น) จะใช้คำสั่งง่ายๆสามบรรทัด ดังนี้

Configuration config = new Configuration();
config.locale = ภาษาที่จะกำหนด;
getResources().updateConfiguration(config, null);

        สำหรับภาษาที่จะกำหนด จะใช้คลาส Locale ในการกำหนดค่า


        สมมติว่าเจ้าของบล็อกต้องการกำหนดให้แอปพลิเคชันใช้เป็นภาษาญี่ปุ่น ก็จะกำหนดค่าดังนี้

config.locale = Locale.JAPAN;
config.locale = Locale.JAPANESE;

        ทั้งนี้แบบนี้ต่างกันยังไง? ทำไมต้องมี JAPAN และ JAPANESE สำหรับ JAPANESE คือ "ja" แต่ JAPANESE จะเป็น "ja_JP" ja ก็คือคำย่อของภาษาญี่ปุ่น และ JP คือประเทศญี่ปุ่น ในตัวอย่างของภาษาญี่ปุ่นอาจจะมองไม่ออกว่าจะมีไปทำอะไร แต่ถ้ามองตัวอย่างของภาษาอังกฤษกันบ้างล่ะ?

config.locale = Locale.ENGLISH;
config.locale = Locale.UK;
config.locale = Locale.US;

        ทั้งสามคำสั่งนี้เป็นภาษาอังกฤษเหมือนกัน แต่ก็มีบางอย่างที่ต่างกัน ENGLISH คือ "en" ส่วน UK คือ "en_GB" และ US คือ "en_US" ENGLISH ก็เปรียบเสมือนว่าหมายถึงภาษาอังกฤษแบบห้วนๆเลย ส่วน UK คือภาษาอังกฤษสำเนียงชาวอังกฤษ ส่วน US ก็จะเป็นภาษาอังกฤษสำเนียงชาวอเมริกัน ดังนั้น ENGLISH เอาไว้สำหรับภาษาอังกฤษแบบโดยรวม แต่ถ้าต้องการทำให้แอปพลิเคชันแยกตามสำเนียงด้วยก็จะต้องใช้ UK กับ US แทนนั่นเอง ดังนั้นถ้ากรณีเช่นนี้ โฟลเดอร์ values ก็จะต้องกำหนดเพิ่มเติม สำหรับ UK จะเป็น values-en_GB ส่วน values-en_US ซึ่งเจ้าของบล็อกมองว่ายังไม่ต้องอธิบายถึงขนาดนี้ ก็เลยขอใช้แค่ values-en หรือ ENGLISH นั่นเอง

        สำหรับคลาส Locale ก็จะมีภาษาให้เลือกดังนี้


        สำหรับคลาส Locale จะมีคำสั่งอื่นอยู่ด้วย ดังนั้นที่ใช้ได้ก็จะมีแค่ที่เจ้าของบล็อกวงกลมสีแดงไว้ข้างหน้าเท่านั้นนะ

        อ้าว!! แล้วทำไมไม่มีภาษาไทยละ? งี้จะกำหนดยังไง อาจจะดูน่าน้อยใจเหมือนกันนะที่ไม่มี THAI ให้เลือกในนี้ สำหรับภาษาที่ไม่มีให้เลือกในคลาส Locale สามารถกำหนดเองได้ แต่ต้องมั่นใจนะว่าเครื่องรองรับภาษานั้นๆด้วย โดยเช็คได้จากคำสั่ง Locale.getAvailableLocales();

        ทีนี้จะกำหนดภาษาไทยก็จะใช้คำสั่งแบบนี้

config.locale = new Locale("th")

        จะ "th" หรือ "th_TH" ก็ได้เหมือนกันนั่นแหละ เพราะบ้านเราไม่เหมือนภาษาอังกฤษที่มีสำเนียงคนละประเทศ

        เมื่อสรุปคร่าวๆให้ฟังแล้ว ก็ลองมาดูคำสั่งที่ใช้ในบทความนี้เลย


Main.java
package app.akexorcist.changeapplicationlanguage;

import java.util.Locale;

import android.os.Bundle;
import android.app.Activity;
import android.content.res.Configuration;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class Main extends Activity {

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        Button butonEn = (Button)findViewById(R.id.buttonEn);
        butonEn.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                Configuration config = new Configuration();
                config.locale = Locale.ENGLISH;
                getResources().updateConfiguration(config, null);

                onCreate(null);
            }
        });
        
        
        Button butonIt = (Button)findViewById(R.id.butonIt);
        butonIt.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                Configuration config = new Configuration();
                config.locale = Locale.ITALIAN;
                getResources().updateConfiguration(config, null);

                onCreate(null);
            }
        });
        
        
        Button butonTh = (Button)findViewById(R.id.butonTh);
        butonTh.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                Configuration config = new Configuration();
                config.locale = new Locale("th");
                getResources().updateConfiguration(config, null);

                onCreate(null);
            }
        });
    }
}

        คำสั่งกำหนดภาษาให้กับแอปพลิเคชันก็จะเป็นโค๊ดที่ตัวหนังสือสีส้ม โดยที่ตัวอักษรสีแดงคือคำสั่งที่ใช้กำหนดภาษาที่ต้องการ

        ทีนี้ให้มาดูที่ตัวอักษรสีฟ้ากันต่อกับคำสั่ง onCreate โดยปกติแล้วเมื่อใช้คำสั่งเปลี่ยนภาษาแล้ว Activity หรือหน้าที่แสดงอยู่จะไม่เปลี่ยนภาษาให้ทันที แต่เมื่อเปลี่ยน Activity หรือไปหน้าอื่นจะค่อยเห็นผล

        แต่ในตัวอย่างนี้เจ้าของบล็อกอยากให้กดเปลี่ยนภาษา แล้วข้อความในหน้านี้เปลี่ยนทันทีเลย เจ้าของบล็อกจึงใช้วิธี Refresh Activity นี้ใหม่ซะ โดยใช้คำสั่ง onCreate(null); นี่แหละ จึงทำให้ onCreate ทำงานและเรียก Activity นี้ขึ้นมาใหม่ ผลก็คือเหมือน Refresh Activity นี้นั่นแหละ

        เท่านี้ก็เป็นอันเสร็จเรียบร้อยแล้ว~


AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="app.akexorcist.changeapplicationlanguage"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="17" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="app.akexorcist.changeapplicationlanguage.Main"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

        เมื่อทดสอบดูก็ให้ลองกดปุ่มเปลี่ยนภาษาก็จะพบว่าข้อความบน Text View กับ Button เปลี่ยนภาษาแล้ว





        ถ้าผู้ที่หลงเข้ามาอ่านอยากรู้ว่าคำนี้แปลเป็นภาษาอื่นจะแปลยังไง ก็แปลเองนะครับ แล้วก็อย่าไปพึ่ง Google Translate มากนัก คนต่างชาติเค้าก็ยังบอกเลยครับว่าแปลได้ไม่ค่อยดีนักหรอก

        โดยปกติแล้วจะใช้วิธีจ้างนักแปลภาษาให้ช่วยแปลให้กัน ที่เคยเห็นก็มีบริษัทต่างประเทศที่รับแปลภาษาสำหรับแอปพลิเคชัน โดยแปลได้ถึง 30 ภาษาแน่ะ ส่วนราคาในการแปลก็แล้วแต่จะคิด มีแบบคิดเป็นคำหรือคิดเป็นแบบเหมารวมทั้งหมดก็มี หรือจะใช้บริการของ Google ก็ได้ อยู่ใน Google Play Developer Console โดยเพิ่มบริการแปลภาษาต่างๆ อันนี้ไม่ได้ใช้ Google Translate แปลให้นะ เค้ามีคนแปลให้  ค่าบริการ $75

        ถ้าไม่ซีเรียสกับแอปพลิเคชันมากนักก็ไม่ต้องถึงขนาดนั้นหรอก เพราะค่าจ้างก็ไม่ใช่ถูกๆอยู่แล้ว


        สำหรับผู้ที่หลงเข้ามาอ่านคนใดต้องการไฟล์ตัวอย่างสามารถดาวน์โหลดได้ที่ Change Application Language [Google Drive]