Skip to main content

คู่มือโลคัลไลเซชันแอป Android อย่างครบถ้วน

ตั้งแต่ strings.xml จนถึงเมทาดาทา Play Store ทำโลคัลไลเซชันแอป Android ด้วย Kotlin, Jetpack Compose, Fastlane และการแปลอัตโนมัติด้วย AI

1

ตั้งค่าโปรเจกต์ Android สำหรับโลคัลไลเซชัน

Android ใช้หลักการแบบโฟลเดอร์สำหรับโลคัลไลเซชัน ข้อความเริ่มต้นอยู่ใน res/values/strings.xml ส่วนข้อความที่แปลแล้วอยู่ในโฟลเดอร์เฉพาะภาษาอย่าง res/values-de/, res/values-ja/ และอื่นๆ

Android Project Structure
// Android project structure for localization:
// res/
// ├── values/              ← Default (fallback) locale
// │   └── strings.xml
// ├── values-de/           ← German
// │   └── strings.xml
// ├── values-ja/           ← Japanese
// │   └── strings.xml
// └── values-es/           ← Spanish
//     └── strings.xml
//
// Folder naming: values-{language} or values-{language}-r{Region}
// Examples: values-pt-rBR, values-zh-rCN, values-zh-rTW
โฟลเดอร์ res/values/ คือภาษาสำรอง หากไม่มีข้อความในโฟลเดอร์เฉพาะภาษา Android จะโหลดจากค่าเริ่มต้น แต่หากไม่มีข้อความในค่าเริ่มต้น แอปจะหยุดทำงานในภาษาที่ไม่รองรับ
2

สร้าง strings.xml

ทรัพยากรข้อความ Android ใช้ XML ที่มีองค์ประกอบ '<string>' ภายในราก '<resources>' ใช้ %s สำหรับตัวยึดตำแหน่งข้อความ %d สำหรับจำนวนเต็ม และ %1$s/%2$s สำหรับอาร์กิวเมนต์ตามตำแหน่งที่นักแปลจัดลำดับใหม่ได้

res/values/strings.xml
<!-- res/values/strings.xml -->
<resources>
    <string name="welcome_title">Welcome to MyApp</string>
    <string name="login_button">Sign In</string>
    <string name="settings_label">Settings</string>
    <string name="greeting">Hello, %s!</string>        <!-- %s = string -->
    <string name="item_count">%d items</string>          <!-- %d = integer -->
    <string name="app_name" translatable="false">MyApp</string>
</resources>
เครื่องหมายอัญประกาศเดี่ยวที่ไม่ได้หลีกทำให้ตัวแยกวิเคราะห์ XML หยุดทำงานโดยไม่แจ้งเตือน ให้ใช้ \' หรือครอบค่าด้วยเครื่องหมายคำพูดคู่ นอกจากนี้หากไม่มีข้อความใน values/strings.xml เริ่มต้น แอปจะหยุดทำงานทันที ไม่ได้ถอยไปใช้ค่าอื่นอย่างราบรื่นเหมือน iOS
Common strings.xml Mistakes
<!-- ❌ Common mistakes in strings.xml: -->

<!-- Unescaped apostrophe — crashes XML parser silently -->
<string name="message">It's a great day</string>

<!-- Missing from default values/strings.xml — app crashes -->
<!-- (only exists in values-de/strings.xml) -->

<!-- ✅ Correct versions: -->
<string name="message">It\'s a great day</string>
<!-- Or wrap in double quotes: -->
<string name="message">"It's a great day"</string>
3

จัดการพหูพจน์

Android ใช้องค์ประกอบ '&lt;plurals&gt;' พร้อมแอตทริบิวต์ quantity ได้แก่ zero, one, two, few, many, other แต่ละภาษาเป้าหมายอาจต้องใช้หมวดหมู่ต่างกัน ภาษาอาหรับใช้ครบทั้ง 6 หมวดหมู่ ภาษารัสเซียต้องใช้ few/many และภาษาญี่ปุ่นใช้เฉพาะ other

res/values/strings.xml
<!-- res/values/strings.xml -->
<resources>
    <plurals name="items_count">
        <item quantity="zero">No items</item>
        <item quantity="one">%d item</item>
        <item quantity="other">%d items</item>
    </plurals>
</resources>

<!-- Usage in Kotlin: -->
<!-- val text = resources.getQuantityString(
    R.plurals.items_count,
    count,    // selects plural form
    count     // format argument
) -->
แอตทริบิวต์ quantity เลือกรูปพหูพจน์ตามกฎ CLDR สำหรับภาษาของอุปกรณ์ ให้ใส่ 'other' เป็นค่าสำรองเสมอ เพราะเป็นหมวดหมู่เดียวที่รับประกันว่ามีในทุกภาษา
4

