Как заставить NSLocalizedString использовать определенный язык


263

На iPhone NSLocalizedStringвозвращает строку на языке iPhone. Можно ли принудительно NSLocalizedStringиспользовать определенный язык, чтобы приложение было на другом языке, чем устройство?


Пожалуйста, обратитесь сюда: stackoverflow.com/a/48187049/6665075 Работает как шарм
Анкит Кумар Гупта

Ответы:


261

NSLocalizedString()(и их варианты) получить доступ к клавише «AppleLanguages», NSUserDefaultsчтобы определить настройки пользователя для предпочитаемых языков. Это возвращает массив языковых кодов, первый из которых задается пользователем для его телефона, а последующие используются в качестве запасных, если ресурс недоступен на предпочтительном языке. (на рабочем столе пользователь может указать несколько языков с пользовательским порядком в Системных настройках)

Вы можете переопределить глобальные настройки для своего собственного приложения, если хотите, используя setObject: forKey: метод, чтобы установить свой собственный список языков. Это будет иметь приоритет над глобально установленным значением и будет возвращено любому коду в вашем приложении, которое выполняет локализацию. Код для этого будет выглядеть примерно так:

[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:@"de", @"en", @"fr", nil] forKey:@"AppleLanguages"];
[[NSUserDefaults standardUserDefaults] synchronize]; //to make the change immediate

Это сделало бы немецкий язык предпочтительным языком для вашего приложения с английским и французским в качестве запасных. Вы можете захотеть вызвать это как можно раньше при запуске вашего приложения. Вы можете узнать больше о предпочтениях языка / локали здесь: Интернационализация Темы программирования: Получение текущего языка и локали


4
Это не работает для меня, он будет использовать язык по умолчанию, несмотря ни на что.
Quano

50
Denniss; Кажется, это будет работать лучше, если вы установите языковые предпочтения до запуска приложения. Я делаю это в функции main (), прежде чем вызывается UIApplicationMain (). Как только он действительно запустится, он не изменит используемый язык, а просто установит предпочтение на следующий раз.
2010 года

3
Вы должны установить язык перед тем, как инициализировать UIKit, и вы должны указать полный язык + языковой стандарт региона - посмотрите полный пример на blog.federicomestrone.com/2010/09/15/…
fedmest

18
установка AppleLanguages ​​изменит язык во время выполнения, если сделано в main () - true! но ... ВПЕРВЫЕ приложение установлено, инициализируются nsuserdefaults ПОСЛЕ вызова UIApplicationMain (), который будет игнорировать ранее установленные AppleLanguages ​​и потребует безобразного принудительного перезапуска приложения, чтобы увидеть нужный язык.
ДжонПейн

3
Это не работает с множественными элементами, определенными в Localizable.stringsdict. Любая идея, если есть возможное решение?
Михай Фрату

147

Недавно у меня возникла та же проблема, и я не хотел запускать и исправлять всю NSLocalizedString, а также заставлять приложение перезагружаться, чтобы новый язык работал. Я хотел, чтобы все работало как есть.

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

Заголовочный файл

@interface NSBundle (Language)
+(void)setLanguage:(NSString*)language;
@end

Реализация

#import <objc/runtime.h>

static const char _bundle=0;

@interface BundleEx : NSBundle
@end

@implementation BundleEx
-(NSString*)localizedStringForKey:(NSString *)key value:(NSString *)value table:(NSString *)tableName
{
    NSBundle* bundle=objc_getAssociatedObject(self, &_bundle);
    return bundle ? [bundle localizedStringForKey:key value:value table:tableName] : [super localizedStringForKey:key value:value table:tableName];
}
@end

@implementation NSBundle (Language)
+(void)setLanguage:(NSString*)language
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^
    {
        object_setClass([NSBundle mainBundle],[BundleEx class]);
    });
    objc_setAssociatedObject([NSBundle mainBundle], &_bundle, language ? [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:language ofType:@"lproj"]] : nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end

В общем, когда ваше приложение запускается и перед загрузкой вашего первого контроллера, просто позвоните:

[NSBundle setLanguage:@"en"];

Когда ваш пользователь меняет предпочитаемый язык на экране настроек, просто позвоните еще раз:

[NSBundle setLanguage:@"fr"];

Чтобы вернуться к системным настройкам по умолчанию, просто введите nil:

[NSBundle setLanguage:nil];

Наслаждаться...

Для тех, кому нужна версия Swift:

var bundleKey: UInt8 = 0

class AnyLanguageBundle: Bundle {

    override func localizedString(forKey key: String,
                                  value: String?,
                                  table tableName: String?) -> String {

        guard let path = objc_getAssociatedObject(self, &bundleKey) as? String,
              let bundle = Bundle(path: path) else {

            return super.localizedString(forKey: key, value: value, table: tableName)
            }

        return bundle.localizedString(forKey: key, value: value, table: tableName)
    }
}

