Locale — nó là gì, lớp Foundation và bản địa hóa

Tác giả: IT Sectr Đã đăng: 2026-07-12 Thời gian đọc: 9 phút

Locale là một lớp Foundation trong iOS và macOS, đóng gói các quy ước ngôn ngữ và văn hóa của người dùng: định dạng ngày tháng, số, tiền tệ và đơn vị đo lường. Theo Apple Developer Documentation, 2024, Locale xác định cách DateFormatter hiển thị tháng (tháng Một hoặc January), dấu phân cách thập phân trong số (dấu phẩy hoặc dấu chấm) và ký hiệu tiền tệ (rúp, đô la hoặc euro). Mỗi phiên bản Locale được gắn với một định danh như ru_RU hoặc en_US, trong đó phần đầu là mã ngôn ngữ (ISO 639-1) và phần thứ hai là mã khu vực (ISO 3166-1). Không giống như TimeZone, Locale không ảnh hưởng đến giá trị tuyệt đối của thời gian, chỉ ảnh hưởng đến biểu diễn chuỗi của nó.

Các điểm chính

  • Locale — một lớp Foundation cho cài đặt định dạng văn hóa và ngôn ngữ trong iOS và macOS
  • Định danh ru_RU — kết hợp mã ngôn ngữ (ISO 639-1) và mã khu vực (ISO 3166-1)
  • DateFormatter và NumberFormatter tự động sử dụng Locale để bản địa hóa đầu ra
  • Locale.current — ngôn ngữ vùng hệ thống của người dùng, có thể ghi đè cho các thao tác cụ thể
  • Cố định vs tự động cập nhật — Locale(identifier:) tạo ngôn ngữ vùng cố định, Locale.current theo dõi thay đổi trong cài đặt

Locale trong Foundation là gì?

Locale là một kiểu giá trị trong Swift (NSLocale trong Objective-C) đại diện cho một tập hợp các quy tắc định dạng cụ thể cho một ngôn ngữ và khu vực nhất định. Không giống như TimeZone, xác định độ lệch tuyệt đối của thời gian, Locale xác định cách thời gian, số và tiền tệ hiển thị trong biểu diễn chuỗi. Cùng một ngày 2024-07-21 sẽ được hiển thị là “21 tháng 7 năm 2024” cho ru_RU và “July 21, 2024” cho en_US.

Mỗi phiên bản Locale bao gồm hai thành phần: Ngôn ngữ (xác định tên tháng, tên ngày trong tuần, thứ tự từ) và Khu vực (xác định định dạng số, tiền tệ, lị). Sự kết hợp của các thành phần này được mã hóa thành định danh theo tiêu chuẩn BCP 47: ru_RU (tiếng Nga, Nga), en_US (tiếng Anh, Mỹ), de_DE (tiếng Đức, Đức).

Theo Unicode CLDR (2024), số lượng ngôn ngữ vùng được hỗ trợ trong iOS vượt quá 700 tổ hợp ngôn ngữ-khu vực. Foundation sử dụng dữ liệu từ CLDR (Common Locale Data Repository) — kho dữ liệu bản địa hóa toàn diện nhất được duy trì bởi Unicode Consortium. Điều này đảm bảo định dạng nhất quán trên tất cả các thiết bị Apple.

Locale ảnh hưởng thế nào đến định dạng ngày và số?

DateFormatter sử dụng Locale để chọn tên tháng và ngày chính xác, xác định thứ tự các thành phần ngày (ngày/tháng/năm hoặc tháng/ngày/năm) và dấu phân cách. Nếu không chỉ định Locale rõ ràng, DateFormatter sử dụng ngôn ngữ vùng của thiết bị — điều này đúng cho giao diện người dùng nhưng nguy hiểm cho dữ liệu máy chủ nơi định dạng phải cố định.

Thành phầnru_RUen_USde_DE
Ngày (medium)21 tháng 7 năm 2024Jul 21, 202421.07.2024
Số (1000.5)1 000,51,000.51.000,5
Tiền tệ (100)100,00 ₽$100.00100,00 €
LịchGregorianGregorianGregorian
Dấu phân cách danh sách;,;

NumberFormatter sử dụng Locale để xác định dấu phân cách thập phân (dấu phẩy hoặc dấu chấm), dấu phân cách nhóm (khoảng trắng, dấu phẩy, dấu chấm) và ký hiệu tiền tệ. Bỏ qua Locale khi phân tích số là một trong những nguyên nhân phổ biến gây lỗi trong các ứng dụng quốc tế: số “1,5” có nghĩa là “một rưỡi” cho ru_RU, nhưng đối với en_US, trình phân tích kỹ thuật số sẽ đọc nó là “năm” sau dấu phẩy.

