Locale — 什么是 Foundation 类和本地化

作者: IT Sectr 发布日期: 2026-07-12 阅读时间: 9 分钟

Locale — 是 iOS 和 macOS 中的一个 Foundation 类,封装了用户的语言和文化约定:日期、数字、货币和度量单位的格式。根据 Apple Developer Documentation, 2024Locale 决定了 DateFormatter 如何显示月份(一月或 January)、数字中的小数分隔符(逗号或点号)以及货币符号(人民币、美元或欧元)。每个 Locale 实例都绑定到一个标识符,如 ru_RU 或 en_US,其中第一部分是语言代码(ISO 639-1),第二部分是地区代码(ISO 3166-1)。与 TimeZone 不同,Locale 不影响时间的绝对值,只影响其字符串表示形式。

要点

  • Locale — iOS 和 macOS 中用于文化和语言格式设置的 Foundation 类
  • ru_RU 标识符 — 语言代码(ISO 639-1)和地区代码(ISO 3166-1)的组合
  • DateFormatter 和 NumberFormatter 自动使用 Locale 进行输出本地化
  • Locale.current — 用户的系统区域设置,可为单个操作覆盖
  • 固定 vs 自动更新 — Locale(identifier:) 创建固定区域设置,Locale.current 跟踪设置更改

Foundation 中的 Locale 是什么?

Locale — 是 Swift 中的值类型(Objective-C 中的 NSLocale),代表一组特定于某种语言和地区的格式化规则。与确定绝对时间偏移量的 TimeZone 不同,Locale 决定了时间、数字和货币在字符串表示中的外观。同一个日期 2024-07-21 在 ru_RU 中显示为"2024年7月21日",在 en_US 中显示为"July 21, 2024"。

每个 Locale 实例由两个组件组成:Language(语言 — 确定月份名称、星期几、词序)和 Region(地区 — 确定数字格式、货币、日历)。这些组件的组合根据 BCP 47 标准在标识符中编码:ru_RU(俄语,俄罗斯)、en_US(英语,美国)、de_DE(德语,德国)。

根据 Unicode CLDR(2024),iOS 中支持的区域设置列表超过 700 种语言-地区组合。Foundation 使用来自 CLDR(通用区域设置数据仓库)的数据 — 这是由 Unicode 联盟支持的最全面的本地化数据存储库。这保证了所有 Apple 设备上格式化的统一性。

Locale 如何影响日期和数字的格式化?

DateFormatter 使用 Locale 来选择正确的月份和星期名称,确定日期组件的顺序(日/月/年或月/日/年)和分隔符。如果没有明确指定 Locale,DateFormatter 将使用设备的区域设置 — 这对 UI 是正确的,但对于格式必须固定的服务器数据来说是危险的。

组件ru_RUen_USde_DE
日期(中等)2024年7月21日Jul 21, 202421.07.2024
数字(1000.5)1 000,51,000.51.000,5
货币(100)100,00 ₽$100.00100,00 €
日历公历公历公历
列表分隔符,;

NumberFormatter 使用 Locale 来确定小数分隔符(逗号或点号)、分组分隔符(空格、逗号、点号)和货币符号。在解析数字时忽略 Locale 是国际化应用程序中常见的错误原因之一:数字"1,5"对于 ru_RU 来说意味着"一点五",但对于 en_US,数字分析器将其读作逗号后的"五"。

重要: 通过 Calendar.current 创建的 Calendar 继承了设备的区域设置。带有明确指定 locale 的 Calendar(identifier: .gregorian) — 推荐的可预测格式化的方法。在使用 ISO 8601 日期时,始终使用 Locale(identifier: "en_US_POSIX") — 一种用于技术格式化的特殊区域设置,不受区域设置的影响。

Locale 标识符:基本格式

Locale 标识符由语言代码(ISO 639-1,两个字符)和地区代码(ISO 3166-1,两个字符)组成,用下划线分隔。示例:ru_RU、en_US、fr_FR、zh_Hans_CN(中文,简体,中国)。Foundation 还支持 BCP 47 格式的标识符:ru-RU、en-US,用于 Web 标准。

除了完整的标识符之外,Locale 还可以仅基于语言创建:Locale(identifier: "ru") 返回一个使用俄语和该语言默认地区(通常为俄罗斯)的区域设置。类似地,对于英语:Locale(identifier: "en") 使用美国地区。这种方法对于设置格式化语言而不绑定到特定地区非常有用。