extension Bundle {

    class func setLanguage(_ language: String) {

        defer {

            object_setClass(Bundle.main, AnyLanguageBundle.self)
        }

        objc_setAssociatedObject(Bundle.main, &bundleKey,    Bundle.main.path(forResource: language, ofType: "lproj"), .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
    }
}

7
Привет @ Wirsing, это прекрасно работает для меня до сих пор. Я даже загрузил одно приложение в магазин, и Apple не жалуется.
Гилад Новик

7
Это отличное решение, я также ссылаюсь на это здесь: medium.com/ios-apprentice/905e4052b9de
Джеймс Тан

1
Привет @JamesTang, Спасибо за ссылку и ваш пост - я нашел много полезной информации о локализации.
Гилад Новик,

3
@Gilad Ваш метод отлично работает для динамических строк (строк, которые я определил в localizable.strings), но что касается кнопок и надписей раскадровки, он работает только в основном методе. так что вы можете расширить свой ответ, чтобы включить локализацию элементов управления пользовательского интерфейса? я имею в виду, как обновить (не закрывая) раскадровку после того, как я вызову [NSBundle setLanguage: @ "??"] ;?
Джавад Аль Шейх

2
Что касается XIB / раскадровки: вам нужно убедиться, что вы вызываете этот метод ПЕРЕД тем, как вы загружаете представление. Что касается обновления представлений в реальном времени: в моем случае я просто перезагрузил контроллер представления (я поместил его в контроллер навигации и просто заменил контроллер. После перезагрузки он получил новые переведенные строки). Я работаю над ним для Cocoapod, и, возможно, я приведу пример там. Оставайтесь с нами ...
Гилад Новик

137

Обычно я делаю это таким образом, но вы ДОЛЖНЫ иметь все файлы локализации в вашем проекте.

@implementation Language

static NSBundle *bundle = nil;

+(void)initialize 
{
    NSUserDefaults* defs = [NSUserDefaults standardUserDefaults];
    NSArray* languages = [defs objectForKey:@"AppleLanguages"];
    NSString *current = [[languages objectAtIndex:0] retain];
    [self setLanguage:current];
}

/*
  example calls:
    [Language setLanguage:@"it"];
    [Language setLanguage:@"de"];
*/
+(void)setLanguage:(NSString *)l
{
    NSLog(@"preferredLang: %@", l);
    NSString *path = [[ NSBundle mainBundle ] pathForResource:l ofType:@"lproj" ];
    bundle = [[NSBundle bundleWithPath:path] retain];
}

+(NSString *)get:(NSString *)key alter:(NSString *)alternate 
{
    return [bundle localizedStringForKey:key value:alternate table:nil];
}

@end

1
+1. Это действительно хороший трюк, которого я больше нигде не видел. Создав «подгруппу» из одной из папок локализации, вы можете заставить работать строковый материал, если вы оберните NSLocalizedString чем-то, что здесь обходится.
Бен Зотто

4
Отлично Мауро. Я заметил, что он также может работать для файлов из вашего проекта. Если по какой-либо причине (как в моем случае) вам необходимо загрузить строковые файлы из сети и сохранить их в своем каталоге «Documents» (со структурой папок Documents / en.lproj / Localizable.strings, Documents / fr.lproj / Localizable.strings, ...). Вы можете даже сделать NSBundle. Просто используйте этот код для пути: NSString * path = [NSHomeDirectory () stringByAppendingPathComponent: [NSString stringWithFormat: @ "/ Documents /% @. Lproj", l, nil]];
Арно дель.

1
Сдается мне, что это лучший подход, наряду с вкладом Алессандро ниже. Это не так навязчиво и не требует перезагрузки.
PKCLsoft

1
Что я делаю не так? Я создал класс Language, унаследовавший NSObject, поместил это в реализацию, поместил имена методов в .h, затем вставил [Language setLanguage: @ "es"]; в моей кнопке «Изменить язык» выполняется код (вызывается метод кнопки и он переходит в класс Language), но ничего не происходит. Я тоже настроил свои localizable.strings (испанский). Но, похоже, это работает для многих других людей. Пожалуйста, кто-нибудь притупит меня.
SirRupertIII

1
@KKendall вы называете это так: [Language setLanguage: @ "es"]; NSString * stringToUse = [Язык get: @ "Ваш текст" изменить: ноль];
Микрася

41

Не используйте на iOS 9. Это возвращает ноль для всех строк, переданных через это.

Я нашел другое решение, которое позволяет обновлять языковые строки без перезагрузки приложения и совместимо с genstrings:

Поместите этот макрос в файл Prefix.pch:

#define currentLanguageBundle [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:[[NSLocale preferredLanguages] objectAtIndex:0] ofType:@"lproj"]]

и где бы вам ни понадобилось использовать локализованную строку:

NSLocalizedStringFromTableInBundle(@"GalleryTitleKey", nil, currentLanguageBundle, @"")

Для установки языка используйте:

[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObject:@"de"] forKey:@"AppleLanguages"];

