为啥我的 iOS 应用程序仅在首次运行时正确检测到当前语言?
Posted
技术标签:
【中文标题】为啥我的 iOS 应用程序仅在首次运行时正确检测到当前语言?【英文标题】:Why does my iOS app only detect the current language properly on first run?为什么我的 iOS 应用程序仅在首次运行时正确检测到当前语言? 【发布时间】:2012-11-27 07:52:46 【问题描述】:我正在本地化我的 ios 应用程序,并且在模拟器中它每次都能以我选择的语言正确运行。
在我的 iPhone 5 上进行测试时,它只会在应用程序第一次运行时正确检测到语言。每隔一次我在设备上重新编译和运行我的应用程序时,它都会检测到“en”作为语言,即使我正在选择 Español(“es”)进行测试。
我使用以下方法检测语言:
[[[NSBundle mainBundle] preferredLocalizations] objectAtIndex:0]
我也用过:
[[NSLocale preferredLanguages] objectAtIndex:0]
同样的结果。
如果我在第一次运行后终止应用程序,然后在设备上重新启动它,它会继续正确检测语言。
但是,如果我在初次运行后终止应用程序,然后通过 Xcode 重新编译/重新启动,它将在加载时检测到“en”(英文)。
之后,除非我完全删除该应用程序并通过 Xcode 重新编译/重新安装/运行该应用程序,否则杀死并重新启动该应用程序会不断检测为英语。然后循环重复......随后的重建/重新启动而不首先从设备中删除应用程序会导致错误检测。
我设备上的所有其他应用程序始终以西班牙语显示。整个用户界面以西班牙语显示。
更新:我现在已经在运行 iOS 6 的 iPad(第 3 代)上进行了测试,并且遇到了相同的行为。
更新 2:
在 didFinishLaunchingWithOptions 中,我有这段代码来检测语言:(语言是 NSString*):
language = [[NSLocale preferredLanguages] objectAtIndex:0];
接下来是这个调试语句,比较我得到的值,以及稍微不同的检测方式,只是为了调试:
NSLog(@"Detected language: %@ / %@", language, [[[NSBundle mainBundle] preferredLocalizations] objectAtIndex:0]);
当应用程序在西班牙语模式下正常工作时,输出显示为“检测到的语言:es / es”,然后在不正常时显示为“检测到的语言:en / en”。仍然不知道为什么它有时决定加载为英文......
更新 4:感谢大家的回答,我尝试了各种建议。不幸的是,我无法授予 +100 赏金,因为似乎没有任何建议可以解决问题。如果有人最终找到适合我的解决方案,届时我将再奖励他们 +50 赏金。
更新 5:我已经从 Xcode 4.5 更新到 4.5.2,并且遇到了同样的问题。
更新 6:我现在从头开始创建了一个新的测试项目,它工作得非常好!显然,我的项目布局方式或其中一个数据文件中一定有问题。我想我的下一个旅程将是从头开始重新创建项目,一个一个地复制文件数据......
第 7 次更新(几个月后): 遗憾的是,在通过煞费苦心地重新创建我的项目暂时解决了这个问题(看似)之后,我再次面临这个问题。在第一次加载时,语言会正确呈现,但在后续加载时,它会恢复为英语。
已解决请参阅我的最终解决方案below。感谢大家的帮助。我可能会发放一些赏金,因为无论如何它都会浪费掉。
【问题讨论】:
我真的没有太多可以和你分享的东西,但是你可能玩过 -AppleLanguage "(English") 运行参数吗? (参见 dl.dropbox.com/u/15994997/Screenshots/2h.png ,位于管理方案部分)。我怀疑这是问题所在,但假设您的代码很好,这是唯一想到的事情。另外,我敢打赌你是这样做的,但是尝试在从语言环境获取语言后立即使用 NSLog 打印语言,并验证它不是你的代码的其余部分。 你在 iOS 5 中测试过这个吗?也许这是 iOS 6 的错误? @Estarriol 今天下午我会试一试您的 -AppleLanguage。我在获得值后立即使用 NSLog 打印出来,它在第一次工作时将其报告为“es”,然后在失败时将其报告为“en”。如果我退出应用程序,iOS 中的其他所有内容都会按预期继续使用西班牙语。 @Lefteris 我没有在 iOS 5 中测试过它,但是该应用程序启用了一些仅适用于 iOS 6 的功能,所以它会有点工作。看来,如果它是 iOS 6 中的一个错误,它会影响很多人。本地化应用程序肯定很流行? @MasonG.Zhwiti : 你把代码 [[[NSBundle mainBundle] preferredLocalizations] objectAtIndex:0] 放在哪里? 【参考方案1】:几个月后我终于解决了这个问题!感谢大家的帮助(我还通过开发渠道与一位 Apple 开发人员进行了交流)。
TL;DR:我不小心在设备之间同步语言首选项(以及许多其他意想不到的事情),使用我的应用程序的 iCloud 键值存储(通过 MKiCloudSync)!继续阅读...
我正在使用名为 MKiCloudSync 的第三方类,它有助于将 [NSUserDefaults standardUserDefaults]
同步到我的应用程序的 iCloud 键值存储。当我开始使用它时,我的意图是让它在后台处理一些用户收藏的同步。
但是,不了解standardUserDefaults
的工作原理,我没有意识到standardUserDefaults
中除了我自己的自定义应用设置之外还有很多其他内容!
所以发生的事情是这样的:
首次启动应用程序。新的standardUserDefaults
就位,并且根据当前设备选择,存储有序语言首选项列表的内部“AppleLanguages”键是正确的。
应用程序以指定语言正确显示。
在后台,MKiCloudSync 将所有standardUserDefaults
同步到 iCloud。相反,如果您在其他地方运行此应用程序,例如使用英语设备,该设备也会将其语言设置同步到 iCloud。所以现在这个当前正在运行的应用程序实际上已经覆盖了它的语言首选项。
BOOM ... 下次运行应用程序时,无论您在设备上选择了什么,它都是从 iCloud 拉下的任何内容,将用作默认语言!
我打算在下一次应用更新中解决这个问题:
使用forked version of MKiCloudSync,只允许同步列入白名单的键名。
添加将执行一次性清理的代码,首先清理我的应用程序的 iCloud 密钥库,然后 (based on this SO answer),调用此代码以重置用户默认值:
NSString *appDomain = [[NSBundle mainBundle] bundleIdentifier]; [[NSUserDefaults standardUserDefaults] removePersistentDomainForName:appDomain];
到目前为止,在我的测试中,这种解决了问题...不幸的是,用户必须重新启动应用程序才能启动语言修复。但是,我猜大多数用户没有遇到这个问题,因为他们不太可能使用具有不同默认语言的多个设备。
【讨论】:
您是否尝试清理您的构建文件夹? cmd+option+shift+k 这只是救了我的命。无论如何,这些是应该被忽略的键: [[NSMutableSet setWithObjects:@"AppleICUForce24HourTime", @"AppleITunesStoreItemKinds", @"AppleKeyboards", @"AppleKeyboardsExpanded", @"AppleLanguages", @"AppleLocale", @"NSInterfaceStyle" , @"NSLanguages", @"OpenUDID", @"StatsDone", @"UIDisableLegacyTextView", nil] 保留]; @AndreiRadulescu 您可能最好将要同步的密钥列入白名单,因为 Apple 将来可能会引入新密钥,这可能会给您带来一些其他意想不到的问题... @MasonG.Zhwiti 你说得对!你的答案是一般情况。问题是我为我的游戏保存了 100 多个键,其中大部分是动态生成的。我不想在这个过程中失去任何东西。干杯!【参考方案2】:在设置->通用->国际中,有一个选项可以设置位置语言等。如果您将其设置为您尝试测试的语言,如果您的代码正确,它将起作用。
【讨论】:
是的,这就是我一直在尝试测试的方式。这就是我选择要测试的语言的方式(在本例中为西班牙语)。正如我的问题中所述,这只适用于第一次。一旦我第二次运行我的代码,就好像它重置了语言,即使电话界面的其余部分很明显我仍然正确设置为西班牙语。 我会试一试并报告。看起来我在 Xcode 4.5 上,最新版本是 4.5.2。 好的,我更新了Xcode 4.5.2,我也遇到了同样的问题。 :-( Xcode 一定把你的项目塞满了,然后我就发生过很多次了【参考方案3】:我在 iPhone 5 上测试了您的步骤,没有出现任何问题。这让我认为这里还有其他东西在起作用:很可能有一些东西干扰了您读取语言环境值的方式。
我建议您采取的帮助您调试此问题的步骤是:
-
发布获取首选语言值的方法的完整代码。确保每次运行应用时都执行该方法。
确保您发布的代码包含您用于测试语言设置的 NSLog 指令的位置。
您是否在第一次运行后将首选语言存储在其他位置?
【讨论】:
即使我的代码中没有明确使用我检测到的值作为首选语言的内容也会恢复为英语。例如,我已经本地化了 Storyboard 文件和 Localizable.strings 文件,它们在初始加载时以西班牙语工作。然后当它切换到英语时,即使这些东西也恢复了。【参考方案4】:试试下面的代码:
LocalizationSystem.h===
#import <Foundation/Foundation.h>
#define AMLocalizedString(key, comment) \
[[LocalizationSystem sharedLocalSystem] localizedStringForKey:(key) value:(comment)]
#define LocalizationSetLanguage(language) \
[[LocalizationSystem sharedLocalSystem] setLanguage:(language)]
#define LocalizationGetLanguage \
[[LocalizationSystem sharedLocalSystem] getLanguage]
#define LocalizationReset \
[[LocalizationSystem sharedLocalSystem] resetLocalization]
@interface LocalizationSystem : NSObject
NSString *language;
+ (LocalizationSystem *)sharedLocalSystem;
//gets the string localized
- (NSString *)localizedStringForKey:(NSString *)key value:(NSString *)comment;
//sets the language
- (void) setLanguage:(NSString*) language;
//gets the current language
- (NSString*) getLanguage;
//resets this system.
- (void) resetLocalization;
@end
LocalizationSystem.m===
#import "LocalizationSystem.h"
@implementation LocalizationSystem
//Singleton instance
static LocalizationSystem *_sharedLocalSystem = nil;
//Current application bundle to get the languages.
static NSBundle *bundle = nil;
+ (LocalizationSystem *)sharedLocalSystem
@synchronized([LocalizationSystem class])
if (!_sharedLocalSystem)
[[self alloc] init];
return _sharedLocalSystem;
// to avoid compiler warning
return nil;
+(id)alloc
@synchronized([LocalizationSystem class])
NSAssert(_sharedLocalSystem == nil, @"Attempted to allocate a second instance of a singleton.");
_sharedLocalSystem = [super alloc];
return _sharedLocalSystem;
// to avoid compiler warning
return nil;
- (id)init
if ((self = [super init]))
//empty.
bundle = [NSBundle mainBundle];
return self;
// Gets the current localized string as in NSLocalizedString.
- (NSString *)localizedStringForKey:(NSString *)key value:(NSString *)comment
return [bundle localizedStringForKey:key value:comment table:nil];
// If this function is not called it will use the default OS language.
// If the language does not exists y returns the default OS language.
- (void) setLanguage:(NSString*) l
NSLog(@"preferredLang: %@", l);
NSString *path = [[ NSBundle mainBundle ] pathForResource:l ofType:@"lproj" ];
if (path == nil)
//in case the language does not exists
[self resetLocalization];
else
bundle = [[NSBundle bundleWithPath:path] retain];
[[NSUserDefaults standardUserDefaults] setObject: [NSArray arrayWithObjects:l, nil] forKey:@"AppleLanguages"];
// Just gets the current setted up language.
// returns "es","fr",...
//
// example call:
// NSString * currentL = LocalizationGetLanguage;
- (NSString*) getLanguage
NSArray* languages = [[NSUserDefaults standardUserDefaults] objectForKey:@"AppleLanguages"];
NSString *preferredLang = [languages objectAtIndex:0];
return preferredLang;
// Resets the localization system, so it uses the OS default language.
//
// example call:
// LocalizationReset;
- (void) resetLocalization
bundle = [NSBundle mainBundle];
@end
正如您提到的,此代码完美运行。 它对我有用,并且该游戏现已上线应用商店,如果您想检查(HueShapes)。
【讨论】:
感谢您的回复,但除非我遗漏了什么,否则您最终会致电[[NSUserDefaults standardUserDefaults] objectForKey:@"AppleLanguages"]
获取语言,这是我已经尝试过的几个电话之一,我仍然体验同样的问题...它第一次工作(返回“es”),然后在随后的运行中失败(返回“en”,即使我从未在电话上更改语言)。
您是在应用程序中还是从设备的设置中更改语言,如果是从应用程序,那么请您分享本地化代码。
来自设备的设置。【参考方案5】:
您是否偶然使用 NSUserDefaults 来保存与语言相关的内容?
查看您的模拟器应用程序目录 -> 库 -> 首选项 -> <YourAppBundleName>.plist
有关设置语言的 NSUserDefaults 方法的说明,请参阅:How to force NSLocalizedString to use a specific language。
也许你只是保存你的语言,因此检测只是返回保存的值。
【讨论】:
感谢您的回复。我没有使用 NSUserDefaults 来保存任何与语言相关的内容。以上是关于为啥我的 iOS 应用程序仅在首次运行时正确检测到当前语言?的主要内容,如果未能解决你的问题,请参考以下文章