Як змусити 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
Це не працює для мене, він буде використовувати мову за замовчуванням незалежно від того.
квано

50
Денніс; Здається, це спрацює краще, якщо встановити перевагу мови перед запуском програми. Я роблю це в функції main (), перш ніж викликати UIApplicationMain (). Після того, як він фактично запущений, він не змінить використовувану мову, а просто встановить перевагу наступного разу.
geon

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

18
якщо налаштування AppleLanguages ​​змінить мову під час виконання, якщо це зроблено в main () - правда! але ... ПЕРШИЙ ЧАС додаток встановлено, nsuserdefaults ініціалізуються ПІСЛЯ виклику UIApplicationMain (), який ігнорує раніше встановлені AppleLanguages ​​і вимагатиме потворного примусового перезапуску програми, щоб побачити потрібну мову.
JohnPayne

3
Це не працює з множиною елементів, визначених у Localizable.stringsdict. Будь-яка ідея, якщо є можливість виправити?
Mihai Fratu

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"];

Щоб повернутись до системних значень за замовчуванням, просто перейдіть до нуля:

[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
Привіт @ Вітання, для мене це чудово працює. Я навіть завантажив одну програму в магазин, і Apple не скаржиться.
Гілад Новік

7
Це чудове рішення, я також посилаюся на
Джеймс Тан

1
Привіт @JamesTang, Дякую за довідку та ваш пост - я знайшов там багато корисної інформації щодо локалізації.
Гілад Новік

3
@Gilad ваш метод працює ідеально для динамічних рядків (рядків, які я визначив у localizable.strings), але для кнопок та міток розкадрівки він працює лише в основному методі. тож чи можете ви розширити свою відповідь, щоб включити локалізацію елементів управління інтерфейсом? я маю на увазі, як оновити (не закриваючи) розкадровку після того, як я попрошу [NSBundle setLanguage: @ "??"] ;?
Jawad Al Shaikh

2
Що стосується XIB / раскадровки: вам потрібно переконатися, що ви зателефонували до цього методу, перш ніж ви завантажите подання. Що стосується оновлення поглядів наживо: у моєму випадку я просто перезавантажив контролер перегляду (помістив його в навігаційний контролер і просто замінив контролер. Після перезавантаження - він отримав нові перекладені рядки). Я працюю над кокаподом для цього, я, мабуть, включу і приклад. Будьте в курсі ...
Гілад Новік

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 / en.lproj / Localizable.strings, Documents / fr.lproj / Localizable.strings, ...). Ви навіть можете зробити NSBundle. Просто використовуйте цей код для шляху: NSString * path = [NSHomeDirectory () stringByAppendingPathComponent: [NSString stringWithFormat: @ "/ Documents /% @. Lproj", l, nil]];
arnaud del.

1
Мені здається, що це найкращий підхід разом із внеском Алессандро нижче. Це не так нав’язливо і не повинно вимагати перезавантаження.
PKCLsoft

1
Що я роблю неправильно? Я зробив Мовний клас, успадкувавши NSObject, поставив це у реалізацію, поклав назви методів у .h, а потім поставив [Language setLanguage: @ "es"]; у моїй кнопці Змінити мову запускається код (метод кнопки викликається і переходить до класу Мова), але нічого не робить. У мене теж налаштовані localizable.strings (іспанська). Але це, здається, працює для багатьох інших людей. Будь ласка, хтось тупий за мене.
SirRupertIII

1
@KKendall ви називаєте це так: [Мова setLanguage: @ "es"]; NSString * stringToUse = [Мова get: @ "Ваш текст" alter: nil];
Мікрася

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, вам доведеться перезавантажити xibs вручну з групи вибраної мови.
gklka

@gklka Я дотримуюся кроків, наданих Тудозьєром, але мої XIB не змінюють мови, як ви сказали, перезавантажити XIB вручну, що означає насправді перезавантажити XIB?
Dk Kumar

@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.m, безпосередньо перед UIApplicationMain(...).


2
Дуже корисна відповідь! PS Може здатися очевидним для початківців, але вам слід вставити цей рядок після того, NSAutoreleasePool * pool ..як декілька об'єктів, що вийшли у світ, просочуються.
pt2ph8

1
ВАЖЛИВО: Це не працюватиме, якщо зателефонувати [[NSBundle mainBundle] URLForResource:withExtension:]раніше.
Коф

3
А як бути, якщо використовувати Swift, то немає main.m?
turingtest