Работает даже при последовательном переключении языков:

NSLog(@"test %@", NSLocalizedStringFromTableInBundle(@"NewKey", nil, currentLanguageBundle, @""));
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObject:@"fr"] forKey:@"AppleLanguages"];
NSLog(@"test %@", NSLocalizedStringFromTableInBundle(@"NewKey", nil, currentLanguageBundle, @""));
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObject:@"it"] forKey:@"AppleLanguages"];
NSLog(@"test %@", NSLocalizedStringFromTableInBundle(@"NewKey", nil, currentLanguageBundle, @""));
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObject:@"de"] forKey:@"AppleLanguages"];
NSLog(@"test %@", NSLocalizedStringFromTableInBundle(@"NewKey", nil, currentLanguageBundle, @""));

2
@ powerj1984: нет, он изменит только строки в ваших исходных файлах. Если вы хотите изменить языки XIB, вы должны перезагрузить XIB вручную, из набора выбранного вами языка.
gklka

@gklka Я выполнил шаги, данные Tudozier, но мои XIB не меняют язык. Как вы сказали, перезагрузите XIB вручную, что на самом деле означает перезагрузка XIB?
ДК Кумар

@DkKumar: Вы должны сделать что-то похожее на это: stackoverflow.com/questions/21304988/…
gklka

@gklka Ваш ответ, кажется, правильный, и я сделал те же шаги, что вы описали в своем ответе, но проблема в том, что если мы изменим путь к пакету, он найдет весь ресурс (т. е. файл .png, используемый в XIB) в один и тот же пакет, поэтому мы должны перезаписать папку с изображениями во всех пакетах, таких как en.lproj, es.lproj и т. д., поэтому это повлияет на увеличение размера IPA, так есть ли способ преодолеть эту проблему? как вы сказали в своем посте, позвольте мне примерить это и позволить вам больше об этом. Спасибо за помощь мне за то же самое
Dk Kumar

1
Это ужасно сломано в iOS 9, возвращая ноль для всех строк.
Алекс Заватоне

32

Как сказано ранее, просто сделайте:

[[NSUserDefaults standardUserDefaults] setObject: [NSArray arrayWithObjects:@"el", nil] forKey:@"AppleLanguages"];

Но чтобы избежать перезапуска приложения, поместите строку в метод main main.m, непосредственно перед UIApplicationMain(...).


2
Очень полезный ответ! PS Может показаться очевидным для новичков, но вы должны вставить эту строку после того, NSAutoreleasePool * pool ..как произойдет утечка нескольких автоматически выпущенных объектов.
pt2ph8

1
ВАЖНО: Это не сработает, если вы позвоните [[NSBundle mainBundle] URLForResource:withExtension:]раньше.
Коф

3
А что если использовать Swift, то нет main.m?
тестирование turing

@turingtested Вы пробовали на Swift и раскадровке, это работает?
iDevAmit

Привет, Как локализовать динамический текст, поступающий с сервера, в моем приложении каждый текст должен отображаться на упрощенном китайском языке, но как отобразить текст на китайском, который идет на английском. Помоги мне.
Махеш Нарла

31

Хитрость в использовании определенного языка, выбрав его в приложении, состоит в том, чтобы заставить NSLocalizedString использовать определенный пакет в зависимости от выбранного языка,

вот пост, который я написал для этой локализации продвижения обучения в приложениях ios

и вот код примера локализации приложения в ios-приложениях


Он работает и на iOS7, мне нужно переключиться с решения Брайана на это, так как кажется, что iOS снова переопределяет опцию языков, поэтому я застрял с первоначальной настройкой языка. Ваше решение работает как шарм!
haxpor

14

Что вы думаете об этом решении для Swift 3?

extension String {

    func localized(forLanguage language: String = Locale.preferredLanguages.first!.components(separatedBy: "-").first!) -> String {

        guard let path = Bundle.main.path(forResource: language == "en" ? "Base" : language, ofType: "lproj") else {

            let basePath = Bundle.main.path(forResource: "Base", ofType: "lproj")!

            return Bundle(path: basePath)!.localizedString(forKey: self, value: "", table: nil)
        }

        return Bundle(path: path)!.localizedString(forKey: self, value: "", table: nil)
    }
}

Простое использование:

"report".localized(forLanguage: "pl") //forced language
"report".localized() //default language selected by user in settings, in case when your app doesnt support selected lanaguage, the default one is selected, here is an english.

Привет, Как локализовать динамический текст, поступающий с сервера, в моем приложении каждый текст должен отображаться на упрощенном китайском языке, но как отобразить текст на китайском, который идет на английском. Помоги мне.
Махеш Нарла

Это блестящий ответ;)
Bartłomiej Semańczyk

отличный ответ. Помогите мне
Дилип Тивари

13

