Несколько локализованных файлов .strings в комплекте приложений для iOS

У меня довольно сложный проект, состоящий из нескольких крупных локализованных подпроектов.

Большинство моих подпроектов локализованы с помощью одного файла Localizable.strings. Этот файл копируется в SubProjectName.bundle цель, которая используется вместе со статической библиотекой SubProjectName.a в основном проекте. Это прекрасно работает.

Однако один из моих подпроектов содержит много локализованных файлов .strings. Этот проект не может читать строки на любом языке, кроме английского, независимо от того, как настроено устройство (или симулятор).

Например, эта строка кода всегда возвращает строку на английском языке:

[[NSBundle myResourcesBundle] localizedStringForKey:@"MY_TEST_STRING" value:@"" table:@"MyTable"]

Где MyTable соответствует файлу MyTable.strings, локализованному на несколько языков. Когда я заглядываю в пакет .app, все локализации находятся внутри ресурса «MyBundle.bundle» в приложении.

Однако следующий код правильно находит переводы для данной строки во всех локализациях:

for (NSString *language in [[NSUserDefaults standardUserDefaults] objectForKey:@"AppleLanguages"])
{
    NSBundle *bundle = [NSBundle bundleWithPath:[[NSBundle myResourcesBundle] pathForResource:language ofType:@"lproj"]];
    NSLog(@"%@: %@", language, NSLocalizedStringFromTableInBundle(@"MY_TEST_STRING", @"MyTable", bundle, nil));
}

Поэтому, когда пакет представляет собой фактическую папку MyBundle.bundle/<LanguageCode>.lproj, поиск по строке работает. Но, очевидно, это противоречит цели автоматического поиска, предоставляемого iOS.

(Обратите внимание, что [NSBundle myResourcesBundle] выше - это просто статический удобный метод для получения моего пользовательского пакета для подпроекта).

--

Изменить: я экспериментировал с этим еще, и если я удалю папку en.lproj из пакета подпроекта, то она правильно использует языковой стандарт устройства или симулятора.

Например, у меня есть:

MyApp.app/
 |
  - MyResources.bundle/
      |
       - en.lproj/
      |
       - zh-Hans.lproj/

Когда я устанавливаю симулятор (или устройство) на китайский упрощенный , он ищет строки в en.lproj, даже если языковой стандарт - zh-Hans. Если я удалю папку en.lproj и перезапущу приложение, оно правильно использует локализацию zh-Hans.


person simeon    schedule 09.12.2012    source источник


Ответы (5)


Мне удалось воспроизвести и исправить проблему, хотя решение подразумевает наличие ошибки в NSBundle.

Я воспроизвел его со следующей структурой пакета:

MyApp.app/
 |
  - MyResources.bundle/
      |
       - en.lproj/
      |
       - fr.lproj/

и код:

  NSLog(@"A key: %@", NSLocalizedString(@"A key", nil));
  NSBundle *bundle = [NSBundle bundleWithPath: [[NSBundle mainBundle] pathForResource: @"MyResources" ofType: @"bundle"]];
  NSLog(@"Current locale: %@", [[NSLocale currentLocale] localeIdentifier]);
  NSLog(@"Bundle localizations: %@", [bundle localizations]);
  NSLog(@"Key from bundle: %@", [bundle localizedStringForKey: @"A key" value: @"Can't find it." table: nil]);
  NSLog(@"Key using bundle macro: %@", NSLocalizedStringFromTableInBundle(@"A key",
                                                                             nil,
                                                                             bundle,
                                                                             nil));

Если языковой стандарт установлен на fr_FR (т.е. французский), пакет выбрал строку из таблицы английских строк - даже строка «не могу найти» не отображается.

Не меняя код, я смог получить французскую строку, используя вместо этого следующую структуру пакета:

MyApp.app/
 |
  - MyResources.bundle/
      |
       - Resources/
          |
           - en.lproj/
          |
           - fr.lproj/

Похоже, что NSBundle по-прежнему ожидает старую структуру пакетов Mac OS X вместо того, что должна использовать iOS. Так что простое изменение структуры пакета должно решить проблему ...