Quan trọng: Calendar được tạo qua Calendar.current kế thừa ngôn ngữ vùng của thiết bị. Calendar(identifier: .gregorian) với ngôn ngữ vùng được đặt rõ ràng là cách tiếp cận được khuyến nghị để định dạng có thể dự đoán được. Khi làm việc với ngày ISO 8601, luôn sử dụng Locale(identifier: “en_US_POSIX”) — một ngôn ngữ vùng đặc biệt cho định dạng kỹ thuật không bị ảnh hưởng bởi cài đặt khu vực.

Định danh Locale: các định dạng cơ bản

Định danh Locale bao gồm mã ngôn ngữ (ISO 639-1, hai ký tự) và mã khu vực (ISO 3166-1, hai ký tự), được phân tách bằng dấu gạch dưới. Ví dụ: ru_RU, en_US, fr_FR, zh_Hans_CN (tiếng Trung, chữ viết đơn giản hóa, Trung Quốc). Foundation cũng hỗ trợ các định danh ở định dạng BCP 47: ru-RU, en-US, được sử dụng trong các tiêu chuẩn web.

Ngoài các định danh đầy đủ, Locale có thể được tạo chỉ bằng ngôn ngữ: Locale(identifier: “ru”) trả về ngôn ngữ vùng với tiếng Nga và khu vực mặc định cho ngôn ngữ đó (thường là Nga). Tương tự cho tiếng Anh: Locale(identifier: “en”) sử dụng khu vực Mỹ. Cách tiếp cận này hữu ích để đặt ngôn ngữ định dạng mà không bị ràng buộc với một khu vực cụ thể.

Ngôn ngữ vùng đặc biệt bao gồm en_US_POSIX — một ngôn ngữ vùng kỹ thuật để định dạng ngày và số tự động, đảm bảo định dạng ổn định bất kể cài đặt người dùng. Ngôn ngữ vùng này là bắt buộc để phân tích ngày từ API máy chủ, đặc biệt là cho định dạng ISO 8601. Nó sử dụng lịch Gregorian, định dạng 24 giờ và dấu chấm làm dấu phân cách thập phân.

swift
import Foundation

// Định danh ngôn ngữ vùng khả dụng
let available: [String] = Locale.availableIdentifiers
print("Tổng số ngôn ngữ vùng: \(available.count)")

// Lọc ngôn ngữ vùng tiếng Nga
let russianLocales = available.filter { $0.hasPrefix("ru") }
print("Ngôn ngữ vùng tiếng Nga: \(russianLocales)")

// Thành phần ngôn ngữ vùng
let locale = Locale(identifier: "de_DE")
print("Ngôn ngữ: \(locale.languageCode ?? "nil")")
print("Khu vực: \(locale.regionCode ?? "nil")")
print("Tiền tệ: \(locale.currencyCode ?? "nil")")
print("Lịch: \(locale.calendar.identifier)")

Kiểm tra ngôn ngữ vùng khả dụng qua Locale.availableIdentifiers trả về một mảng tất cả các định danh được hỗ trợ bởi phiên bản iOS hiện tại. Để lọc theo khu vực, hãy sử dụng Locale.availableIdentifiers.filter với kiểm tra regionCode. Điều này hữu ích để xây dựng giao diện chọn khu vực mà không cần danh sách cứng.

Xác định ngôn ngữ vùng hiện tại của người dùng trong Swift

Locale.current là cách chính để lấy ngôn ngữ vùng hiện tại của thiết bị do người dùng đặt trong cài đặt iOS (Settings > General > Language & Region). Thuộc tính này tự động cập nhật khi thay đổi ngôn ngữ hoặc khu vực trong cài đặt mà không cần khởi động lại ứng dụng. Tuy nhiên, nó có thể không khớp với ngôn ngữ vùng ưa thích để hiển thị nội dung: người dùng có thể đặt ngôn ngữ giao diện là tiếng Anh nhưng xem ngày tháng theo định dạng tiếng Nga.

Để xác định chính xác hơn sở thích của người dùng, hãy sử dụng Locale.preferredLanguages — một mảng các ngôn ngữ được sắp xếp theo ưu tiên của người dùng. Phần tử đầu tiên là ngôn ngữ giao diện chính. Danh sách này tương ứng với cài đặt trong Language & Region, bao gồm việc kéo thả ngôn ngữ theo thứ tự ưu tiên. Các ứng dụng giao tiếp (trình nhắn tin, ứng dụng email) nên xem xét thứ tự này khi chọn ngôn ngữ hiển thị nội dung.

swift
import Foundation

// Ngôn ngữ vùng hệ thống hiện tại
let current = Locale.current
print("Ngôn ngữ vùng hiện tại: \(current.identifier)")
print("Ngôn ngữ: \(current.language?.disjointName ?? "nil")")

// Ngôn ngữ ưa thích của người dùng
let preferred = Locale.preferredLanguages
print("Ngôn ngữ ưa thích: \(preferred)")