Как упоминает Брайан Вебстер, язык должен быть установлен «как можно раньше при запуске вашего приложения». Я думал applicationDidFinishLaunching:оAppDelegate это подходящее место для этого, так как именно там я делаю все остальные инициализации.

Но, как отмечает Уильям Деннисс, это, похоже, дает эффект только после перезапуска приложения, что бесполезно.

Кажется, это работает нормально, если я помещу код в основную функцию, хотя:

int main(int argc, char *argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    // Force language to Swedish.
    [[NSUserDefaults standardUserDefaults]
     setObject:[NSArray arrayWithObject:@"sv"]
     forKey:@"AppleLanguages"];

    int retVal = UIApplicationMain(argc, argv, nil, nil);
    [pool release];
    return retVal;
}

Буду признателен за любые комментарии по этому вопросу.


в моей реализации это настраиваемая пользователем вещь, поэтому я просто выскакиваю диалоговое окно, сообщающее им, что их нужно будет перезапустить :)
William Denniss

Это работает, почти для всех - за исключением локализованного изображения Default.png.
Лукаш

2
Если вы позволяете пользователям устанавливать язык, а затем говорите им перезапустить приложение, просто помните, что NSUserDefaults могут не сохраняться, если они перезапускаются слишком быстро. Большинство пользователей не такие быстрые, но когда вы тестируете приложение, вы можете быть слишком быстрыми и в результате увидеть противоречивое поведение. Мне потребовалось пару часов, чтобы понять, почему мой переключатель языка иногда работает, а иногда нет!
arlomedia

3
Это не проблема, просто используйте [[NSUserDefaults standardUserDefaults] synchronize];после звонкаsetObject:forKey:
Бен Барон

11

Мне больше всего нравится метод Мауро Делрио. Я также добавил следующее в мой Project_Prefix.pch

#import "Language.h"    
#define MyLocalizedString(key, alt) [Language get:key alter:alt]

Поэтому, если вы когда-нибудь захотите использовать стандартный метод (который использует NSLocalizedString), вы можете быстро заменить синтаксис во всех файлах.


1
Мне также нравится его метод, и я добавляю метод для загрузки изображений png: + (UIImage ) imageNamed: (NSString *) imageFileName {NSString fullPath = [bundle pathForResource: imageFileName ofType: @ "png"]; UIImage * imageObj = [[UIImage alloc] initWithContentsOfFile: fullPath]; возвращение [imageObj autorelease]; }
Jagie

11

NSLocalizedString()читает значение для ключа AppleLanguagesиз стандартного пользовательского значения по умолчанию ( [NSUserDefaults standardUserDefaults]). Это значение используется для выбора подходящей локализации среди всех существующих локализаций во время выполнения. Когда Apple создает пользовательский словарь по умолчанию при запуске приложения, они ищут ключ предпочтительного языка (ов) в системных настройках и копируют значение оттуда. Это также объясняет, например, почему изменение языковых настроек в OS X не влияет на запущенные приложения, только на приложения, запущенные впоследствии. После копирования значение не обновляется только из-за изменения настроек. Вот почему iOS перезапускает все приложения, если вы меняете язык.

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

Поэтому, если вы хотите изменить язык только для тестирования, вы, вероятно, не хотите изменять свой код (если позже вы забудете удалить этот код ...), вместо этого попросите Xcode запустить ваше приложение с параметрами командной строки ( например, используйте испанскую локализацию):

введите описание изображения здесь

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


Работает только 1 раз, после чего снова загружается в преф. язык ..
Акс

@Aks работает каждый раз для меня. Убедитесь, что вы запускаете правильную схему, убедитесь, что вы запускаете из Xcode (перезапуски, разветвления и т. Д. Не получат аргумент) и убедитесь, что вы не переопределяете язык, Optionsкак в более новых версиях Xcode, которые Apple предлагает также.
Меки

Спасибо за ваш ответ @Mecki .. Для меня это не работает в xcode, но когда я упоминаю в коде, это работает .. Но все же, если я установил свое устройство с некоторыми другими предпочтительными языками, он должен перезапустить приложение, чтобы загрузить в мой любимый язык, который я передаю в аргументах ....
Акс

@Aks При использовании этого на устройстве, оно будет работать только при запуске непосредственно из XCode, оно не будет работать при повторном запуске приложения, нажав его значок на каком-либо устройстве, и также не будет работать, когда приложение убито (например, потому что он перешел в фоновый режим) и перезапускается системой (поскольку этот перезапуск не является перезапуском, выполненным XCode) - поэтому переключение на другое приложение и обратно может не сработать, если iOS должна убить ваше приложение в промежутке (например, потому что оно нужна память).
Меки

10

Я придумал решение, которое позволяет вам использовать NSLocalizedString. Я создаю категорию NSBundleвызова NSBundle+RunTimeLanguage. Интерфейс такой.