person David Doyle    schedule 13.12.2012
comment
Ух ты - отличная детективная работа! Я пытаюсь реализовать ваш ответ. Как вы поддерживали структуру ресурсов / папок в пакете, сохраняя при этом локализацию проекта? Когда я добавляю свои .strings в папку Resources / и перетаскиваю их в Xcode в качестве ссылки на папку, я теряю возможность выбирать файлы .strings для локализации. - person simeon; 13.12.2012
comment
Скажу честно, обманул :). Я добавил пакет в проект как папку .bundle, которая работает должным образом, а затем реорганизовал папку пакета в Finder. Xcode не участвовал, за исключением добавления исходного пакета. - person David Doyle; 13.12.2012
comment
Спасибо, Дэвид. Я награжу вас наградой, поскольку вы, кажется, догадались, почему он это делает, хотя нет никакого способа исправить это. Я приму свой обходной путь как ответ, потому что это способ обойти проблему. - person simeon; 17.12.2012

Чтобы добавить к тому, что сказал Дэвид Дойл.

Убедитесь, что вы установили доступные языки в разделе информации вашего проекта как в пакете, так и в самом приложении. Так, например, если вы поддерживаете французский и английский языки в своем приложении, убедитесь, что и ваш пакет, и ваше приложение имеют французский и английский языки, определенные в доступных локализациях для ваших проектов.

person FBronner    schedule 04.01.2013
comment
Это должен быть принятый ответ. Если приложение не поддерживает языковой стандарт Fr, то этот языковой стандарт не загружается и для настраиваемого пакета, даже если в нем может быть каталог fr.lproj. Если приложение поддерживает Fr, то Fr загружается из настраиваемого пакета. Нет необходимости изменять внутреннюю структуру пакета, добавляя каталог «Ресурсы». - person Vladimir Grigorov; 10.12.2013
comment
Также хочу добавить, что если у вас есть несколько языков в конфигурации вашего проекта, но на самом деле никакие файлы не локализованы (рядом с нужным языком есть сообщение 0 Files Localized), вы не получите локализованные строки из вашего настраиваемого пакета (только что проверено мной). - person Accid Bright; 29.03.2018

Теперь у меня есть хакерское решение для этого, но я был бы признателен, если бы у кого-то был лучший ответ (или объяснение, почему это не работает).

Я расширил свою категорию NSBundle, включив в нее предпочтительный языковой ресурс:

Заголовок

@interface NSBundle (MyBundle)

+ (NSBundle*) myResourcesBundle;
+ (NSBundle*) myPreferredLanguageResourcesBundle;

@end

Выполнение

@implementation NSBundle (MyBundle)

+ (NSBundle*) myResourcesBundle
{
    static dispatch_once_t onceToken;
    static NSBundle *myLibraryResourcesBundle = nil;

    dispatch_once(&onceToken, ^
    {
        myLibraryResourcesBundle = [NSBundle bundleWithURL:[[NSBundle mainBundle] URLForResource:@"MyResources" withExtension:@"bundle"]];
    });

    return myLibraryResourcesBundle;
}

+ (NSBundle*) myPreferredLanguageResourcesBundle
{
    static dispatch_once_t onceToken;
    static NSBundle *myLanguageResourcesBundle = nil;

    dispatch_once(&onceToken, ^
                  {
                      NSString *language = [[[NSBundle myResourcesBundle] preferredLocalizations] firstObject];
                      myLanguageResourcesBundle = [NSBundle bundleWithPath:[[NSBundle myResourcesBundle] pathForResource:language ofType:@"lproj"]];

                      if( myLanguageResourcesBundle == nil )
                      {
                          myLanguageResourcesBundle = [NSBundle myResourcesBundle];
                      }                
                  });

    return myLanguageResourcesBundle;
}

@end

Затем у меня есть простой макрос для получения моих локализованных строк:

#define MyLocalizedDocumentation(key, comment, chapter) \
  NSLocalizedStringFromTableInBundle((key),(chapter),[NSBundle myPreferredLanguageResourcesBundle],(comment))

Это решение просто получает код предпочтительного языка из NSLocale, а затем проверяет, существует ли пакет для этого языка. Если нет, он возвращается к основному пакету ресурсов (возможно, ему следует выполнить итерацию по индексам NSLocale preferredLanguage, чтобы проверить, существует ли пакет? Кто-нибудь знает?)