// Lấy khu vực từ ngôn ngữ vùng hiện tại
if let region = current.regionCode {
    let regionLocale = Locale(identifier: "en_\(region)")
    let countryName = regionLocale.localizedString(
        forRegionCode: region
    )
    print("Quốc gia: \(countryName ?? region)")
}

// Kiểm tra định dạng 24h
let uses24h = current.uses24hClock(
    for: .dateAndTime
)
print("Sử dụng 24h: \(uses24h)")

Bản địa hóa trong giao diện người dùng: để hiển thị tên tháng và ngày bằng ngôn ngữ giao diện, hãy sử dụng Calendar với ngôn ngữ vùng được đặt. Calendar.current.symbols(for: .month) trả về tên tháng bằng ngôn ngữ của ngôn ngữ vùng hiện tại. Để hiển thị tên quốc gia bằng ngôn ngữ của người dùng, hãy sử dụng Locale.current.localizedString(forRegionCode:).

Locale trong Swift: ví dụ sử dụng

Định dạng ngày có nhận biết ngôn ngữ vùng là một nhiệm vụ quan trọng khi hiển thị ngày cho người dùng. DateFormatter với ngôn ngữ vùng được đặt tự động chọn định dạng ngày và giờ chính xác cho khu vực của người dùng. Đối với dateStyle và timeStyle với các giá trị .short, .medium, .long, .full, bộ định dạng sử dụng quy tắc ngôn ngữ vùng để tổng hợp các thành phần ngày.

swift
import Foundation

let date = Date()

// Định dạng với các ngôn ngữ vùng khác nhau
let formatter = DateFormatter()
formatter.dateStyle = .medium

formatter.locale = Locale(identifier: "ru_RU")
print("Tiếng Nga: \(formatter.string(from: date))")

formatter.locale = Locale(identifier: "en_US")
print("Tiếng Anh: \(formatter.string(from: date))")

formatter.locale = Locale(identifier: "ja_JP")
print("Tiếng Nhật: \(formatter.string(from: date))")

// Định dạng tiền tệ với ngôn ngữ vùng
let numFormatter = NumberFormatter()
numFormatter.numberStyle = .currency

numFormatter.locale = Locale(identifier: "de_DE")
print("Tiền tệ Đức: \(numFormatter.string(from: 1234.56) ?? "nil")")

numFormatter.locale = Locale(identifier: "en_US")
print("Tiền tệ Mỹ: \(numFormatter.string(from: 1234.56) ?? "nil")")

Phân tích ngày từ API máy chủ phải luôn sử dụng Locale(identifier: “en_US_POSIX”) cho định dạng cố định. Máy chủ thường gửi ngày ở định dạng ISO 8601 với tên tháng bằng tiếng Anh và việc sử dụng ngôn ngữ vùng hiện tại của thiết bị có thể gây ra lỗi phân tích nếu người dùng ở khu vực không nói tiếng Anh. en_US_POSIX đảm bảo việc phân tích không phụ thuộc vào cài đặt thiết bị.

swift
import Foundation

// Phân tích ngày máy chủ chính xác
let isoFormatter = DateFormatter()
isoFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
isoFormatter.locale = Locale(identifier: "en_US_POSIX")
isoFormatter.timeZone = TimeZone(secondsFromGMT: 0)

let serverDate = "2024-07-21T14:30:00+0000"
if let parsed = isoFormatter.date(from: serverDate) {
    print("Ngày đã phân tích: \(parsed)")
}

// Tên tiền tệ được bản địa hóa
let usLocale = Locale(identifier: "en_US")
let currencyName = usLocale.localizedString(
    forCurrencyCode: "RUB"
)
print("Rúp Nga trong ngôn ngữ vùng Mỹ: \(currencyName ?? "nil")")

Khả năng bổ sung: Locale cung cấp mô tả được bản địa hóa của các thành phần thông qua các phương thức localizedString(forRegionCode:), localizedString(forLanguageCode:), localizedString(forCurrencyCode:) và localizedString(forCalendarIdentifier:). Các phương thức này trả về tên bằng ngôn ngữ của ngôn ngữ vùng mà chúng được gọi. Ví dụ, Locale(identifier: “ru_RU”).localizedString(forCountryCode: “DE”) trả về “Đức”.

Các lỗi thường gặp khi làm việc với Locale

Bỏ qua Locale khi phân tích số là một lỗi nghiêm trọng trong các ứng dụng quốc tế. NumberFormatter không có ngôn ngữ vùng rõ ràng sử dụng ngôn ngữ vùng hiện tại của thiết bị. Nếu người dùng ở Nga nhập “1,5”, NumberFormatter.number(from: “1,5”) trả về chính xác 1.5. Nhưng nếu cùng mã đó chạy trên thiết bị có ngôn ngữ vùng en_US, việc phân tích trả về nil vì đối với en_US, dấu phân cách thập phân là dấu chấm.

