Đa ngôn ngữ trong ứng dụng di động: localization và những cạm bẫy thường gặp

  • author-image

    Nguyễn Đình Cường

  • blog-tag
  • blog-comment 0 comment
  • created-date 09 Nov, 2026
blog-thumbnail

Một ứng dụng chỉ có tiếng Anh hay chỉ có tiếng Việt sẽ bỏ lỡ nhiều người dùng. Localization là việc làm cho ứng dụng thích ứng với ngôn ngữ và vùng miền khác nhau. Làm sớm thì nhẹ nhàng, làm muộn thì phải sửa rất nhiều chỗ. Bài viết giới thiệu cách tiếp cận và các lỗi thường gặp.

i18n và l10n

Internationalization (i18n) là thiết kế ứng dụng sao cho có thể hỗ trợ nhiều ngôn ngữ mà không phải sửa mã. Localization (l10n) là việc thực hiện dịch và điều chỉnh cho từng vùng cụ thể. Bạn làm i18n một lần, rồi l10n cho mỗi ngôn ngữ mới.

Nguyên tắc đầu tiên: không viết cứng chuỗi

Đừng viết chữ trực tiếp trong giao diện như Text('Đăng nhập'). Hãy đưa mọi chuỗi hiển thị vào tệp tài nguyên theo ngôn ngữ, rồi giao diện tham chiếu bằng khóa. Khi cần thêm ngôn ngữ, chỉ cần thêm một tệp dịch.

Cách làm trong Flutter

Flutter dùng tệp ARB (Application Resource Bundle), định dạng JSON. Khai báo trong pubspec.yaml bật generate: true và thêm gói flutter_localizations cùng intl, rồi tạo tệp cấu hình l10n.yaml. Ví dụ hai tệp ARB:

// lib/l10n/app_vi.arb
{
  "@@locale": "vi",
  "loginButton": "Đăng nhập",
  "welcomeUser": "Xin chào, {name}!",
  "@welcomeUser": {
    "placeholders": { "name": { "type": "String" } }
  },
  "itemCount": "{count, plural, =0{Không có mục nào} other{{count} mục}}",
  "@itemCount": {
    "placeholders": { "count": { "type": "int" } }
  }
}
// lib/l10n/app_en.arb
{
  "@@locale": "en",
  "loginButton": "Log in",
  "welcomeUser": "Hello, {name}!",
  "@welcomeUser": {
    "placeholders": { "name": { "type": "String" } }
  },
  "itemCount": "{count, plural, =0{No items} =1{1 item} other{{count} items}}",
  "@itemCount": {
    "placeholders": { "count": { "type": "int" } }
  }
}

Sau khi tạo mã, sử dụng trong giao diện:

Text(AppLocalizations.of(context)!.welcomeUser('Bem'))

Cách làm ở nền tảng khác

  • Android: thư mục res/values/strings.xml cho ngôn ngữ mặc định và values-vi, values-en... cho từng ngôn ngữ; gọi bằng stringResource(R.string.login) trong Compose.
  • iOS: tệp String Catalog hoặc Localizable.strings, dùng String(localized:) hoặc Text("key") trong SwiftUI.
  • React Native: thư viện như i18next kết hợp react-i18next hoặc expo-localization để biết ngôn ngữ thiết bị.

Những cạm bẫy hay gặp

  1. Nối chuỗi: ghép "Bạn có " + n + " tin nhắn" khiến việc dịch sai vì thứ tự từ mỗi ngôn ngữ khác nhau. Dùng chuỗi có tham số.
  2. Số nhiều: tiếng Anh phân biệt một và nhiều, tiếng Việt thì không, nhưng một số ngôn ngữ có nhiều dạng số nhiều. Dùng cơ chế plural của thư viện.
  3. Định dạng ngày, giờ, số, tiền tệ: dùng API định dạng theo vùng thay vì tự ghép chuỗi.
  4. Độ dài văn bản: bản dịch có thể dài hơn gốc đến hàng chục phần trăm, khiến nút hay nhãn bị tràn; thiết kế bố cục linh hoạt.
  5. Hướng chữ: các ngôn ngữ viết từ phải sang trái như tiếng Ả Rập cần giao diện phản chiếu.
  6. Chuỗi nằm trong ảnh: chữ bị đóng vào ảnh không dịch được.
import 'package:intl/intl.dart';

final money = NumberFormat.currency(locale: 'vi_VN', symbol: '₫');
print(money.format(1250000));

final date = DateFormat.yMMMMd('vi').format(DateTime(2025, 3, 8));

Quy trình dịch hiệu quả

  • Viết khóa có ý nghĩa và có chú thích ngữ cảnh cho người dịch.
  • Dùng ngôn ngữ nguồn thống nhất làm chuẩn để dịch sang các ngôn ngữ khác.
  • Dùng công cụ quản lý bản dịch khi có nhiều ngôn ngữ hoặc nhiều người dịch.
  • Nhờ người bản ngữ kiểm tra, đừng chỉ dựa vào dịch máy.

Cho người dùng chọn ngôn ngữ

Mặc định theo ngôn ngữ thiết bị, nhưng nên có tùy chọn đổi ngôn ngữ trong cài đặt. Lưu lựa chọn và áp dụng ngay không cần khởi động lại nếu có thể. Cũng cần có ngôn ngữ dự phòng khi thiếu bản dịch cho một khóa.

Đừng quên trang cửa hàng

Mô tả ứng dụng, ảnh chụp màn hình và từ khóa trên Google Play và App Store cũng có thể bản địa hóa, giúp người dùng ở mỗi nơi tìm thấy bạn dễ hơn.

Kết luận

Hãy đưa mọi chuỗi ra tệp tài nguyên ngay từ đầu, dùng tham số và plural thay vì nối chuỗi, và thiết kế bố cục chịu được văn bản dài. Làm như vậy việc thêm ngôn ngữ mới sau này chỉ còn là chuyện dịch.

Kiểm thử bản địa hóa

Đừng chỉ dịch rồi tin là xong. Hãy chạy ứng dụng ở từng ngôn ngữ và duyệt mọi màn hình để tìm văn bản bị tràn, bị cắt hoặc còn sót tiếng gốc. Thử đổi ngôn ngữ hệ thống khi ứng dụng đang chạy để chắc nó cập nhật đúng. Một mẹo hay là tạo một ngôn ngữ giả có chuỗi dài hơn nhiều để phát hiện sớm vấn đề bố cục trước khi có bản dịch thật.

Để học cách xây dựng ứng dụng sẵn sàng cho nhiều thị trường, bạn có thể xem các khóa học tại codebem.com.

author_photo
Nguyễn Đình Cường

0 comment