Change the iOS simulator's current locale at runtime

 ̄綄美尐妖づ 提交于 2019-12-22 03:35:17

问题


While developing a set of date calculations and language rules for converting numeric values and dates to strings, I'm writing tests that assert the outcome of the string formatting method. An imaginary assertion for it might looks like this:

NSAssert([dateString isEqualToString:@"Three days, until 6:00 PM"], @"Date string should match expectation");

However, because the app is localized for several languages, and my fellow developers are also from and in different locales than I, it can happen that your device or simulator is set to a different locale than the one that the tests are being written for. In a scenario like this, the contents of the dateString might be something like:

@"Drie dagen, tot 18:00" // the assertion fails
@"Drei Tage, bis 18 Uhr" // the assertion also fails

This may or may not be the correct date notation for these locales, but the part that my question is about, is how to be able to run tests to a specific locale, when the underlying code makes use of Apple API like this:

[NSDateFormatter localizedStringFromDate:date 
                 dateStyle:NSDateFormatterNoStyle 
                 timeStyle:NSDateFormatterShortStyle];

I would love to cover at two or more languages in my assertions, with something like this:

[NSSomething actionToSetTheLocaleTo:@"en_US"];
dateString = ...; // the formatting
NSAssert([dateString isEqualToString:@"Three days, until 6:00 PM"], @"match en_US");

[NSSomething actionToSetTheLocaleTo:@"nl_NL"];
dateString = ...; // the formatting
NSAssert([dateString isEqualToString:@"Drie dagen, tot 18:00"], @"match nl_NL");

Who knows how to achieve this effect?

Notes:

  • Changing the preferred language does not cut it, it also needs to influence the NSDateFormatter and NSNumberFormatter behavior.
  • Because this is for unit testing purposes only, I'd be content with using private API. However, for the benefit of other people stumbling on this post, public API is preferred.
  • Passing a custom locale to each and every date or number formatting API might be a final consideration, but I'm posting this question hoping to avoid falling back to those extreme measures. If you however know this to be the only solution, please provide some reference and I'll waste no more time

Links on the topic:

  • Nice article by Ray Lillywhite on i18n and l10n
  • NSHipster article on NSLocale

回答1:


@Desmond pointed out a working solution. Until he places an answer in here to put this info in, let me summarize what I ended up doing with a bit of code.

The solution, turns out, is "as easy" as swizzling the methods that the class methods use internally:

beforeEach(^{
    [NSBundle ttt_overrideLanguage:@"nl"];
    [NSLocale ttt_overrideRuntimeLocale:[NSLocale localeWithLocaleIdentifier:@"nl_NL"]];
});

afterEach(^{
    [NSLocale ttt_resetRuntimeLocale];
    [NSBundle ttt_resetLanguage];
});

The ttt_... methods you see above use the categories on NSObject, NSLocale and NSBundle to check at runtime whether it should use the original methods or return something else. This method works flawlessly when writing your tests, and although it doesn't technically use any private API, I would strongly suggest only to use this in your test setup, not for anything you submit to the App Store for review.

In this gist you'll find the Objective-C categories I added to my app's test target to achieve the required behavior.




回答2:


Since this is in a unit test, have you tried setting the locale on the NSDateFormatter? You should not have to set the locale on the whole simulator, you can make it a parameter of your tests. Most cocoa methods that are dependent on locale can take it as a parameter or property.

Something like:

NSLocale *locale = [NSLocale localeWithLocaleIdentifier:@"en-US"];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setLocale:locale];
[formatter setDateStyle: NSDateFormatterNoStyle];
[formatter setTimeStyle: NSDateFormatterShortStyle];
[formatter setFormatterBehavior:NSDateFormatterBehavior10_4];
thing = [formatter stringForObjectValue:date];

The localizedStringFromDate:dateStyle:timeStyle: method you're using is documented here, and it's pretty explicit about everything but the locale. So you can do the same steps, but set the locale to something other than the system's current locale using the steps outlined above.




回答3:


The only way I see is what quellish has mentioned. However, you mentioned it exists in a lot of places.

Instead of rewriting all your current code, in your pch you could do something fancy like

#import "UnitTestDateFormatter.h"
#define NSDateFormatter UnitTestDateFormatter

And then simply create a subclass to handle it:

@implementation UnitTestDateFormatter

- (id) init
{
    self = [super init];

    if(self != nil)
    {
        [self setLocale:...];
    }

    return self;
}

@end

At least then your code can remain unchanged.



来源:https://stackoverflow.com/questions/19143808/change-the-ios-simulators-current-locale-at-runtime

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!