ใช้ในโค้ด : Kotlin และ Jetpack Compose

Android แบบเดิมใช้ getString(R.string.key) และ resources.getQuantityString() ส่วน Jetpack Compose ใช้ stringResource(R.string.key) และ pluralStringResource() ทั้งสองแบบจะเลือกคำแปลที่ถูกต้องตามภาษาของอุปกรณ์ขณะรัน

WelcomeScreen.kt
// Traditional Android (Activity/Fragment)
val title = getString(R.string.welcome_title)
val greeting = getString(R.string.greeting, userName)
val items = resources.getQuantityString(
    R.plurals.items_count, count, count
)

// Jetpack Compose
@Composable
fun WelcomeScreen(userName: String, itemCount: Int) {
    // ✅ stringResource — Compose-aware, triggers recomposition
    Text(text = stringResource(R.string.welcome_title))

    // ✅ With format arguments
    Text(text = stringResource(R.string.greeting, userName))

    // ✅ Plurals — count passed TWICE
    Text(text = pluralStringResource(
        R.plurals.items_count,
        itemCount,    // selects plural form
        itemCount     // format argument
    ))
}
ใน pluralStringResource(R.plurals.items, count, count) ต้องส่งพารามิเตอร์ count สองครั้ง ครั้งแรกใช้เลือกรูปพหูพจน์ ส่วนครั้งที่สองเป็นอาร์กิวเมนต์รูปแบบ การขาด count ตัวที่สองคือข้อผิดพลาดด้านพหูพจน์ใน Compose ที่พบบ่อยที่สุด
5

อาร์เรย์ข้อความและข้อความที่จัดรูปแบบ

ใช้ '&lt;string-array&gt;' สำหรับรายการเรียงลำดับ เช่น ตัวเลือกแบบดรอปดาวน์และขั้นตอนเริ่มต้นใช้งาน ใช้อาร์กิวเมนต์รูปแบบตามตำแหน่ง (%1$s, %2$d) ในข้อความที่จัดรูปแบบ เพื่อให้นักแปลจัดลำดับคำใหม่ได้โดยไม่ทำให้โครงสร้างประโยคเสียหาย

res/values/strings.xml
<!-- res/values/strings.xml -->
<resources>
    <!-- String array for dropdown/list -->
    <string-array name="sort_options">
        <item>Most Recent</item>
        <item>Most Popular</item>
        <item>Price: Low to High</item>
        <item>Price: High to Low</item>
    </string-array>

    <!-- Positional format args for reordering -->
    <string name="welcome_message">
        Hello %1$s, you have %2$d new messages
    </string>
    <!-- Translators can reorder: -->
    <!-- %2$d neue Nachrichten für %1$s -->
</resources>
อาร์กิวเมนต์ตามตำแหน่งอย่าง %1$s ช่วยให้นักแปลจัดลำดับพารามิเตอร์ใหม่ได้อย่างอิสระ “Hello %1$s, you have %2$d items” เปลี่ยนเป็น “%2$d items for %1$s” ในภาษาที่มีลำดับคำต่างกันได้โดยไม่ต้องแก้โค้ด
6

ทำโลคัลไลเซชันเมทาดาทา Google Play ด้วย Fastlane

ใช้คำสั่ง supply ของ Fastlane เพื่อจัดการเมทาดาทา Play Store ได้แก่ ชื่อ คำอธิบายสั้น คำอธิบายเต็ม และบันทึกการเปลี่ยนแปลง เป็นไฟล์ข้อความธรรมดาที่จัดตามภาษาในที่เก็บ

Terminal
# Install Fastlane
$ gem install fastlane

# Initialize supply for Play Store metadata
$ fastlane supply init

# Directory structure created:
# fastlane/metadata/android/
# ├── en-US/
# │   ├── title.txt              # App name (50 chars)
# │   ├── short_description.txt  # Short desc (80 chars)
# │   ├── full_description.txt   # Full desc (4000 chars)
# │   └── changelogs/
# │       └── default.txt        # What's New
# ├── de-DE/
# │   └── ...
# └── ja-JP/
#     └── ...

# Push metadata to Play Store:
$ fastlane supply
หน้ารายการ Play Store ที่โลคัลไลซ์เพิ่มยอดดาวน์โหลดมากกว่า 30% ในตลาดที่ไม่ใช้ภาษาอังกฤษ ชื่อ คำอธิบายสั้น และคำอธิบายเต็มได้รับการจัดทำดัชนีสำหรับค้นหา การแปลรายการเหล่านี้จึงเป็นโลคัลไลเซชันที่ให้ผลตอบแทนสูงสุด
Google Play