// NSBundle+RunTimeLanguage.h
#import <Foundation/Foundation.h>
@interface NSBundle (RunTimeLanguage)
#define NSLocalizedString(key, comment) [[NSBundle mainBundle] runTimeLocalizedStringForKey:(key) value:@"" table:nil]
- (NSString *)runTimeLocalizedStringForKey:(NSString *)key value:(NSString *)value table:(NSString *)tableName;
@end

Реализация такая.

// NSBundle+RunTimeLanguage.m
#import "NSBundle+RunTimeLanguage.h"
#import "AppDelegate.h"

@implementation NSBundle (RunTimeLanguage)

- (NSString *)runTimeLocalizedStringForKey:(NSString *)key value:(NSString *)value table:(NSString *)tableName
{
    AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
    NSString *path= [[NSBundle mainBundle] pathForResource:[appDelegate languageCode] ofType:@"lproj"];
    NSBundle *languageBundle = [NSBundle bundleWithPath:path];
    NSString *localizedString=[languageBundle localizedStringForKey:key value:key table:nil];
    return localizedString;
}
@end

Чем просто добавить импорт NSBundle+RunTimeLanguage.hв файлы, которые используют NSLocalizedString.

Как вы можете видеть, я храню свой languageCode в свойстве AppDelegate. Это может быть сохранено где угодно.

Единственное, что мне не нравится в этом, - это предупреждение о том, что NSLocalizedStringМарко переопределен. Возможно, кто-то может помочь мне исправить эту часть.


1
Добавьте #undef NSLocalizedStringнезадолго до #defineотключения предупреждение
бериллий

Это работает на локализацию для файла XIB? У меня есть файл .xib и .strings для перевода имени пользовательского интерфейса на определенный язык. Я пытался, и это не работает с XIB, но я не уверен, правильно ли я все сделал
Kong Hantrakool

Конг извините за задержку ответа. Это работает везде, где вы используете NSLocalizedString. Так что он не будет работать напрямую в XIB. Вам понадобится IBOutlets для строк в XIB, а затем придется программно устанавливать значения строк в коде.
qman64

Этот подход работал для меня, и я смог менять языки на лету. Обратите внимание, что языковые коды, такие как «es», fr »и« en », работали нормально. Но« zh »(для упрощенного китайского) не удался и вернул мне нулевые строки. Я выполнил глобальный поиск в своей системе« es.lproj », чтобы Посмотрите, где находятся файлы, к которым я обращался, и я обнаружил, что "zh.lproj" на самом деле является "zh-Hans.lproj".
Gallymon

9

Swift версия:

NSUserDefaults.standardUserDefaults().setObject(["fr"], forKey: "AppleLanguages")
NSUserDefaults.standardUserDefaults().synchronize()

8

В двух словах :

Локализуйте свое приложение

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

Переопределить NSLocalizedString

В вашем коде вместо использования NSLocalizedString(key, comment)используйте макрос, MYLocalizedString(key, comment)определенный следующим образом: #define MYLocalizedString(key, comment) [[MYLocalizationSystem sharedInstance] localizedStringForKey:(key) value:(comment)];

Этот MYLocalizationSystemсинглтон будет:

  • Установите язык, установив правильный локализованный пользователь NSBundle запрашивает
  • Возвращает локализованную строку NSString в соответствии с этим ранее установленным языком

Установить язык пользователя

Когда пользователь изменил язык приложения на французский, звоните [[MYLocalizationSystem sharedInstance] setLanguage:@"fr"];

- (void)setLanguage:(NSString *)lang
{
    NSString *path = [[NSBundle mainBundle] pathForResource:lang ofType:@"lproj"];
    if (!path)
    {
        _bundle = [NSBundle mainBundle];
        NSLog(@"Warning: No lproj for %@, system default set instead !", lang);
        return;
    }

    _bundle = [NSBundle bundleWithPath:path];
}

В этом примере этот метод устанавливает для локализованного пакета значение fr.lproj.

Вернуть локализованную строку

Установив локализованный пакет, вы сможете получить правильную локализованную строку от него с помощью этого метода:

- (NSString *)localizedStringForKey:(NSString *)key value:(NSString *)value
{
    // bundle was initialized with [NSBundle mainBundle] as default and modified in setLanguage method
    return [self.bundle localizedStringForKey:key value:value table:nil];
}

Надеюсь, что это поможет вам.

Вы найдете более подробную информацию в этой статье от NSWinery.io


Мы просим вас не просто ссылаться на решение в своем ответе; ссылка может перестать работать когда-нибудь. Хотя вам не нужно удалять ссылку из своего ответа, мы просим вас отредактировать свой ответ, чтобы включить краткое изложение соответствующего раздела связанной статьи.
Забывчивый мудрец

7

Свифт 3 расширения:

extension Locale {
    static var preferredLanguage: String {
        get {
            return self.preferredLanguages.first ?? "en"
        }
        set {
            UserDefaults.standard.set([newValue], forKey: "AppleLanguages")
            UserDefaults.standard.synchronize()
        }
    }
}