Thiếu en_US_POSIX cho ngày máy chủ dẫn đến các lỗi khó phát hiện. DateFormatter với dateFormat và locale = Locale.current có thể hỏng đối với người dùng từ các khu vực có định dạng ngày khác với định dạng Mỹ. Ví dụ, ở Đức, DateFormatter có thể mong đợi “21.07.2024” trong khi máy chủ gửi “07/21/2024”. en_US_POSIX đảm bảo định dạng cố định cho phân tích tự động bất kể khu vực của người dùng.

So sánh chuỗi ngày thay vì sử dụng Date là một lỗi phổ biến khác. Các nhà phát triển đôi khi so sánh biểu diễn chuỗi của ngày từ các ngôn ngữ vùng khác nhau, nhận được kết quả không chính xác. Locale chỉ thay đổi cách hiển thị, không thay đổi giá trị tuyệt đối của ngày. Luôn so sánh các đối tượng Date, không phải biểu diễn chuỗi của chúng. Để so sánh các thành phần ngày, hãy sử dụng Calendar với ngôn ngữ vùng được đặt rõ ràng.

Theo WWDC 2023, khoảng 30% vấn đề quốc tế hóa trong ứng dụng liên quan đến cấu hình Locale không chính xác. Apple khuyến nghị luôn đặt rõ ràng ngôn ngữ vùng cho DateFormatter và NumberFormatter khi làm việc với dữ liệu máy chủ và chỉ sử dụng Locale.current để hiển thị giao diện người dùng. Thực hành đơn giản này loại bỏ hầu hết các lỗi liên quan đến cài đặt khu vực.

Các câu hỏi thường gặp

Locale trong Foundation là gì?

Locale là một lớp Foundation đại diện cho các quy tắc định dạng văn hóa và ngôn ngữ: định dạng ngày, số, tiền tệ và đơn vị đo lường. Nó được sử dụng cùng với DateFormatter, NumberFormatter và Calendar để hiển thị dữ liệu được bản địa hóa.

Sự khác biệt giữa Locale và TimeZone là gì?

Locale xác định định dạng hiển thị (ngôn ngữ, quy ước khu vực), trong khi TimeZone xác định độ lệch tuyệt đối của thời gian so với UTC. Locale ảnh hưởng đến biểu diễn chuỗi, TimeZone ảnh hưởng đến giá trị số của thời gian. Cả hai được sử dụng cùng nhau để định dạng ngày hoàn chỉnh.

en_US_POSIX là gì và tại sao cần nó?

en_US_POSIX là một ngôn ngữ vùng đặc biệt cho định dạng kỹ thuật đảm bảo định dạng ổn định bất kể cài đặt người dùng. Nó bắt buộc để phân tích ngày máy chủ (ISO 8601) và làm việc với các API nơi định dạng phải có thể dự đoán được.

Làm thế nào để lấy danh sách tất cả ngôn ngữ vùng khả dụng?

Locale.availableIdentifiers trả về một mảng chuỗi với các định danh của tất cả ngôn ngữ vùng được hỗ trợ. Để lọc theo ngôn ngữ, sử dụng filter với hasPrefix; để lấy khu vực, sử dụng Locale(identifier:).regionCode.

Locale ảnh hưởng thế nào đến NumberFormatter?

NumberFormatter sử dụng Locale để xác định dấu phân cách thập phân (dấu phẩy hoặc dấu chấm), ký hiệu tiền tệ và dấu phân cách nhóm. Để có định dạng cố định, hãy đặt ngôn ngữ vùng thành en_US_POSIX hoặc đặt rõ ràng các thuộc tính của bộ định dạng.

Tổng kết

  • Locale — một lớp cơ bản để bản địa hóa định dạng ngày, số và tiền tệ trong iOS và macOS
  • Định danh ru_RU mã hóa ngôn ngữ (ISO 639-1) và khu vực (ISO 3166-1) cho cài đặt định dạng chính xác
  • DateFormatter và NumberFormatter tự động sử dụng Locale.current cho đầu ra được bản địa hóa
  • en_US_POSIX — ngôn ngữ vùng bắt buộc để phân tích ngày máy chủ và định dạng tự động cố định
  • Locale.current theo dõi thay đổi trong cài đặt, Locale(identifier:) tạo ngôn ngữ vùng cố định
  • Phân tích số yêu cầu đặt ngôn ngữ vùng rõ ràng — dấu phân cách thập phân phụ thuộc vào khu vực người dùng
  • Calendar.current kế thừa ngôn ngữ vùng của thiết bị; để tính toán có thể dự đoán, hãy đặt ngôn ngữ vùng rõ ràng

Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay

IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.

Thảo luận dự án

Đọc thêm