@turingested Ви пробували на швидкій та розгортці, чи працює вона?
iDevAmit

Привіт, Як локалізувати динамічний текст, який надходить із сервера, у моїй програмі кожен текст, який я повинен відображати спрощеною китайською мовою, але як відображати текст китайською мовою, який надходить англійською мовою. допоможи мені.
Mahesh Narla

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.

Привіт, Як локалізувати динамічний текст, який надходить із сервера, у моїй програмі кожен текст, який я повинен відображати спрощеною китайською мовою, але як відображати текст китайською мовою, який надходить англійською мовою. допоможи мені.
Mahesh Narla

Це геніальна відповідь;)
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;
}

Буду вдячний за будь-які коментарі з цього приводу.


в моєму виконанні це налаштована користувачем річ ​​- тому я просто
спливу

Він працює майже для всіх - за винятком локалізованого зображення 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 = [пучок зв’язкуForResource: imageFileName ofType: @ "png"]; UIImage * imageObj = [[UIImage alloc] initWithContentsOfFile: fullPath]; повернення [imageObj автореліз]; }
Ягі

11

NSLocalizedString()зчитує значення ключа AppleLanguagesз стандартних стандартних параметрів користувача ([NSUserDefaults standardUserDefaults] ). Це значення використовує для вибору відповідної локалізації серед усіх існуючих локалізацій під час виконання. Коли Apple запускає словник за замовчуванням користувачів під час запуску програми, вони шукають ключ бажаних мов у системних налаштуваннях та копіюють звідти значення. Це також пояснює, наприклад, чому зміна мовних налаштувань в OS X не впливає на запущені програми, лише на додатки, які починаються після цього. Після копіювання значення не оновлюється лише тому, що змінюються налаштування. Ось чому iOS перезапускає всі програми, якщо ви змінюєте мову.

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

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

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

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


Він працює лише перший раз, після чого він знову завантажується в попередній пристрій. мова ..
Акс

@Aks працює для мене кожен раз. Переконайтеся, що ви запускаєте правильну схему, переконайтеся, що ви починаєте з Xcode (повторні повтори, вилки тощо не будуть отримувати аргументи) і переконайтеся, що ви не перекриваєте мову, Optionsяк у нових версіях Xcode, що Apple пропонує, що також.
Mecki

Дякую за вашу відповідь @Mecki .. Для мене це не працює в xcode, але коли я згадую в коді, він працює .. Але все ж, якщо я встановив свій пристрій деякими іншими бажаними мовами, йому потрібно перезапустити додаток, щоб завантажити його в вибраний мовою, яку я передаю в аргументах ....
Акс

@Aks При використанні цього пристрою він працюватиме лише при запуску безпосередньо з Xcode, він не працюватиме, коли ви знову запустите додаток, натиснувши його значок на якомусь пристрої, і він також не працюватиме, коли додаток буде вбито (наприклад, тому що він перейшов у фоновий режим) і перезапускається системою (оскільки цей перезапуск не є перезавантаженням, зробленим Xcode) - тому перемикання на інший додаток і назад може не спрацювати, якщо iOS повинен вбити ваш додаток між ними (наприклад, тому що це потрібна пам'ять).
Mecki

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".
Галлімон,

9

Швидка версія:

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

Розширення Swift 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

Дякую. Добре працює.
Krunal Nagvadia

5

У файлі .pch визначте:

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


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

Як я можу позбутися попередження "овервер ..."?
Ідан Моше

1
Просте рішення робить все, що мені потрібно. Вдалося використовувати це як керівництво для вирішення моєї проблеми. Якщо pathForResource повертає нуль, оскільки у мене немає файлу локалізації, я використовую 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

я думаю, що сплеск працює до запуску основної програми, тому я не знаю, як її переосмислити, можливо, пліст може допомогти, але це може спрацювати наступного разу, коли програма відкриється
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

Використовуйте клас вище для посилання на рядовий файл / зображення / відео / тощо:

// 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

У швидкому 4 я вирішив це, не потребуючи перезавантаження або використання бібліотек.

Спробувавши багато варіантів, я знайшов цю функцію, де ви передаєте рядокToLocalize (з Localizable.String, файл рядків), який ви хочете перекласти, і мову, якою ви хочете перекласти його, і те, що він повертає, значення для той рядок, який у вас є у файлі Strings:

    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 і декількох підказок Брайана Вебстера та geon, я придумав цей фрагмент коду (виклав його в 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, ...)?
miham

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.