extension String {
    var localized: String {

    var result: String

    let languageCode = Locale.preferredLanguage //en-US

    var path = Bundle.main.path(forResource: languageCode, ofType: "lproj")

    if path == nil, let hyphenRange = languageCode.range(of: "-") {
        let languageCodeShort = languageCode.substring(to: hyphenRange.lowerBound) // en
        path = Bundle.main.path(forResource: languageCodeShort, ofType: "lproj")
    }

    if let path = path, let locBundle = Bundle(path: path) {
        result = locBundle.localizedString(forKey: self, value: nil, table: nil)
    } else {
        result = NSLocalizedString(self, comment: "")
    }
        return result
    }
}

Использование:

Locale.preferredLanguage = "uk"

label.text = "localizedKey".localized

Спасибо. Работает хорошо.
Крунал Нагвадия

5

В файле .pch определить:

#define currentLanguageBundle [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:[[NSLocale preferredLanguages] objectAtIndex:0] ofType:@"lproj"]]


#define NSLocalizedString(str,nil) NSLocalizedStringFromTableInBundle(str, nil, currentLanguageBundle, @"")

Как мне избавиться от предупреждения «ovverride ...»?
Идан Моше

1
Простое решение делает все, что мне нужно. Был в состоянии использовать это в качестве руководства для решения моей проблемы. Если pathForResource возвращает nil, потому что у меня нет файла локализации, тогда я использую pathForResource: @ "Base", так как ничего другого, что я пробовал, извлекает строки из базовой локализации. Спасибо.
GRW

Это хакерство (как и все остальные ответы), и его было проще всего реализовать в модульном тесте, который я писал.
Макс

4

Возможно, вам следует дополнить это (в файле .pch после #import):

extern NSBundle* bundle; // Declared on Language.m

#ifdef NSLocalizedString
    #undef NSLocalizedString
    // Delete this line to avoid warning
    #warning "Undefining NSLocalizedString"
#endif

#define NSLocalizedString(key, comment) \
    [bundle localizedStringForKey:(key) value:@"" table:nil]

/Users/pwang/Desktop/Myshinesvn/Myshine/viewController/OrientationReadyPagesView.m:193:31: Использование необъявленного идентификатора «bundle»
pengwang

4

Решение Swift 3:

let languages = ["bs", "zh-Hant", "en", "fi", "ko", "lv", "ms", "pl", "pt-BR", "ru", "sr-Latn", "sk", "es", "tr"]
UserDefaults.standard.set([languages[0]], forKey: "AppleLanguages")

Дали несколько примеров языковых кодов, которые можно использовать. Надеюсь это поможет


3

Вы можете создать вложенный комплект с набором локализованных строк, с которыми вы хотите сделать это, и затем использовать их NSLocalizedStringFromTableInBundle()для загрузки. (Я предполагаю, что это контент, отдельный от обычной локализации пользовательского интерфейса, которую вы можете делать в приложении.)


3

для моего случая у меня есть два локализованных файла, ja и en

и я хотел бы заставить его en, если предпочтительный язык в системе ни en или ja

я собираюсь редактировать файл main.m

я проверю, является ли первый предпочтительный en или ja, если нет, то я изменю второй предпочтительный язык на en.

int main(int argc, char *argv[])
{

    [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"AppleLanguages"];
    [[NSUserDefaults standardUserDefaults] synchronize];

    NSString *lang = [[NSLocale preferredLanguages] objectAtIndex:0];

    if (![lang isEqualToString:@"en"]  &&  ![lang isEqualToString:@"ja"]){

        NSMutableArray *array = [[NSMutableArray alloc] initWithArray:[NSLocale preferredLanguages]];
        [array replaceObjectAtIndex:1 withObject:@"en"];

        [[NSUserDefaults standardUserDefaults] setObject:array forKey:@"AppleLanguages"];
        [[NSUserDefaults standardUserDefaults] synchronize];


    }

    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));


    }


}

Спасибо @ chings228, это полезно для меня, но это не меняет образ запуска по умолчанию (заставка) для приложения. У меня есть разные заставки для каждого языка. ты знаешь как подать заявку на это ??
JERC

Я думаю, что всплеск запускается до запуска основной программы, поэтому я не знаю, как переопределить его, может быть, plist может помочь, но он может сработать в следующий раз, когда приложение откроется
chings228

2

Вы можете сделать что-то вроде этого:

NSString *bundlePath = [[NSBundle mainBundle] pathForResource:@"Localizable" ofType:@"strings" inDirectory:nil forLocalization:@"es"];


NSBundle *spanishBundle = [[NSBundle alloc] initWithPath:[bundlePath stringByDeletingLastPathComponent]];

NSLocalizedStringFromTableInBundle(@"House", nil, spanishBundle, nil):