特殊区域设置包括 en_US_POSIX — 一种用于日期和数字机器格式化的技术区域设置,无论用户设置如何都能保证稳定的格式。此区域设置对于解析服务器 API 的日期是强制性的,尤其是 ISO 8601 格式。它使用公历、24 小时时间格式和点号作为小数分隔符。

swift
import Foundation

// 可用的区域设置标识符
let available: [String] = Locale.availableIdentifiers
print("区域设置总数:\(available.count)")

// 过滤俄语区域设置
let russianLocales = available.filter { $0.hasPrefix("ru") }
print("俄语区域设置:\(russianLocales)")

// 区域设置组件
let locale = Locale(identifier: "de_DE")
print("语言:\(locale.languageCode ?? "nil")")
print("地区:\(locale.regionCode ?? "nil")")
print("货币:\(locale.currencyCode ?? "nil")")
print("日历:\(locale.calendar.identifier)")

检查可用的区域设置通过 Locale.availableIdentifiers 返回当前 iOS 版本支持的所有标识符数组。要按地区过滤,请使用 Locale.availableIdentifiers.filter 并检查 regionCode。这对于构建没有硬编码列表的地区选择 UI 非常有用。

在 Swift 中确定用户的当前区域设置

Locale.current — 获取用户在 iOS 设置(Settings > General > Language & Region)中设置的设备当前区域设置的主要方法。当在设置中更改语言或地区时,此属性会自动更新,无需重新启动应用程序。但是,它可能与用于显示内容的首选区域设置不一致:用户可以将界面语言设置为英语,但以俄语格式查看日期。

要更准确地确定用户偏好,请使用 Locale.preferredLanguages — 按用户优先级排序的语言数组。第一个元素是主界面语言。此列表对应于 Language & Region 中的设置,包括按偏好顺序拖动语言。通信应用程序(即时通讯工具、电子邮件客户端)在选择内容显示语言时应考虑此顺序。

swift
import Foundation

// 当前系统区域设置
let current = Locale.current
print("当前区域设置:\(current.identifier)")
print("语言:\(current.language?.disjointName ?? "nil")")

// 用户偏好的语言
let preferred = Locale.preferredLanguages
print("偏好的语言:\(preferred)")

// 从当前区域设置获取地区
if let region = current.regionCode {
    let regionLocale = Locale(identifier: "en_\(region)")
    let countryName = regionLocale.localizedString(
        forRegionCode: region
    )
    print("国家:\(countryName ?? region)")
}

// 检查24小时格式
let uses24h = current.uses24hClock(
    for: .dateAndTime
)
print("使用24小时:\(uses24h)")

UI 中的本地化:要以界面语言显示月份和星期几的名称,请使用设置了 locale 的 Calendar。Calendar.current.symbols(for: .month) 返回当前区域设置语言的月份名称。要以用户语言显示国家名称,请使用 Locale.current.localizedString(forRegionCode:)。

Swift 中的 Locale:使用示例

日期格式化考虑区域设置 — 向用户显示日期时的关键任务。设置了 locale 的 DateFormatter 会自动为用户所在地区选择正确的日期和时间格式。对于 dateStyle 和 timeStyle 的值 .short、.medium、.long、.full,格式化程序使用区域设置规则来组合日期组件。

swift
import Foundation

let date = Date()

// 使用不同区域设置格式化
let formatter = DateFormatter()
formatter.dateStyle = .medium

formatter.locale = Locale(identifier: "ru_RU")
print("俄语:\(formatter.string(from: date))")

formatter.locale = Locale(identifier: "en_US")
print("英语:\(formatter.string(from: date))")

formatter.locale = Locale(identifier: "ja_JP")
print("日语:\(formatter.string(from: date))")

// 使用区域设置格式化货币
let numFormatter = NumberFormatter()
numFormatter.numberStyle = .currency

numFormatter.locale = Locale(identifier: "de_DE")
print("德国货币:\(numFormatter.string(from: 1234.56) ?? "nil")")

numFormatter.locale = Locale(identifier: "en_US")
print("美国货币:\(numFormatter.string(from: 1234.56) ?? "nil")")

从服务器 API 解析日期应始终使用 Locale(identifier: "en_US_POSIX") 以获得固定格式。服务器通常以 ISO 8601 格式发送带有英文月份名称的日期,使用设备的当前区域设置可能会导致解析错误,如果用户位于非英语语言区域。en_US_POSIX 保证解析不依赖于设备设置。