ทำให้โลคัลไลเซชันหน้ารายการ Play Store เป็นอัตโนมัติ

ข้ามการคัดลอกและวางด้วยตนเอง แปลชื่อ คำอธิบาย และบันทึกประจำรุ่นบน Play Store เป็นมากกว่า 175 ภาษาและภูมิภาคโดยคำนึงถึงขีดจำกัดอักขระ

ดูการเชื่อมต่อ Google Play
7

ทดสอบโลคัลไลเซชัน

ทดสอบด้วยการสลับภาษาในโปรแกรมจำลอง พรีวิว Compose พร้อม LocaleList แบบกำหนดเอง และภาษาจำลองในตัวเลือกสำหรับนักพัฒนา ใช้ resConfigs ใน Gradle เพื่อลบทรัพยากรภาษาที่ไม่ต้องการจากไลบรารีของบุคคลที่สาม

Testing Localization
// 1. Emulator: Settings > System > Language > Add language

// 2. Compose Preview with locale:
@Preview
@Composable
fun WelcomePreview() {
    val config = Configuration(resources.configuration).apply {
        setLocale(Locale("de"))
    }
    val localContext = LocalContext.current
    val localizedContext = localContext.createConfigurationContext(config)
    CompositionLocalProvider(
        LocalContext provides localizedContext
    ) {
        WelcomeScreen()
    }
}

// 3. Restrict library locales in build.gradle.kts:
android {
    defaultConfig {
        // Only include locales you actually translate
        resourceConfigurations += listOf("en", "de", "ja", "es", "fr")
    }
}

// 4. Enable pseudolocales in Developer Options:
// en-XA (accented) — detects hardcoded strings
// ar-XB (RTL) — tests layout mirroring
ทดสอบด้วยภาษาเยอรมัน ซึ่งข้อความยาวขึ้นประมาณ 30% และภาษาญี่ปุ่น ซึ่งข้อความสั้นลงประมาณ 50% เพื่อจับปัญหาเลย์เอาต์ เปิดใช้ภาษาจำลอง (en-XA สำหรับอักขระใส่เครื่องหมายกำกับ และ ar-XB สำหรับ RTL) ในตัวเลือกสำหรับนักพัฒนาเพื่อทดสอบเลย์เอาต์อย่างหนักโดยไม่ต้องมีคำแปลจริง

ทำให้คุณภาพการแปลเป็นอัตโนมัติ

ใช้ i18n-validate จับคีย์ที่หายไป ตัวยึดตำแหน่งเสียหาย และปัญหาพหูพจน์ก่อนส่งขึ้นใช้งาน แล้วทดสอบ UI ด้วยคำแปลจำลองผ่าน i18n-pseudo ก่อนคำแปลจริงจะมาถึง
8

ทำให้การแปลเป็นอัตโนมัติ

แปล strings.xml พหูพจน์ อาร์เรย์ข้อความ และเมทาดาทา Fastlane Supply ด้วย AI ทำให้การแปลทั้งข้อความในแอปและเมทาดาทา Play Store เป็นอัตโนมัติ เพื่อให้ผู้ใช้เห็นประสบการณ์ฉบับโลคัลไลซ์อย่างครบถ้วน

Terminal
# Translate strings.xml files
> Translate res/values/strings.xml
  to Japanese, German, and Spanish

# Translate Play Store metadata too
> Translate fastlane/metadata/android/en-US/
  to de-DE, ja-JP, es-ES

✓ 6 files translated in 3.2s
i18n Agent จัดการการหลีก XML ของ Android รักษาเครื่องหมาย translatable="false" เคารพหมวดหมู่พหูพจน์ CLDR ของแต่ละภาษาเป้าหมาย และคงอาร์กิวเมนต์รูปแบบตามตำแหน่งไว้
JetBrains

มีปลั๊กอิน Android Studio

แปลทรัพยากร XML ของ Android จาก IDE โดยตรงด้วยปลั๊กอิน i18n Agent สำหรับ IntelliJ / Android Studio

Install
+

เพิ่มเติม : การใช้ภาษาสำรองอัจฉริยะด้วย LocaleChain

ระบบปฏิบัติการควบคุมการใช้ทรัพยากรสำรองของ Android เมื่อไม่มีคำแปล pt-BR Android จะข้าม pt-PT ไปทั้งหมดแล้วแสดงภาษาอังกฤษ LocaleChain ดักการค้นหาข้อความและไล่ตามลำดับการใช้ภาษาสำรองที่กำหนดค่าได้ ผู้ใช้ในแต่ละภูมิภาคจึงเห็นคำแปลที่ใกล้เคียงที่สุด

LocaleChain สำหรับ Android เป็นไลบรารี Kotlin แบบโอเพนซอร์ส ดูบน GitHub