2

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

Вместо макроса используйте класс для доступа к предпочтительному языку, чтобы проверить наличие кода определенного языка.

Ниже приведен класс, используемый для получения текущего языкового пакета, который работает для iOS 9:

@implementation OSLocalization

+ (NSBundle *)currentLanguageBundle
{
    // Default language incase an unsupported language is found
    NSString *language = @"en";

    if ([NSLocale preferredLanguages].count) {
        // Check first object to be of type "en","es" etc
        // Codes seen by my eyes: "en-US","en","es-US","es" etc

        NSString *letterCode = [[NSLocale preferredLanguages] objectAtIndex:0];

        if ([letterCode rangeOfString:@"en"].location != NSNotFound) {
            // English
            language = @"en";
        } else if ([letterCode rangeOfString:@"es"].location != NSNotFound) {
            // Spanish
            language = @"es";
        } else if ([letterCode rangeOfString:@"fr"].location != NSNotFound) {
            // French
            language = @"fr";
        } // Add more if needed
    }

    return [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:language ofType:@"lproj"]];
}

/// Check if preferred language is English
+ (BOOL)isCurrentLanguageEnglish
{
    if (![NSLocale preferredLanguages].count) {
        // Just incase check for no items in array
        return YES;
    }

    if ([[[NSLocale preferredLanguages] objectAtIndex:0] rangeOfString:@"en"].location == NSNotFound) {
        // No letter code for english found
        return NO;
    } else {
        // Tis English
        return YES;
    }
}

/*  Swap language between English & Spanish
 *  Could send a string argument to directly pass the new language
 */
+ (void)changeCurrentLanguage
{
    if ([self isCurrentLanguageEnglish]) {
        [[NSUserDefaults standardUserDefaults] setObject:@[@"es"] forKey:@"AppleLanguages"];
    } else {
        [[NSUserDefaults standardUserDefaults] setObject:@[@"en"] forKey:@"AppleLanguages"];
    }
}
@end

Используйте приведенный выше класс для ссылки на строковый файл / image / video / etc:

// Access a localized image
[[OSLocalization currentLanguageBundle] pathForResource:@"my_image_name.png" ofType:nil]
// Access  a localized string from Localizable.strings file
NSLocalizedStringFromTableInBundle(@"StringKey", nil, [OSLocalization currentLanguageBundle], @"comment")

Измените язык в строке, как показано ниже, или обновите метод «changeCurrentLanguage» в вышеприведенном классе, чтобы получить строковый параметр, ссылающийся на новый язык.

[[NSUserDefaults standardUserDefaults] setObject:@[@"es"] forKey:@"AppleLanguages"];

2

В Swift 4 я решил это без необходимости перезапуска или использования библиотек.

Перепробовав много вариантов, я нашел эту функцию, где вы передаете stringToLocalize (Localizable.String, файл строк), который вы хотите перевести, и язык, на который вы хотите перевести его, и что он возвращает, это значение для та строка, которая у вас есть в файле строк:

    func localizeString (stringToLocalize: String, language: String) -> String
    {
        let path = Bundle.main.path (forResource: language, ofType: "lproj")
        let languageBundle = Bundle (path: path!)
        return languageBundle! .localizedString (forKey: stringToLocalize, value: "", table: nil)
    }

Учитывая эту функцию, я создал эту функцию в файле Swift:

struct CustomLanguage {

    func createBundlePath () -> Bundle {
        let selectedLanguage = //recover the language chosen by the user (in my case, from UserDefaults)
        let path = Bundle.main.path(forResource: selectedLanguage, ofType: "lproj")
        return Bundle(path: path!)!
    }
}

Для доступа ко всему приложению и в каждой строке остальных ViewControllers вместо:

NSLocalizedString ("StringToLocalize", comment: ")

Я заменил его

let customLang = CustomLanguage() //declare at top
let bundleLanguage = customLang.createBundle()

NSLocalizedString("StringToLocalize", tableName: nil, bundle: bundleLanguage, value: "", comment: “”) //use in each String

Я не знаю, является ли это лучшим способом, но я нашел это очень простым, и это работает для меня, я надеюсь, что это поможет вам!


1

Эта функция попытается получить локализованную строку для текущего языка и, если она не найдена, она получит ее с использованием английского языка.

- (NSString*)L:(NSString*)key
{
    static NSString* valueNotFound = @"VALUE_NOT_FOUND";
    static NSBundle* enBundle = nil;

    NSString* pl = [NSLocale preferredLanguages][0];
    NSString* bp = [[NSBundle mainBundle] pathForResource:pl ofType:@"lproj"];
    NSBundle* b = [NSBundle bundleWithPath:bp];

    NSString* s = [b localizedStringForKey:key value:valueNotFound table:nil];
    if ( [s isEqualToString:valueNotFound] ) {
        if ( !enBundle ) {
            bp = [[NSBundle mainBundle] pathForResource:@"en" ofType:@"lproj"];
            enBundle = [NSBundle bundleWithPath:bp];
        }
        s = [enBundle localizedStringForKey:key value:key table:nil];
    }

    return s;
}

1

Я хотел добавить поддержку языка, который официально не поддерживается iOS (не указан в разделе «Язык» в настройках системы). Следуя Руководству Apple по интернационализации и нескольким советам Брайана Вебстера и Джона, я разработал этот фрагмент кода (поместите его в main.m):

int main(int argc, char * argv[]) {
    @autoreleasepool {
        // Grab regional settings locale, for Slovenian this is either sl_SI or en_SI
        NSLocale *locale = [NSLocale currentLocale];
        NSString *ll = [locale localeIdentifier]; // sl_SI

        // Grab the first part of language identifier
        NSArray *comp = [ll componentsSeparatedByString:@"_"];
        NSString *ll1 = @"en";
        if (comp.count > 0) {
            ll1 = comp[0]; // sl, en, ...
        }
        // Check if we already saved language (user can manually change it inside app for example)
        if (![[NSUserDefaults standardUserDefaults] objectForKey:@"SelectedLanguage"]) {
            //   Slovenian (Slovenia),            Slovenia
            if ([ll isEqualToString:@"sl_SI"] || [ll isEqualToString:@"en_SI"]) {
                ll1 = @"sl-SI"; // This is the part of localized path for Slovenian language that Xcode generates
            }
            // Add more unsupported languages here...

            [[NSUserDefaults standardUserDefaults] setObject:ll1 forKey:@"SelectedLanguage"]; // Save language
        }
        else {
            // Restore language as we have previously saved it
            ll1 = [[NSUserDefaults standardUserDefaults] objectForKey:@"SelectedLanguage"];
        }
        // Overwrite NSLocalizedString and StoryBoard language preference
        [[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:ll1, @"en", @"fr", nil] forKey:@"AppleLanguages"];
        // Make sure settings are stored to disk
        [[NSUserDefaults standardUserDefaults] synchronize];

        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

Это хорошо работает как для Storyboard, так и для кода NSLocalizedString. Код предполагает, что пользователь будет иметь возможность вручную изменить язык внутри приложения позже.

Конечно, не забудьте добавить правильные переводы Storyboard и Localizable.strings (см. Ссылку на страницу Apple выше, чтобы узнать, как это сделать).


Я вижу, что вы также сталкиваетесь с проблемами со словенским языком :) Чтобы улучшить этот ответ для языков, не поддерживаемых iOS, вы выяснили, можем ли мы установить настраиваемые локализуемые строки, которые будут использоваться для системных элементов управления в приложении (Edit, Done, More, ...)?
Михам

1

Вот достойное решение этой проблемы, и оно не требует перезапуска приложения.

https://github.com/cmaftuleac/BundleLocalization

Эта реализация работает путем настройки внутри NSBundle. Идея состоит в том, что вы переопределяете метод localizedStringForKey в экземпляре объекта NSBundle, а затем вызываете этот метод в другом пакете с другим языком. Простой и элегантный, полностью совместимый со всеми типами ресурсов.


Этот код из проекта мне очень помог. Как найти пакет для конкретного кода языка ---NSString *path = [[NSBundle mainBundle] pathForResource:lang ofType:@"lproj" ];
Eonil

Этот класс работает нормально, но после смены языка на другой он не действует.
Anilkumar iOS ReactNative

0

Что бы вы ни делали, лучший способ - взять короткое имя для указанного языка, то есть: fr, en, nl, de, it и т. д ... и присвоить его глобальному значению.

сделайте вид выбора, чтобы он выскочил, как выпадающее меню (комбинация кнопки, при нажатии которой вид сборщика появляется снизу со списком языков) и выберите нужный язык. пусть короткое имя хранится внутри. сделать файл .h + .m с именем LocalisedString.

Установите глобальное значение short_name равным полученному значению в LocalisedString.m Когда выбран необходимый язык, назначьте NSBundlePath для создания подкаталогов проекта для нужного языка. например, nl.proj, en.proj.

Когда выбрана конкретная папка proj, вызовите локализованную строку для соответствующего языка и измените язык динамически.

никаких правил не нарушено.


0

Для Swift вы можете переопределить main.swiftфайл и установить там строку UserDefaults перед запуском приложения. Таким образом, вам не нужно перезапускать приложение, чтобы увидеть желаемый эффект.

import Foundation
import UIKit

// Your initialisation code here
let langCultureCode: String = "LANGUAGE_CODE"

UserDefaults.standard.set([langCultureCode], forKey: "AppleLanguages")
UserDefaults.standard.synchronize()

UIApplicationMain(CommandLine.argc, CommandLine.unsafeArgv, nil, NSStringFromClass(AppDelegate.self))

в паре с удалением @UIApplicationMainв вашем AppDelegate.swiftфайле.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.