swift
import Foundation

// 正确解析服务器日期
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("解析后的日期:\(parsed)")
}

// 本地化货币名称
let usLocale = Locale(identifier: "en_US")
let currencyName = usLocale.localizedString(
    forCurrencyCode: "RUB"
)
print("美国区域设置中的俄罗斯卢布:\(currencyName ?? "nil")")

附加功能: Locale 通过 localizedString(forRegionCode:)、localizedString(forLanguageCode:)、localizedString(forCurrencyCode:) 和 localizedString(forCalendarIdentifier:) 方法提供其组件的本地化描述。这些方法返回调用它们的区域设置的语言名称。例如,Locale(identifier: "ru_RU").localizedString(forCountryCode: "DE") 将返回"德国"。

使用 Locale 时的常见错误

在解析数字时忽略 Locale — 国际化应用程序中的严重错误。没有明确区域设置的 NumberFormatter 会使用设备的当前区域设置。如果俄罗斯的用户输入"1,5",NumberFormatter.number(from: "1,5") 会正确返回 1.5。但是,如果相同的代码在具有 en_US 区域设置的设备上运行,解析将返回 nil,因为对于 en_US,小数分隔符是点号。

缺少用于服务器日期的 en_US_POSIX 会导致难以发现的错误。使用 dateFormat 和 locale = Locale.current 的 DateFormatter 可能会在日期格式与美国不同的地区的用户中出现问题。例如,在德国,DateFormatter 可能期望"21.07.2024",而服务器发送"07/21/2024"。en_US_POSIX 保证了机器解析的固定格式,与用户地区无关。

比较日期字符串而不是使用 Date — 另一个常见错误。开发人员有时会比较来自不同区域设置的日期的字符串表示,得到不正确的结果。Locale 只改变显示,不改变日期的绝对值。始终比较 Date 对象,而不是它们的字符串表示。要比较日期组件,请使用明确指定了 locale 的 Calendar。

根据 WWDC 2023,大约 30% 的应用程序国际化问题与 Locale 配置错误有关。Apple 建议在使用服务器数据时始终为 DateFormatter 和 NumberFormatter 明确设置 locale,并且仅在 UI 中使用 Locale.current。这种简单的做法可以消除大多数与区域设置相关的错误。

常见问题

Foundation 中的 Locale 是什么?

Locale — 表示文化和语言格式化规则的 Foundation 类:日期、数字、货币和度量单位的格式。与 DateFormatter、NumberFormatter 和 Calendar 一起使用,实现本地化数据展示。

Locale 和 TimeZone 有什么区别?

Locale 确定显示格式(语言、地区约定),而 TimeZone 确定相对于 UTC 的绝对时间偏移。Locale 影响字符串表示,TimeZone 影响时间的数值。两者一起使用可以完成日期的完整格式化。

什么是 en_US_POSIX,它有什么用?

en_US_POSIX — 一种用于技术格式化的特殊区域设置,无论用户设置如何都能保证稳定的格式。对于解析服务器日期(ISO 8601)和使用格式必须可预测的 API 是强制性的。

如何获取所有可用区域设置的列表?

Locale.availableIdentifiers 返回包含所有支持的区域设置标识符的字符串数组。要按语言过滤,请使用带 hasPrefix 的 filter,要获取地区 — Locale(identifier:).regionCode。

Locale 如何影响 NumberFormatter?

NumberFormatter 使用 Locale 来确定小数分隔符(逗号或点号)、货币符号和分组分隔符。对于固定格式,将 locale 设置为 en_US_POSIX 或明确指定格式化程序的属性。

总结

  • Locale — iOS 和 macOS 中用于本地化日期、数字和货币格式的基础类
  • ru_RU 标识符 编码语言(ISO 639-1)和地区(ISO 3166-1)以精确调整格式
  • DateFormatter 和 NumberFormatter 自动使用 Locale.current 进行本地化输出
  • en_US_POSIX — 用于解析服务器日期和固定机器格式化的强制区域设置
  • Locale.current 跟踪设置更改,Locale(identifier:) 创建固定区域设置
  • 数字解析需要明确设置 locale — 小数分隔符取决于用户的地区
  • Calendar.current 继承设备的区域设置;对于可预测的计算,请明确设置 locale

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读