build.gradle.kts
// build.gradle.kts (app module)
dependencies {
    implementation("com.i18nagent:locale-chain-android:0.1.0")
}
MyApp.kt / BaseActivity.kt
// 1. Application.onCreate() — configure chains once
class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        LocaleChain.configure()
    }
}

// 2. BaseActivity — wrap context per Activity
open class BaseActivity : AppCompatActivity() {
    override fun attachBaseContext(newBase: Context) {
        super.attachBaseContext(LocaleChain.wrap(newBase))
    }
}

// pt-BR user with only pt-PT translations?
// → Shows Portuguese instead of falling back to English

// Custom overrides for your specific locales:
LocaleChain.configure(
    overrides = mapOf("es-MX" to listOf("es-419", "es"))
)

ข้อผิดพลาดที่พบบ่อย

ข้อความเริ่มต้นที่หายไปทำให้แอปหยุดทำงาน

ต่างจาก iOS ที่แสดงคีย์ดิบ Android จะหยุดทำงานด้วย ResourceNotFoundException หากไม่มีข้อความใน res/values/strings.xml เริ่มต้น ตรวจให้ทุกคีย์อยู่ในไฟล์เริ่มต้นเสมอ

การแบ่งภาษาของ App Bundle ทำให้การสลับภาษาในแอปใช้งานไม่ได้

Google Play App Bundle แบ่ง APK ตามภาษา ผู้ใช้จะได้รับเฉพาะข้อความในภาษาของอุปกรณ์ หากมีการสลับภาษาในแอป ให้เพิ่ม bundle '{ language { enableSplit = false } }' ลงใน build.gradle.kts

เลย์เอาต์ RTL เสียหาย

สาเหตุคือใช้ left/right แทน start/end ในเลย์เอาต์ หรือไม่มี android:supportsRtl="true" ใน AndroidManifest.xml ใช้ Refactor > Add RTL Support ของ Android Studio เพื่อแปลงเลย์เอาต์เดิมโดยอัตโนมัติ

การปนเปื้อนของทรัพยากรไลบรารี

ไลบรารีของบุคคลที่สามรวมไฟล์ values-XX/strings.xml ของตัวเอง ทำให้ Android คิดว่าแอปรองรับภาษาที่ไม่ได้รองรับ ใช้ resConfigs ใน build.gradle.kts เพื่อจำกัดภาษาให้เหลือเฉพาะภาษาที่คุณแปลจริง

โครงสร้างไฟล์ที่แนะนำ

Project Structure
MyApp/
├── app/
│   └── src/main/
│       ├── res/
│       │   ├── values/
│       │   │   ├── strings.xml          # Default (source) strings
│       │   │   └── plurals.xml          # Plural rules
│       │   ├── values-de/
│       │   │   └── strings.xml
│       │   ├── values-ja/
│       │   │   └── strings.xml
│       │   └── values-es/
│       │       └── strings.xml
│       ├── java/com/example/myapp/
│       └── AndroidManifest.xml
├── fastlane/
│   └── metadata/android/
│       ├── en-US/
│       │   ├── title.txt
│       │   ├── short_description.txt
│       │   ├── full_description.txt
│       │   └── changelogs/default.txt
│       ├── de-DE/
│       └── ja-JP/
├── build.gradle.kts
└── settings.gradle.kts

แปลรายการต่อไปนี้ได้ด้วย :

ลองใช้ i18n Agent ตอนนี้

ลากและวางไฟล์แปลของคุณที่นี่

JSON, YAML, PO, XML, CSV, Markdown, Properties

หรือคลิกเพื่อเลือกไฟล์

ภาษาเป้าหมาย

ไม่ต้องลงทะเบียนประเมินราคาได้ทันที

การใช้ภาษาสำรองด้วย locale-chain-android

เมื่อไม่มีคีย์คำแปลในภาษาตามภูมิภาคอย่าง pt-BR Android จะข้ามไปใช้โฟลเดอร์ทรัพยากรเริ่มต้นทันทีแทนที่จะตรวจภาษาหลัก pt ก่อน

Terminal
implementation("com.i18nagent:locale-chain-android:0.1.0")
Configuration
import com.i18nagent.localechain.LocaleChain

LocaleChain.configure(
    overrides = mapOf(
        "pt-BR" to listOf("pt", "en"),
        "zh-Hant-HK" to listOf("zh-Hant", "zh", "en"),
    )
)

ดูคู่มือการใช้ภาษาสำรองของเราสำหรับรายการเฟรมเวิร์กที่รองรับทั้งหมดและลำดับสำเร็จรูป 75 รายการ Learn more →

คำถามที่พบบ่อยเกี่ยวกับโลคัลไลเซชัน Android