Изменение текущей локали симулятора iOS во время выполнения
При разработке набора вычислений дат и языковых правил для преобразования числовых значений и дат в строки я пишу тесты, которые утверждают результат метода форматирования строк. Мнимое утверждение для него может выглядеть так:
NSAssert([dateString isEqualToString:@"Three days, until 6:00 PM"], @"Date string should match expectation");
Однако, поскольку приложение локализовано для нескольких языков, а мои коллеги-разработчики также находятся в разных локалях, а не в разных местах, может случиться, что ваше устройство или симулятор настроено на другой язык, чем тот, который тестирует написан для. В подобном сценарии содержимое dateString
может быть примерно таким:
@"Drie dagen, tot 18:00" // the assertion fails
@"Drei Tage, bis 18 Uhr" // the assertion also fails
Это может быть или не быть правильной датой даты для этих локалей, но часть, о которой идет речь, - это способ запуска тестов в определенную локаль, когда базовый код использует API Apple, как это
[NSDateFormatter localizedStringFromDate:date
dateStyle:NSDateFormatterNoStyle
timeStyle:NSDateFormatterShortStyle];
Я хотел бы прикрыть свои слова на двух или более языках, например:
[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");
Кто знает, как добиться этого эффекта?
Примечания:
- Изменение предпочтительного языка не сокращает его, оно также должно влиять на поведение NSDateFormatter и NSNumberFormatter.
- Поскольку это только для тестирования модулей, я бы хотел использовать частный API. Однако, в интересах других людей, спотыкающихся об этом посту, предпочтительным является публичный API.
- Передача пользовательской локали для каждого API-интерфейса даты или числа может быть окончательным, но я отправляю этот вопрос, надеясь, что избежать вернется к этим экстремальным мерам. Если вы, однако, знаете, что это единственное решение, предоставьте некоторую ссылку, и я больше не буду тратить время.
Ссылки по теме:
Ответы
Ответ 1
@Desmond указал на рабочее решение. Пока он не поставит здесь ответ, чтобы разместить эту информацию, позвольте мне подвести итог тому, что я закончил с небольшим количеством кода.
Решение, оказывается, "легко", как swizzling the methods, которое методы класса используют внутри:
beforeEach(^{
[NSBundle ttt_overrideLanguage:@"nl"];
[NSLocale ttt_overrideRuntimeLocale:[NSLocale localeWithLocaleIdentifier:@"nl_NL"]];
});
afterEach(^{
[NSLocale ttt_resetRuntimeLocale];
[NSBundle ttt_resetLanguage];
});
Методы ttt_...
, которые вы видите выше, используют категории NSObject, NSLocale и NSBundle для проверки во время выполнения, должны ли они использовать исходные методы или возвращать что-то еще. Этот метод работает безупречно, когда вы пишете свои тесты, и хотя он технически не использует какой-либо частный API, я бы настоятельно рекомендовал использовать его только в тестовой настройке, а не за все, что вы отправляете в App Store для просмотра.
В этом разделе вы найдете категории Objective-C, которые я добавил в приложение для тестирования, чтобы достичь требуемого поведения.
Ответ 2
Так как это в unit test, вы пробовали установить локаль на NSDateFormatter
? Вам не нужно устанавливать языковой стандарт на весь симулятор, вы можете сделать его параметром своих тестов. Большинство методов cocoa, зависящих от языка, могут принимать его как параметр или свойство.
Что-то вроде:
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];
ЛокализованныйStringFromDate: dateStyle: метод timeStyle: вы используете описанный здесь, и он довольно подробно описывает все, кроме локали. Таким образом, вы можете сделать те же шаги, но установите локаль в нечто иное, чем текущая локаль системы, используя шаги, описанные выше.
Ответ 3
Единственный способ, который я вижу, - это то, что упомянуто quellish. Однако вы упомянули, что он существует во многих местах.
Вместо того, чтобы переписывать весь ваш текущий код, в вашем pch вы могли бы сделать что-то вроде
#import "UnitTestDateFormatter.h"
#define NSDateFormatter UnitTestDateFormatter
И затем просто создайте подкласс для его обработки:
@implementation UnitTestDateFormatter
- (id) init
{
self = [super init];
if(self != nil)
{
[self setLocale:...];
}
return self;
}
@end
По крайней мере, ваш код может оставаться без изменений.