iOS11 中的 NSDateFormatter 和当前语言
NSDateFormatter and current language in iOS11
NSDateFormatter
的默认行为似乎已在 iOS11 中更改。此代码用于根据当前选择的 iPhone/iPad 语言在 iOS11:
之前工作并生成日期格式化程序
_dateFormatterInstance = [[NSDateFormatter alloc] init];
_dateFormatterInstance.timeZone = [NSTimeZone systemTimeZone];
看起来在 iOS11 中我们必须为其明确指定语言环境 属性:
_dateFormatterInstance = [[NSDateFormatter alloc] init];
_dateFormatterInstance.timeZone = [NSTimeZone systemTimeZone];
_dateFormatterInstance.locale = [NSLocale localeWithLocaleIdentifier:[[NSLocale preferredLanguages] firstObject]];
有人可以证实我的发现吗?
这不是 NSDateFormatter
的问题,而是 iOS 11 支持本地化的方式发生了变化。
在 iOS 11 下,[NSLocale currentLocale]
只有 return 种您的应用本地化支持的语言。如果您的应用仅支持英语(作为基本本地化),那么无论用户在设备上选择什么语言,currentLocale
将始终 return 英语。
在 iOS 10 及更早版本中,currentLocale
将直接代表用户选择的语言和地区,无论您的应用支持何种本地化。
类如NSDateFormatter
默认使用NSLocale currentLocale
。因此,无论您的应用通过其本地化实际支持哪种语言,类(如 NSDateFormatter
)都会以设备上设置的语言显示文本,即使它与您的应用使用的语言不同。
iOS 11 修复了这种不一致。虽然有人可能会争辩说这一变化破坏了许多只支持一种(或几种)语言的应用程序,但它实际上使应用程序更加一致。
为了使所有这些都清楚,请考虑一个示例。您创建了一个基本的英语本地化的简单测试应用程序。如果您 运行 您的应用 iOS 10 并且设备的语言设置为英语,您显然会看到英语文本,并且您会看到格式为英语的日期。如果您现在将设备的语言更改为法语并重新启动应用程序,用户现在会在应用程序中看到英文文本(因为这是其唯一的本地化版本),但日期现在显示为法语月份和星期名称。
现在 运行 iOS 11 下的相同应用程序。与 iOS 10 一样,如果设备的语言是英语,您会看到英语的所有内容。如果您随后将设备的语言和 运行 应用程序的语言更改为法语,iOS 11 会发现您的应用程序仅支持英语和 currentLocale
returns 英语,而不是法语。所以现在用户看到的是英文文本(由于应用程序的本地化),日期现在也仍然是英文。
是的,iOS11 中的默认行为完全按照@rmaddy 的描述进行了更改。
就我而言,我有一个基本开发语言设置为 英语 的项目,但是在 iOS11 上,当我将设备的语言更改为任何其他语言时(比如瑞典语)日期仍会显示为,例如 Monday 6 November
。发生这种情况是因为我的应用程序不支持任何本地化。
解决方案很简单:为了让应用程序以瑞典语显示日期,我只需要添加一个空 Strings.strings
文件,然后在项目设置中添加瑞典语本地化。尽管字符串文件是空的,但该应用随后已本地化为瑞典语,因此通过在“设置”中将语言更改为瑞典语,我们可以看到与 måndag 6 november
相同的日期,从而实现 [=28] 的预期用例=].
注意: 如果您执行类似的操作但它对您不起作用,请确保在项目设置中添加语言时转到 "Other" 和从那里选择一种语言(而不是只从第一级下拉列表中选择一种)。
在 iOS 11 中,这实际上更像是一个错误,而不是故意更改行为。如果您只设置了一种语言,则此行为不会出现,因为 Locale.current
总是 return是正确的语言和地区即使您的应用未本地化为该语言。
但是,如果您使用不止一种语言(例如法语和英语),那么在使用 Locale.current
时,iOS 11 似乎总是偏爱英语或您的应用程序中最接近支持的语言。
Locale.preferredLanguages
似乎 return 正确的语言区域信息,因此您可以改用它。
下面的示例显示了 Locale.current
和 Locale.preferredLanguages
的输出,显示了不一致之处。
这是从仅支持英语的应用生成的。在设备上,法语被设置为主要语言和地区,在第一个示例中英语(澳大利亚)被设置为次要语言。
(不正确)Locale.current
有多种语言 - 注意英语是什么语言,什么时候应该是法语,因此 fr_FR
- identifier : "en_FR"
- kind : "current"
(正确)Locale.preferredLanguages
多种语言
- 0 : "fr-FR"
- 1 : "en-AU"
(正确)Locale.current
法语是唯一的语言
- 0 : "fr-FR"
(正确)Locale.preferredLanguages
法语是唯一的语言
- identifier : "fr_FR"
- kind : "current"
iOS15,Swift5
感谢 rmaddy,他完美地解释了 为什么 和 如何 问题出现了.不过,我发现唯一真正有效的 解决方案 是在 Formatter 实现中更改 Locale 的实例,如下所示:
let formatter = DateFormatter()
// this helps
formatter.locale = Locale(identifier: Locale.preferredLanguages.first!)
// this is the problematic code that I deleted
formatter.locale = Locale.current
不幸的是,将本地化添加到应用程序并没有改变 DateFormatter(默认使用 Locale.current)使用的“英语”字样。所以我最终得到了上面的这个解决方案,手动设置了一个 Locale 实例,其中(用户)首选语言排名第一。
Apple 更改了区域设置的行为 API。从 iOS 11 开始,它的行为与 rmaddy 的回答描述的一样。如果您想实现 iOS 10 和更早版本的行为,我收到了 Apple 的以下指导:
There are a few ways that it can be addressed (in “most recommended”
to “least recommended” order):
- Add localizations for languages that your app should support.
- In your app’s Info.plist, replace CFBundleDevelopmentRegion with CFBundleLocalizations and populate it with one or more localizations
that your app is intended to be localized into.
- In your app’s Info.plist, set CFBundleAllowMixedLocalizations to YES. This will cause Locale.current to always use the locale most
preferred as specified in user preferences. Please note that if you
have other strings in your app, this may result in mixed localizations
of string content (e.g system framework strings in French but the rest
of the app in Spanish).
请注意,Locale.current
会考虑用户的设置,例如他们是否启用了 24 小时制。上面没有提到的一个选项是将日期格式化程序的语言环境设置为 Locale(identifier: Locale.preferredLanguages.first!)
但这将使用该语言环境的默认设置,忽略用户指定的任何设置,您可能不想这样做。
NSDateFormatter
的默认行为似乎已在 iOS11 中更改。此代码用于根据当前选择的 iPhone/iPad 语言在 iOS11:
_dateFormatterInstance = [[NSDateFormatter alloc] init];
_dateFormatterInstance.timeZone = [NSTimeZone systemTimeZone];
看起来在 iOS11 中我们必须为其明确指定语言环境 属性:
_dateFormatterInstance = [[NSDateFormatter alloc] init];
_dateFormatterInstance.timeZone = [NSTimeZone systemTimeZone];
_dateFormatterInstance.locale = [NSLocale localeWithLocaleIdentifier:[[NSLocale preferredLanguages] firstObject]];
有人可以证实我的发现吗?
这不是 NSDateFormatter
的问题,而是 iOS 11 支持本地化的方式发生了变化。
在 iOS 11 下,[NSLocale currentLocale]
只有 return 种您的应用本地化支持的语言。如果您的应用仅支持英语(作为基本本地化),那么无论用户在设备上选择什么语言,currentLocale
将始终 return 英语。
在 iOS 10 及更早版本中,currentLocale
将直接代表用户选择的语言和地区,无论您的应用支持何种本地化。
类如NSDateFormatter
默认使用NSLocale currentLocale
。因此,无论您的应用通过其本地化实际支持哪种语言,类(如 NSDateFormatter
)都会以设备上设置的语言显示文本,即使它与您的应用使用的语言不同。
iOS 11 修复了这种不一致。虽然有人可能会争辩说这一变化破坏了许多只支持一种(或几种)语言的应用程序,但它实际上使应用程序更加一致。
为了使所有这些都清楚,请考虑一个示例。您创建了一个基本的英语本地化的简单测试应用程序。如果您 运行 您的应用 iOS 10 并且设备的语言设置为英语,您显然会看到英语文本,并且您会看到格式为英语的日期。如果您现在将设备的语言更改为法语并重新启动应用程序,用户现在会在应用程序中看到英文文本(因为这是其唯一的本地化版本),但日期现在显示为法语月份和星期名称。
现在 运行 iOS 11 下的相同应用程序。与 iOS 10 一样,如果设备的语言是英语,您会看到英语的所有内容。如果您随后将设备的语言和 运行 应用程序的语言更改为法语,iOS 11 会发现您的应用程序仅支持英语和 currentLocale
returns 英语,而不是法语。所以现在用户看到的是英文文本(由于应用程序的本地化),日期现在也仍然是英文。
是的,iOS11 中的默认行为完全按照@rmaddy 的描述进行了更改。
就我而言,我有一个基本开发语言设置为 英语 的项目,但是在 iOS11 上,当我将设备的语言更改为任何其他语言时(比如瑞典语)日期仍会显示为,例如 Monday 6 November
。发生这种情况是因为我的应用程序不支持任何本地化。
解决方案很简单:为了让应用程序以瑞典语显示日期,我只需要添加一个空 Strings.strings
文件,然后在项目设置中添加瑞典语本地化。尽管字符串文件是空的,但该应用随后已本地化为瑞典语,因此通过在“设置”中将语言更改为瑞典语,我们可以看到与 måndag 6 november
相同的日期,从而实现 [=28] 的预期用例=].
注意: 如果您执行类似的操作但它对您不起作用,请确保在项目设置中添加语言时转到 "Other" 和从那里选择一种语言(而不是只从第一级下拉列表中选择一种)。
在 iOS 11 中,这实际上更像是一个错误,而不是故意更改行为。如果您只设置了一种语言,则此行为不会出现,因为 Locale.current
总是 return是正确的语言和地区即使您的应用未本地化为该语言。
但是,如果您使用不止一种语言(例如法语和英语),那么在使用 Locale.current
时,iOS 11 似乎总是偏爱英语或您的应用程序中最接近支持的语言。
Locale.preferredLanguages
似乎 return 正确的语言区域信息,因此您可以改用它。
下面的示例显示了 Locale.current
和 Locale.preferredLanguages
的输出,显示了不一致之处。
这是从仅支持英语的应用生成的。在设备上,法语被设置为主要语言和地区,在第一个示例中英语(澳大利亚)被设置为次要语言。
(不正确)Locale.current
有多种语言 - 注意英语是什么语言,什么时候应该是法语,因此 fr_FR
- identifier : "en_FR"
- kind : "current"
(正确)Locale.preferredLanguages
多种语言
- 0 : "fr-FR"
- 1 : "en-AU"
(正确)Locale.current
法语是唯一的语言
- 0 : "fr-FR"
(正确)Locale.preferredLanguages
法语是唯一的语言
- identifier : "fr_FR"
- kind : "current"
iOS15,Swift5
感谢 rmaddy,他完美地解释了 为什么 和 如何 问题出现了.不过,我发现唯一真正有效的 解决方案 是在 Formatter 实现中更改 Locale 的实例,如下所示:
let formatter = DateFormatter()
// this helps
formatter.locale = Locale(identifier: Locale.preferredLanguages.first!)
// this is the problematic code that I deleted
formatter.locale = Locale.current
不幸的是,将本地化添加到应用程序并没有改变 DateFormatter(默认使用 Locale.current)使用的“英语”字样。所以我最终得到了上面的这个解决方案,手动设置了一个 Locale 实例,其中(用户)首选语言排名第一。
Apple 更改了区域设置的行为 API。从 iOS 11 开始,它的行为与 rmaddy 的回答描述的一样。如果您想实现 iOS 10 和更早版本的行为,我收到了 Apple 的以下指导:
There are a few ways that it can be addressed (in “most recommended” to “least recommended” order):
- Add localizations for languages that your app should support.
- In your app’s Info.plist, replace CFBundleDevelopmentRegion with CFBundleLocalizations and populate it with one or more localizations that your app is intended to be localized into.
- In your app’s Info.plist, set CFBundleAllowMixedLocalizations to YES. This will cause Locale.current to always use the locale most preferred as specified in user preferences. Please note that if you have other strings in your app, this may result in mixed localizations of string content (e.g system framework strings in French but the rest of the app in Spanish).
请注意,Locale.current
会考虑用户的设置,例如他们是否启用了 24 小时制。上面没有提到的一个选项是将日期格式化程序的语言环境设置为 Locale(identifier: Locale.preferredLanguages.first!)
但这将使用该语言环境的默认设置,忽略用户指定的任何设置,您可能不想这样做。