
Hướng dẫn toàn diện về bản địa hóa app iOS
Từ Localizable.strings đến siêu dữ liệu App Store: bản địa hóa app iOS bằng Xcode, SwiftUI, Fastlane và công nghệ dịch AI tự động.
Bật tính năng bản địa hóa trong Xcode
Mở phần cài đặt dự án Xcode, chuyển đến Info > Localizations rồi thêm các ngôn ngữ bạn muốn hỗ trợ. Xcode tự động tạo thư mục .lproj cho từng ngôn ngữ.
// In Xcode:
// 1. Select your project in the navigator
// 2. Go to Info tab > Localizations
// 3. Click + to add languages (e.g., German, Japanese)
// 4. Select which files to localize
//
// Xcode creates .lproj directories automatically:
// en.lproj/Localizable.strings
// de.lproj/Localizable.strings
// ja.lproj/Localizable.stringsTạo Localizable.strings
Tệp bản địa hóa iOS tiêu chuẩn sử dụng các cặp khóa-giá trị phân tách bằng dấu bằng và mỗi dòng kết thúc bằng dấu chấm phẩy. Đặt tệp trong thư mục Base.lproj dành cho ngôn ngữ nguồn.
// Base.lproj/Localizable.strings
"welcome_title" = "Welcome to MyApp";
"login_button" = "Sign In";
"settings_label" = "Settings";
"greeting" = "Hello, %@!"; // %@ = string placeholder
"item_count" = "%d items"; // %d = integer placeholder// ❌ Common mistakes in .strings files:
// Missing semicolon — file loads but translations are empty
"welcome_title" = "Welcome"
// Unescaped quotes — causes parse error
"message" = "Click "here" to continue";
// ✅ Correct versions:
"welcome_title" = "Welcome";
"message" = "Click \"here\" to continue";Chuyển sang String Catalogs (Xcode 15+)
String Catalogs (.xcstrings) là giải pháp hiện đại của Apple thay thế các tệp .strings. Công cụ này cung cấp trình chỉnh sửa trực quan trong Xcode, tự động trích xuất chuỗi từ các view SwiftUI và tích hợp sẵn khả năng hỗ trợ số nhiều.
// Xcode 15+ String Catalog (Localizable.xcstrings)
// Xcode automatically extracts strings from your code
// and manages translations in a visual editor.
// In SwiftUI, strings are automatically localizable:
Text("Welcome to MyApp")
Text("Hello, \(userName)!")
// Mark strings explicitly:
let title = String(localized: "welcome_title")Dùng chuỗi đã bản địa hóa trong SwiftUI và UIKit
View Text của SwiftUI tự động bản địa hóa chuỗi ký tự. UIKit dùng NSLocalizedString. Trên iOS 16+, API String(localized:comment:) hiện đại cung cấp cú pháp gọn hơn và tích hợp sẵn khả năng hỗ trợ của trình biên dịch.
import SwiftUI
struct ContentView: View {
let userName: String
var body: some View {
VStack {
// ✅ SwiftUI auto-localizes string literals
Text("welcome_title")
// ⚠️ This does NOT localize (String interpolation)
// Text("Hello, \(userName)")
// ✅ Use String(localized:) for dynamic strings
Text(String(localized: "greeting \(userName)"))
// ✅ UIKit style (works everywhere)
let title = NSLocalizedString(
"settings_label",
comment: "Settings screen title"
)
// ✅ Modern API (iOS 16+)
let modern = String(
localized: "welcome_title",
comment: "Main screen title"
)
}
}
}Xử lý số nhiều
iOS dùng tệp .stringsdict cho quy tắc số nhiều và hỗ trợ mọi nhóm số nhiều CLDR: zero, one, two, few, many, other. String Catalogs xử lý số nhiều bằng trình chỉnh sửa trực quan trong Xcode — đơn giản hơn nhiều so với viết XML stringsdict thủ công.
<!-- Localizable.stringsdict -->
<?xml version="1.0" encoding="UTF-8"?>
<plist version="1.0">
<dict>
<key>items_count</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@count@</string>
<key>count</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>zero</key>
<string>No items</string>
<key>one</key>
<string>%d item</string>
<key>other</key>
<string>%d items</string>
</dict>
</dict>
</dict>
</plist>
// Usage in Swift:
String(format: NSLocalizedString("items_count", comment: ""),
itemCount)Bản địa hóa siêu dữ liệu App Store bằng Fastlane
Dùng công cụ deliver của Fastlane để lưu siêu dữ liệu App Store — tên app, phụ đề, mô tả, từ khóa và ghi chú phát hành — dưới dạng tệp văn bản thuần túy được sắp xếp theo locale và quản lý phiên bản trong kho lưu trữ.
# Install Fastlane
$ gem install fastlane
# Initialize deliver for App Store metadata
$ fastlane deliver init
# Directory structure created:
# fastlane/metadata/
# ├── en-US/
# │ ├── name.txt # App name (30 chars)
# │ ├── subtitle.txt # Subtitle (30 chars)
# │ ├── description.txt # Full description
# │ ├── keywords.txt # Search keywords (100 chars)
# │ ├── release_notes.txt # What's New
# │ └── promotional_text.txt
# ├── de-DE/
# │ └── ...
# └── ja/
# └── ...
# Push metadata to App Store Connect:
$ fastlane deliverKiểm thử bản địa hóa
Kiểm thử nội dung đã bản địa hóa mà không cần đổi ngôn ngữ thiết bị. Dùng chế độ ghi đè scheme của Xcode để chạy app bằng bất kỳ ngôn ngữ nào, bản xem trước SwiftUI với môi trường locale và XCUITest kèm đối số khởi chạy để kiểm thử tự động.
// 1. Xcode Scheme Override:
// Edit Scheme > Run > Options > App Language > Choose language
// 2. SwiftUI Preview with locale:
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.environment(\.locale, Locale(identifier: "de"))
ContentView()
.environment(\.locale, Locale(identifier: "ja"))
ContentView()
.environment(\.locale, Locale(identifier: "ar"))
}
}
// 3. XCUITest with language override:
let app = XCUIApplication()
app.launchArguments += ["-AppleLanguages", "(de)"]
app.launchArguments += ["-AppleLocale", "de_DE"]
app.launch()Tự động bảo đảm chất lượng bản dịch
Tự động dịch
Dùng AI để dịch các tệp .strings, .xcstrings và siêu dữ liệu Fastlane. Tự động dịch cả chuỗi trong app lẫn siêu dữ liệu App Store để bản địa hóa toàn diện.
# Translate .strings files
> Translate Base.lproj/Localizable.strings
to Japanese, German, and Spanish
# Translate App Store metadata too
> Translate fastlane/metadata/en-US/
to de-DE, ja, es-MX
✓ 6 files translated in 3.2sPhần thêm: Cơ chế dự phòng locale thông minh với LocaleChain
Theo mặc định, iOS chuyển về ngôn ngữ phát triển khi không có locale chính xác của người dùng. Người dùng pt-BR chỉ có bản dịch pt-PT sẽ thấy tiếng Anh thay vì tiếng Bồ Đào Nha. LocaleChain khắc phục vấn đề này bằng các chuỗi dự phòng có thể cấu hình.
LocaleChain là gói Swift nguồn mở. Xem trên GitHub
// Swift Package Manager
// File > Add Package Dependencies >
// https://github.com/i18n-agent/ios-localechain.gitimport LocaleChain
// In your App init or AppDelegate:
LocaleChain.configure() // Activates all default chains
// 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: ["es-MX": ["es-419", "es"]]
)Lỗi thường gặp
Lỗi cú pháp tệp .strings
Phép nội suy Text của SwiftUI không bản địa hóa
Widget/extension hiển thị khóa thô
Bản dịch bị thiếu làm lộ khóa trong môi trường thực tế
Cấu trúc tệp đề xuất
MyApp/
├── MyApp.xcodeproj
├── MyApp/
│ ├── Base.lproj/
│ │ ├── Localizable.strings # Source strings
│ │ └── Localizable.stringsdict # Plural rules
│ ├── en.lproj/
│ │ └── Localizable.strings
│ ├── de.lproj/
│ │ └── Localizable.strings
│ ├── ja.lproj/
│ │ └── Localizable.strings
│ ├── Localizable.xcstrings # OR String Catalog
│ └── Info.plist
├── MyAppTests/
├── fastlane/
│ ├── Fastfile
│ └── metadata/
│ ├── en-US/
│ │ ├── name.txt
│ │ ├── description.txt
│ │ └── keywords.txt
│ ├── de-DE/
│ └── ja/
└── Package.swiftDùng thử i18n Agent ngay
Thả tệp bản dịch của bạn vào đây
JSON, YAML, PO, XML, CSV, Markdown, Properties
hoặc nhấp để duyệt
Ngôn ngữ đích
Cơ chế dự phòng locale với ios-localechain
Khi locale theo khu vực như de-AT thiếu khóa bản dịch, iOS chuyển thẳng sang ngôn ngữ phát triển thay vì kiểm tra locale cha de trước.
// Swift Package Manager
// https://github.com/i18n-agent/ios-localechainimport LocaleChain
LocaleChain.configure(overrides: [
"de": ["en-GB", "en"],
"pt-BR": ["pt", "en"],
"zh-Hant-HK": ["zh-Hant", "zh", "en"],
])Xem Hướng dẫn về cơ chế dự phòng locale để biết danh sách đầy đủ các framework được hỗ trợ và 75 chuỗi tích hợp sẵn. Learn more →