person simeon    schedule 09.12.2012
comment
Этот ответ не допускает частичных локализаций (т.е. один переведенный файл .strings из десяти). - person simeon; 10.12.2012
comment
Это помогает, но почему не работает нормально? Вы нашли причину? - person some.birdie; 30.12.2013
comment
@Anastasia см. Ответ Дэвида Дойла выше. Похоже, это ошибка в NSBundle. - person simeon; 31.12.2013
comment
С 2015 года это больше не будет работать. Вы захотите изменить NSString *language = [NSLocale preferredLanguages][0]; на это: [[[NSBundle mainBundle] preferredLocalizations] firstObject]; - person Alex Zavatone; 14.10.2015
comment
@AlexZavatone есть идеи, почему он перестал работать? (Он не указан как устаревший). preferredLocalizations перечисляет локализации, содержащиеся в пакете, в порядке предпочтения на основе локали, а preferredLanguages перечисляет все языки в порядке предпочтения, независимо от того, что находится в пакете. Я все равно отредактировал свой ответ, так как preferredLocalizations лучше использовать, даже если preferredLanguages все еще работает. - person simeon; 15.10.2015
comment
Есть несколько причин, но Apple заявляет в заголовке NSLocale для использования mainBundle. Одна из БОЛЬШИХ проблем заключается в том, что если вы установите не английский язык и ваш регион, чтобы он не находился в США, NSLocale вернет что-то вроде этого: ar-IT, если вы использовали арабский язык в итальянском область. Вот комментарии из заголовка NSLocale: + (NSArray<NSString *> *)preferredLanguages NS_AVAILABLE(10_5, 2_0); // note that this list does not indicate what language the app is actually running in; the [NSBundle mainBundle] object determines that at launch and knows that information - person Alex Zavatone; 15.10.2015
comment
Решение просто потрясающее для приложений, которые ищут основной язык у разработчика (или пользователя динамически на экране), то есть принудительную локализацию. Но если приложение должно работать на основе настроек локали пользователя, то ему не удается найти правильный пакет, например: индийский английский - это EN_IN, а мы - EN_US .. dev, возможно, не добавил lproj для индийского английского, но имел бы lproj для en_us . Кроме того, средство форматирования даты может не искать формат из пакета, а ищет в настройках региона MM / dd / yyyy в США и dd / MM / yyyy в Индии. Но все же решение лучшее из приведенных .. Спасибо за это .. - person RamaKrishna Chunduri; 04.04.2016

Не совсем понимаю ваш вопрос, но я использую этот макрос для использования нескольких локализованных строковых файлов:

#define CustomLocalizedString(key, comment) \
  [[[NSBundle mainBundle] localizedStringForKey:(key) value:nil table:nil] isEqualToString:(key)] ? \
  [[NSBundle mainBundle] localizedStringForKey:(key) value:@"" table:@"MyTable"] : \
  [[NSBundle mainBundle] localizedStringForKey:(key) value:@"" table:nil]

или вы можете попробовать

[[NSBundle mainBundle] localizedStringForKey:(key) value:[[NSBundle mainBundle] localizedStringForKey:(key) value:@"" table:@"MyTable"] table:nil]

Это сначала проверит Localizable.strings, если ключ не существует, он вернет сам ключ, а затем проверит и использует MyTable.strings. Конечно, вам лучше называть свой ключ префиксом. например "KYName" = "Name";.

Если это то, что вы хотите, не стесняйтесь проверить ЭТО вопрос, который я задавал раньше. ;)

person Kjuly    schedule 12.12.2012
comment
Проблема возникает, когда я использую: [MyCustomBundle localizedStringForKey: (key) value: @ table: @MyCustomStringsFile] - я всегда получаю результат на английском языке (например, из папки en.lproj /). Даже если есть другие языки и устройство правильно настроено на этом языке. - person simeon; 12.12.2012
comment
Не уверен, что это решит проблему, изложенную выше, но рад, что нашел ее, так как это решает мою проблему! - person LoriHC; 09.05.2013
comment
@LoriHC рад это слышать :) - person Kjuly; 09.05.2013

У меня есть другое решение.

#define localizedString(key)    [NSLocalizedString(key, nil) isEqualToString:key] ? \
                            [[NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:@"en" ofType:@"lproj"]]  localizedStringForKey:key value:@"" table:nil] : \
                            NSLocalizedString(key, nil)

так, например, если мы собираемся найти ключ "title" = "Manager"; в Localizable.strings (fr), и если его нет, то результат для ключа "title" будет таким же, как и для ключа.

В этом случае мы можем найти ключ «title» в localizable.string (en), но если найдем в (fr), мы можем просто использовать его.

person Hwangho Kim    schedule 20.11.2015