Cách buộc NSLocalizedString sử dụng ngôn ngữ cụ thể


263

Trên iPhone NSLocalizedStringtrả về chuỗi bằng ngôn ngữ của iPhone. Có thể buộc NSLocalizedStringphải sử dụng một ngôn ngữ cụ thể để ứng dụng có ngôn ngữ khác với thiết bị không?


Vui lòng tham khảo điều này: stackoverflow.com/a/48187049/6665075 Hoạt động như một bùa mê
Ankit Kumar Gupta

Câu trả lời:


261

NSLocalizedString()(và các biến thể của chúng) truy cập khóa "AppleLacular" NSUserDefaultsđể xác định cài đặt của người dùng cho các ngôn ngữ ưa thích là gì. Điều này trả về một mảng mã ngôn ngữ, với mã đầu tiên là mã do người dùng đặt cho điện thoại của họ và mã tiếp theo được sử dụng làm dự phòng nếu tài nguyên không có sẵn trong ngôn ngữ ưa thích. (trên máy tính để bàn, người dùng có thể chỉ định nhiều ngôn ngữ với thứ tự tùy chỉnh trong Tùy chọn hệ thống)

Bạn có thể ghi đè cài đặt chung cho ứng dụng của riêng bạn nếu bạn muốn bằng cách sử dụng phương thức setObject: forKey: để đặt danh sách ngôn ngữ của riêng bạn. Điều này sẽ được ưu tiên hơn giá trị được đặt trên toàn cầu và được trả lại cho bất kỳ mã nào trong ứng dụng của bạn đang thực hiện nội địa hóa. Mã cho điều này sẽ trông giống như:

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

Điều này sẽ làm cho tiếng Đức trở thành ngôn ngữ ưa thích cho ứng dụng của bạn, với tiếng Anh và tiếng Pháp là dự phòng. Bạn sẽ muốn gọi điều này đôi khi sớm khi khởi động ứng dụng của bạn. Bạn có thể đọc thêm về sở thích ngôn ngữ / ngôn ngữ tại đây: Chủ đề lập trình quốc tế hóa: Lấy ngôn ngữ và ngôn ngữ hiện tại


4
Điều này không làm việc cho tôi, nó sẽ sử dụng ngôn ngữ mặc định bất kể điều gì.
quano

50
Denniss; Nó dường như hoạt động tốt hơn nếu bạn đặt tùy chọn ngôn ngữ trước khi ứng dụng được khởi chạy. Tôi làm điều đó trong hàm main (), trước khi UIApplicationMain () được gọi. Khi nó thực sự đang chạy, nó sẽ không thay đổi ngôn ngữ đã sử dụng mà chỉ đặt tùy chọn cho lần tiếp theo.
geon

3
Bạn phải đặt ngôn ngữ trước khi khởi tạo UIKit và bạn phải chỉ định ngôn ngữ hoàn chỉnh + ngôn ngữ vùng - kiểm tra xem ví dụ đầy đủ cho blog.federicomestrone.com/2010/09/15/iêu
fedmest

18
cài đặt AppleLacular sẽ thay đổi ngôn ngữ trong thời gian chạy nếu được thực hiện trong hàm main () - true! nhưng ... THỜI GIAN ĐẦU TIÊN mà ứng dụng được cài đặt, nsuserdefaults được khởi tạo SAU UIApplicationMain () sẽ được gọi, sẽ bỏ qua AppleLacular đã đặt trước đó và sẽ yêu cầu khởi động lại ứng dụng xấu xí để xem ngôn ngữ mong muốn.
JohnPayne

3
Điều này không hoạt động với các mục số nhiều được xác định trong Localizable.stringsdict. Bất cứ ý tưởng nếu có một sửa chữa có thể?
Mihai Fratu

147

Gần đây tôi đã gặp vấn đề tương tự và tôi không muốn bắt đầu và vá toàn bộ NSLocalizedString của mình cũng như không buộc ứng dụng khởi động lại để ngôn ngữ mới hoạt động. Tôi muốn mọi thứ để làm việc như vốn có.

Giải pháp của tôi là thay đổi linh hoạt lớp của gói chính và tải gói phù hợp ở đó:

Tập tin tiêu đề

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

Thực hiện

#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

Về cơ bản, khi ứng dụng của bạn khởi động và trước khi bạn tải bộ điều khiển đầu tiên, chỉ cần gọi:

[NSBundle setLanguage:@"en"];

Khi người dùng của bạn thay đổi ngôn ngữ ưa thích trong màn hình cài đặt của bạn, chỉ cần gọi lại:

[NSBundle setLanguage:@"fr"];

Để đặt lại về mặc định hệ thống, chỉ cần chuyển nil:

[NSBundle setLanguage:nil];

Thưởng thức...

Đối với những người cần một phiên bản 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
Xin chào @Wirsing, điều này làm việc rất tốt cho tôi cho đến nay. Tôi thậm chí đã tải một ứng dụng lên cửa hàng và không có khiếu nại nào của Apple.
Gilad Novik

7
Đây là một giải pháp tuyệt vời, tôi cũng đã tham khảo điều này ở đây: Medium.com/ios-apprentice/905e4052b9de
James Tang

1
Xin chào @JamesTang, Cảm ơn bạn đã tham khảo và bài đăng của bạn - Tôi đã tìm thấy rất nhiều thông tin hữu ích liên quan đến nội địa hóa.
Gilad Novik

3
@Gilad phương thức của bạn hoạt động hoàn hảo cho các chuỗi động (các chuỗi mà tôi đã xác định trong localizable.strings) nhưng đối với các nút và nhãn của bảng phân cảnh, nó chỉ hoạt động ở phương thức chính. Vì vậy, bạn có thể mở rộng câu trả lời của bạn để bao gồm nội địa hóa kiểm soát UI? ý tôi là làm thế nào để làm mới (không đóng) bảng phân cảnh sau khi tôi gọi [NSBundle setL Language: @ "??"];?
Jawad Al Shaikh

2
Đối với XIB / bảng phân cảnh: bạn cần đảm bảo rằng bạn gọi phương thức này TRƯỚC KHI bạn tải chế độ xem. Khi cập nhật chế độ xem trực tiếp: trong trường hợp của tôi, tôi chỉ cần tải lại bộ điều khiển chế độ xem (tôi đặt nó vào bộ điều khiển điều hướng và chỉ cần thay thế bộ điều khiển. Khi tải lại - nó có các chuỗi dịch mới). Tôi đang làm việc với một cocoapod cho nó, có lẽ tôi cũng sẽ bao gồm một ví dụ ở đó. Hãy theo dõi ...
Gilad Novik

137

Tôi thường làm điều này theo cách này, nhưng bạn PHẢI có tất cả các tệp bản địa hóa trong dự án của bạn.

@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. Đây là một mẹo thực sự hay mà tôi chưa từng thấy ở bất kỳ nơi nào khác. Bằng cách tạo một "gói phụ" từ một trong các thư mục bản địa hóa, bạn có thể làm cho công cụ chuỗi hoạt động tốt miễn là bạn bọc NSLocalizedString bằng thứ gì đó ở đây.
Ben Zotto

4
Mauro xuất sắc. Tôi nhận thấy rằng nó cũng có thể làm việc cho các tập tin trong dự án của bạn. Nếu vì một lý do nào đó (như trong trường hợp của tôi), bạn cần tải xuống các tệp chuỗi từ mạng và lưu trữ chúng trong thư mục 'Documents' (với cấu trúc thư mục Documents / en.lproj / Localizable.strings, Documents / fr.lproj / Bản địa hóa.strings, ...). Bạn thậm chí có thể tạo một NSBundle. Chỉ cần sử dụng mã này cho đường dẫn: NSString * path = [NSHomeDirectory () stringByAppendingPathComponent: [NSString stringWithFormat: @ "/ Documents /% @. Lproj", l, nil]];
arnaud del.

1
Dường như với tôi rằng đây là cách tiếp cận tốt nhất, cùng với sự đóng góp từ Alessandro dưới đây. Nó không quá khó chịu và không cần phải khởi động lại.
PKCLsoft

1
Tôi đang làm gì sai? Tôi đã tạo một lớp Ngôn ngữ, kế thừa NSObject, đưa phần này vào phần triển khai, đặt tên phương thức vào .h, sau đó đặt [Language setL Language: @ "es"]; trong Nút Ngôn ngữ Thay đổi của tôi, mã chạy (phương thức nút được gọi và đi đến lớp Ngôn ngữ), nhưng không làm gì cả. Tôi cũng đã cài đặt localizable.strings (tiếng Tây Ban Nha). Nhưng nó dường như đang làm việc cho rất nhiều người khác. Xin ai đó câm nó xuống cho tôi.
SirRupertIII

1
@KKendall bạn gọi nó như thế này: [Ngôn ngữ tập hợp Ngôn ngữ: @ "es"]; NSString * stringToUse = [Ngôn ngữ get: @ "Văn bản của bạn" thay đổi: nil];
Mikrasya

41

Không sử dụng trên iOS 9. Điều này trả về con số không cho tất cả các chuỗi được truyền qua nó.

Tôi đã tìm thấy một giải pháp khác cho phép bạn cập nhật các chuỗi ngôn ngữ, không khởi động lại ứng dụng và tương thích với các genstrings:

Đặt macro này trong Prefix.pch:

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

và bất cứ khi nào bạn cần một chuỗi sử dụng cục bộ:

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

Để đặt ngôn ngữ, hãy sử dụng:

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

Hoạt động ngay cả với việc nhảy ngôn ngữ liên tiếp như:

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: không, nó sẽ chỉ thay đổi các chuỗi trong tệp nguồn của bạn. Nếu bạn muốn thay đổi ngôn ngữ xib, bạn phải tải lại xibs bằng tay, từ gói ngôn ngữ bạn đã chọn.
gklka

@gklka Tôi đã làm theo các bước do Tudozier đưa ra nhưng XIB của tôi không thay đổi ngôn ngữ, như bạn đã nói tải lại XIB bằng tay, điều gì thực sự có nghĩa là tải lại XIB?
Dk Kumar

@DkKumar: Bạn phải làm một cái gì đó tương tự như thế này: stackoverflow.com/questions/21304988/
Kẻ

@gklka Câu trả lời của bạn có vẻ đúng và tôi đã thực hiện các bước tương tự như bạn đã giải thích trong câu trả lời của mình, nhưng vấn đề là nếu chúng ta thay đổi đường dẫn bó hơn thì nó sẽ tìm thấy tất cả tài nguyên (ví dụ: tệp .png được sử dụng trong XIB) trong cùng một gói, vì vậy chúng tôi phải sao chép quá mức thư mục hình ảnh trong tất cả các gói như en.lproj, es.lproj, v.v. vậy sẽ ảnh hưởng đến việc tăng kích thước IPA, vậy có cách nào khắc phục vấn đề này không? như bạn đã nói trong bài đăng của bạn, hãy để tôi thử điều này và cho bạn biết thêm về điều này Cảm ơn vì đã giúp tôi như vậy
Dk Kumar

1
Điều này bị phá vỡ khủng khiếp trong iOS 9, trả về null cho tất cả các chuỗi.
Alex Zavatone

32

Như đã nói trước đó, chỉ cần làm:

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

Nhưng để tránh phải khởi động lại ứng dụng, hãy đặt dòng này vào phương thức chính main.m, ngay trước đó UIApplicationMain(...).


2
Câu trả lời rất hữu ích! PS có thể rõ ràng đối với người không bắt đầu, nhưng bạn nên chèn dòng đó sau NSAutoreleasePool * pool ..hoặc một vài đối tượng tự động sẽ bị rò rỉ.
pt2ph8

1
QUAN TRỌNG: Điều này sẽ không hoạt động nếu bạn gọi [[NSBundle mainBundle] URLForResource:withExtension:]trước.
Kof

3
Còn nếu sử dụng Swift thì không có main.m?
turingtested

@turingtested bạn đã thử trên swift và bảng phân cảnh, nó có hoạt động không?
iDevAmit

Xin chào, Cách bản địa hóa văn bản động xuất phát từ máy chủ, trong ứng dụng của tôi, mọi văn bản tôi phải hiển thị bằng ngôn ngữ tiếng Trung giản thể, nhưng làm thế nào để hiển thị văn bản bằng tiếng Trung Quốc bằng tiếng Anh. giúp tôi.
Mahesh Narla

31

Mẹo để sử dụng ngôn ngữ cụ thể bằng cách chọn nó từ ứng dụng là buộc NSLocalizedStringsử dụng gói cụ thể tùy thuộc vào ngôn ngữ đã chọn,

đây là bài viết tôi đã viết cho việc học nội địa hóa nâng cao này trong các ứng dụng ios

và đây là mã của một bản địa hóa ứng dụng mẫu trong ứng dụng ios


Nó cũng hoạt động trên iOS7, tôi phải chuyển từ giải pháp của Brian sang điều này vì có vẻ như iOS sẽ ghi đè tùy chọn ngôn ngữ một lần nữa do đó tôi bị mắc kẹt với cài đặt ngôn ngữ lần đầu tiên. Giải pháp của bạn hoạt động như một cơ duyên!
haxpor

14

Bạn nghĩ gì về giải pháp này cho 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)
    }
}

Cách sử dụng đơn giản:

"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.

Xin chào, Cách bản địa hóa văn bản động xuất phát từ máy chủ, trong ứng dụng của tôi, mọi văn bản tôi phải hiển thị bằng ngôn ngữ tiếng Trung giản thể, nhưng làm thế nào để hiển thị văn bản bằng tiếng Trung Quốc bằng tiếng Anh. giúp tôi.
Mahesh Narla

Đây là một câu trả lời tuyệt vời;)
Bartłomiej Semańchot

câu trả lời tuyệt vời. đã giúp tôi
Dilip Tiwari

13

Như Brian Webster đã đề cập, ngôn ngữ cần được đặt "đôi khi sớm trong quá trình khởi động ứng dụng của bạn". Tôi nghĩ applicationDidFinishLaunching:trong những AppDelegatephải là một nơi thích hợp để làm điều đó, vì nó là nơi tôi làm tất cả khởi khác.

Nhưng như William Denniss đề cập, điều đó dường như chỉ có tác dụng sau khi ứng dụng được khởi động lại, điều này là vô ích.

Nó dường như hoạt động tốt nếu tôi đặt mã vào hàm chính:

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;
}

Tôi đánh giá cao bất kỳ ý kiến ​​về điều này.


trong triển khai của tôi, đó là một thứ có thể sử dụng được - vì vậy tôi chỉ cần bật lên một hộp thoại cho họ biết họ sẽ cần phải khởi động lại :)
William Denniss

Nó hoạt động, gần như cho tất cả - ngoại trừ hình ảnh Default.png được bản địa hóa.
Lukasz

2
Nếu bạn cho phép người dùng đặt ngôn ngữ và sau đó bảo họ khởi động lại ứng dụng, chỉ cần nhớ rằng NSUserDefaults có thể không được lưu nếu họ khởi động lại quá nhanh. Hầu hết người dùng không nhanh như vậy, nhưng khi bạn thử nghiệm ứng dụng, bạn có thể quá nhanh và kết quả là hành vi không nhất quán. Phải mất vài giờ tôi mới nhận ra lý do tại sao công tắc ngôn ngữ của tôi đôi khi hoạt động và đôi khi không!
arlomedia

3
Đó không phải là vấn đề, chỉ cần sử dụng [[NSUserDefaults standardUserDefaults] synchronize];sau khi gọisetObject:forKey:
Ben Baron

11

Tôi thích phương pháp tốt nhất của Mauro Delrio. Tôi cũng đã thêm các mục sau trong Project_Prefix.pch của mình

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

Vì vậy, nếu bạn muốn sử dụng phương thức chuẩn (sử dụng NSLocalizedString), bạn có thể thực hiện thay thế cú pháp nhanh trong tất cả các tệp.


1
Tôi cũng thích phương thức của anh ấy và tôi thêm một phương thức để tải hình ảnh png: + (UIImage ) imageNamed: (NSString *) imageFileName {NSString fullPath = [bundle pathForResource: imageFileName ofType: @ "png"]; UIImage * imageObj = [[UIImage alloc] initWithContentsOfFile: fullPath]; trả về [imageObj autorelease]; }
Jagie

11

NSLocalizedString()đọc giá trị của khóa AppleLanguagestừ mặc định của người dùng chuẩn ( [NSUserDefaults standardUserDefaults]). Nó sử dụng giá trị đó để chọn một bản địa hóa phù hợp trong số tất cả các bản địa hóa hiện có trong thời gian chạy. Khi Apple xây dựng từ điển mặc định của người dùng khi khởi chạy ứng dụng, họ sẽ tra cứu (các) ngôn ngữ ưa thích trong tùy chọn hệ thống và sao chép giá trị từ đó. Điều này cũng giải thích ví dụ tại sao việc thay đổi cài đặt ngôn ngữ trong OS X không có tác dụng đối với các ứng dụng đang chạy, chỉ trên các ứng dụng bắt đầu sau đó. Sau khi sao chép, giá trị không được cập nhật chỉ vì cài đặt thay đổi. Đó là lý do iOS khởi động lại tất cả các ứng dụng nếu bạn thay đổi ngôn ngữ.

Tuy nhiên, tất cả các giá trị của từ điển mặc định của người dùng có thể được ghi đè bằng các đối số dòng lệnh. Xem NSUserDefaultstài liệu trên NSArgumentDomain. Điều này thậm chí bao gồm những giá trị được tải từ tệp tùy chọn ứng dụng (.plist). Điều này thực sự tốt để biết nếu bạn muốn thay đổi một giá trị chỉ một lần để thử nghiệm .

Vì vậy, nếu bạn muốn thay đổi ngôn ngữ chỉ để thử nghiệm, có lẽ bạn không muốn thay đổi mã của mình (nếu bạn quên xóa mã này sau ...), thay vào đó hãy nói với Xcode để khởi động ứng dụng của bạn bằng tham số dòng lệnh ( ví dụ: sử dụng bản địa hóa tiếng Tây Ban Nha):

nhập mô tả hình ảnh ở đây

Không cần phải chạm vào mã của bạn cả. Chỉ cần tạo các lược đồ khác nhau cho các ngôn ngữ khác nhau và bạn có thể nhanh chóng khởi động ứng dụng một lần bằng một ngôn ngữ và một lần bằng ngôn ngữ khác bằng cách chuyển đổi lược đồ.


Nó chỉ hoạt động lần đầu tiên sau đó nó lại tải vào thiết bị pref. ngôn ngữ ..
Aks

@Aks Hoạt động mọi lúc cho tôi. Đảm bảo bạn đang bắt đầu sơ đồ chính xác, đảm bảo rằng bạn bắt đầu từ bên trong Xcode (khởi chạy lại, chuyển đổi, v.v. sẽ không nhận được đối số) và đảm bảo rằng bạn không ghi đè ngôn ngữ Optionsnhư với các phiên bản Xcode mới hơn mà Apple cung cấp cũng.
Mecki

Cảm ơn câu trả lời của bạn @Mecki .. Đối với tôi nó không hoạt động trong xcode nhưng khi tôi đề cập đến mã, nó vẫn hoạt động .. Nhưng nếu tôi đã đặt thiết bị của mình với một số ngôn ngữ ưa thích khác, nó cần khởi động lại ứng dụng để tải vào ứng dụng ưa thích của tôi ngôn ngữ mà tôi đang truyền đạt trong các cuộc tranh luận ....
Aks

@Aks Khi sử dụng trên thiết bị, nó sẽ chỉ hoạt động khi được khởi động trực tiếp từ Xcode, nó sẽ không hoạt động khi bạn khởi động lại ứng dụng bằng cách nhấn vào biểu tượng của nó trên một số thiết bị và nó cũng sẽ không hoạt động khi ứng dụng bị tắt (ví dụ: bởi vì nó đã chạy nền) và được hệ thống khởi động lại (vì việc khởi động lại này không phải là khởi động lại được thực hiện bởi Xcode) - vì vậy việc chuyển sang một ứng dụng khác và quay lại có thể không hoạt động nếu iOS phải tắt ứng dụng của bạn ở giữa (ví dụ: vì nó cần bộ nhớ).
Mecki

10

Tôi đã đưa ra một giải pháp cho phép bạn sử dụng NSLocalizedString. Tôi tạo một thể loại của NSBundlecuộc gọi NSBundle+RunTimeLanguage. Giao diện là như thế này.

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

Việc thực hiện là như thế này.

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

Hơn chỉ cần thêm nhập NSBundle+RunTimeLanguage.hvào các tập tin sử dụng NSLocalizedString.

Như bạn có thể thấy tôi lưu trữ mã ngôn ngữ của mình trong một thuộc tính của AppDelegate. Điều này có thể được lưu trữ bất cứ nơi nào bạn muốn.

Điều duy nhất tôi không thích về nó là một Cảnh báo mà NSLocalizedStringmarco đã định nghĩa lại. Có lẽ ai đó có thể giúp tôi sửa phần này.


1
Thêm #undef NSLocalizedStringngay trước #defineđể vô hiệu hóa cảnh báo
beryllium

Điều này có hoạt động trên nội địa hóa cho tập tin xib? Tôi có tệp .xib và .strings để dịch tên UI theo ngôn ngữ cụ thể. Tôi đã thử và nó không hoạt động với XIB nhưng tôi không chắc liệu mình đã làm đúng chưa
Kong Hantrakool

Kong xin lỗi vì phản hồi chậm trễ. Điều này hoạt động ở bất cứ nơi nào bạn sử dụng NSLocalizedString. Vì vậy, nó sẽ không hoạt động trực tiếp trong XIB. Bạn sẽ cần IBOutlets cho các chuỗi trong XIB và sau đó sẽ phải lập trình các giá trị chuỗi trong mã.
qman64

Cách tiếp cận này hiệu quả với tôi và tôi đã có thể thay đổi ngôn ngữ một cách nhanh chóng. Lưu ý rằng các mã ngôn ngữ như "es", fr "và" en "hoạt động tốt. Nhưng" zh "(đối với tiếng Trung giản thể) đã thất bại và trả lại cho tôi chuỗi null. Tôi đã thực hiện tìm kiếm toàn cầu trên hệ thống của mình cho" es.lproj " xem các tệp tôi đang truy cập ở đâu và tôi phát hiện ra rằng "zh.lproj" thực sự là "zh-Hans.lproj".
Gallymon

9

Phiên bản Swift:

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

8

Tóm lại:

Bản địa hóa ứng dụng của bạn

Đó là điều đầu tiên bạn phải làm là bản địa hóa ứng dụng của bạn với ít nhất hai ngôn ngữ (tiếng Anh và tiếng Pháp trong ví dụ này).

Ghi đè NSLocalizedString

Trong mã của bạn, thay vì sử dụng NSLocalizedString(key, comment), hãy sử dụng macro MYLocalizedString(key, comment)được xác định như sau: #define MYLocalizedString(key, comment) [[MYLocalizationSystem sharedInstance] localizedStringForKey:(key) value:(comment)];

Người MYLocalizationSystemđộc thân này sẽ:

  • Đặt langage bằng cách đặt đúng người dùng NSBundle yêu cầu
  • Trả về NSString đã bản địa hóa theo ngôn ngữ đã đặt trước đó

Đặt ngôn ngữ người dùng

Khi người dùng thay đổi ngôn ngữ ứng dụng bằng tiếng Pháp, hãy gọi [[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];
}

Trong ví dụ này, phương thức này đặt gói cục bộ thành fr.lproj

Trả về chuỗi cục bộ

Khi bạn đã đặt gói được bản địa hóa, bạn sẽ có thể nhận được chuỗi địa phương hóa chính xác từ anh ấy bằng phương pháp này:

- (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];
}

Hy vọng điều này sẽ giúp bạn.

Bạn sẽ tìm thấy nhiều thông tin chi tiết hơn trong bài viết này từ NSWeries.io


Chúng tôi yêu cầu bạn không chỉ liên kết đến một giải pháp trong câu trả lời của bạn; liên kết có thể ngừng hoạt động một ngày nào đó. Mặc dù bạn không phải xóa liên kết khỏi câu trả lời của mình, chúng tôi yêu cầu bạn chỉnh sửa câu trả lời của mình để bao gồm một bản tóm tắt về phần có liên quan của bài viết được liên kết.
Hiền nhân lãng quên

7

Tiện ích mở rộng 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
    }
}

Sử dụng:

Locale.preferredLanguage = "uk"

label.text = "localizedKey".localized

Cảm ơn. Làm việc tốt.
Krunal Nagvadia

5

Trong tệp .pch để xác định:

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


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

Làm cách nào để thoát khỏi cảnh báo 'ovverride ...'?
Idan Moshe

1
Giải pháp đơn giản làm mọi thứ tôi cần. Đã có thể sử dụng điều này như một hướng dẫn để giải quyết vấn đề của tôi. Nếu pathForResource trả về nil vì tôi không có tệp bản địa hóa thì tôi sử dụng pathForResource: @ "Base" vì không có gì khác tôi đã thử kéo các chuỗi từ bản địa hóa cơ sở. Cảm ơn bạn.
GRW

Đây là hacky (tất cả các câu trả lời khác), và nó dễ thực hiện nhất trong bài kiểm tra đơn vị tôi đã viết.
Tối đa

4

Có lẽ bạn nên bổ sung với điều này (trên tệp .pch sau #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/Mylight/viewControll/OrientationReadyPagesView.m:193:31: Sử dụng 'bó' không được khai báo '
pengwang

4

Giải pháp 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")

Đã cho một số ví dụ về mã ngôn ngữ có thể được sử dụng. Hi vọng điêu nay co ich


3

Bạn có thể xây dựng một gói phụ với tập hợp các chuỗi được bản địa hóa mà bạn muốn thực hiện điều này với, sau đó sử dụng NSLocalizedStringFromTableInBundle()để tải chúng. (Tôi cho rằng đây là nội dung tách biệt với nội địa hóa UI thông thường mà bạn có thể đang thực hiện trên ứng dụng.)


3

đối với trường hợp của tôi, tôi có hai tệp cục bộ, ja và en

và tôi muốn buộc nó thành en nếu ngôn ngữ ưa thích trong hệ thống không en hoặc ja

Tôi sẽ chỉnh sửa tập tin main.m

Tôi sẽ kiểm tra xem ưu tiên thứ nhất là en hay ja, nếu không thì tôi sẽ thay đổi ngôn ngữ ưa thích thứ hai thành 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]));


    }


}

Cảm ơn @ chings228 nó rất hữu ích đối với tôi, nhưng điều này không thay đổi hình ảnh khởi chạy mặc định (giật gân) cho ứng dụng mà tôi có các giật gân khác nhau cho mỗi ngôn ngữ. Bạn có biết làm thế nào để áp dụng cho điều này ??
JERC

Tôi nghĩ rằng trò chơi giật gân chạy trước khi chương trình chính bắt đầu chạy, vì vậy tôi không biết cách ghi đè lên nó, có thể rất hữu ích nhưng nó có thể hoạt động cho lần tiếp theo khi ứng dụng mở ra
chings228

2

Bạn có thể làm một cái gì đó như thế này:

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

Dựa trên câu trả lời của Tudorizer để thay đổi ngôn ngữ mà không cần rời khỏi hoặc khởi động lại ứng dụng.

Thay vì macro, sử dụng một lớp để truy cập ngôn ngữ ưa thích để kiểm tra xem có mã ngôn ngữ cụ thể không.

Dưới đây là một lớp được sử dụng để có được gói ngôn ngữ hiện tại đang hoạt động cho 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

Sử dụng lớp ở trên để tham chiếu tệp chuỗi / hình ảnh / video / vv:

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

Thay đổi ngôn ngữ nội tuyến như bên dưới hoặc cập nhật phương thức "changeCiverseL Language" trong lớp ở trên để lấy tham số chuỗi tham chiếu ngôn ngữ mới.

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

2

Trong swift 4, tôi đã giải quyết nó mà không cần phải khởi động lại hoặc sử dụng các thư viện.

Sau khi thử nhiều tùy chọn, tôi đã tìm thấy hàm này, nơi bạn truyền chuỗi StringToLocalize (của Localizable.String, tệp chuỗi) mà bạn muốn dịch và ngôn ngữ mà bạn muốn dịch và giá trị mà nó trả về là giá trị cho Chuỗi mà bạn có trong tệp Chuỗi:

    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)
    }

Có tính đến chức năng này, tôi đã tạo chức năng này trong tệp 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!)!
    }
}

Để truy cập từ toàn bộ ứng dụng và trong từng chuỗi của phần còn lại của ViewControllers, thay vì đặt:

NSLocalizedString ("StringToLocalize", comment: ")

Tôi đã thay thế nó bằng

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

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

Tôi không biết đó có phải là cách tốt nhất không, nhưng tôi thấy nó rất đơn giản và nó hiệu quả với tôi, tôi hy vọng nó sẽ giúp bạn!


1

Hàm này sẽ cố gắng lấy chuỗi địa phương hóa cho ngôn ngữ hiện tại và nếu không tìm thấy, nó sẽ lấy chuỗi đó bằng ngôn ngữ tiếng Anh.

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

Tôi muốn thêm hỗ trợ cho một ngôn ngữ không được iOS hỗ trợ chính thức (không được liệt kê trong phần Ngôn ngữ trong cài đặt hệ thống). Bằng cách làm theo Hướng dẫn quốc tế hóa của Apple và một vài gợi ý ở đây của Brian Webster và geon, tôi đã đưa ra đoạn mã này (đặt nó vào 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]));
    }
}

Điều này hoạt động tốt cho cả mã Storyboard và NSLocalizedString. Mã này giả định rằng người dùng sẽ có tùy chọn thay đổi ngôn ngữ bên trong ứng dụng theo cách thủ công sau này.

Tất nhiên, đừng quên thêm các bản dịch Storyboard và bản dịch Localizable.strings thích hợp (xem liên kết đến trang Apple ở trên để biết cách làm điều đó).


Tôi thấy bạn cũng đang gặp vấn đề với ngôn ngữ tiếng Slovenia :) Để cải thiện câu trả lời này cho các ngôn ngữ không được iOS hỗ trợ, bạn đã biết liệu chúng ta có thể đặt các chuỗi có thể định vị tùy chỉnh sẽ được sử dụng cho các điều khiển hệ thống trong ứng dụng hay không (Chỉnh sửa, Xong, Khác, ...)?
miham

1

Đây là một giải pháp tốt cho vấn đề này và nó không yêu cầu khởi động lại ứng dụng.

https://github.com/cmaftuleac/BundleLocalization

Việc thực hiện này hoạt động bằng cách điều chỉnh bên trong NSBundle. Ý tưởng là bạn ghi đè phương thức localizedStringForKey trên thể hiện của đối tượng NSBundle, sau đó gọi phương thức này trên một gói khác với một ngôn ngữ khác. Đơn giản và thanh lịch hoàn toàn tương thích với tất cả các loại tài nguyên.


Mã này từ dự án đã giúp tôi rất nhiều. Cách tìm gói cho mã ngôn ngữ cụ thể ---NSString *path = [[NSBundle mainBundle] pathForResource:lang ofType:@"lproj" ];
Eonil

Lớp này đang hoạt động tốt, nhưng sau khi thay đổi ngôn ngữ sang ngôn ngữ khác, nó không có hiệu lực.
Anilkumar iOS ReactNative

0

bất cứ điều gì bạn làm, cách tốt nhất là lấy short_name cho ngôn ngữ đã chỉ định, ví dụ: fr, en, nl, de, it, v.v ... và gán cùng một giá trị toàn cầu.

tạo chế độ xem bộ chọn để bật lên như trình đơn thả xuống (kết hợp một nút khi nhấp vào chế độ xem bộ chọn xuất hiện từ bên dưới với danh sách các ngôn ngữ) và chọn ngôn ngữ bạn muốn. hãy để tên ngắn được lưu trữ nội bộ tạo một tệp .h + .m có tên LocalisedString.

Đặt giá trị toàn cầu của short_name bằng với giá trị thu được trong LocalisedString.m Khi ngôn ngữ được yêu cầu được chọn, gán NSBundlePath để tạo thư mục con dự án cho ngôn ngữ cần thiết. ví dụ: nl.proj, en.proj.

Khi thư mục proj cụ thể được chọn, hãy gọi chuỗi cục bộ cho ngôn ngữ tương ứng và thay đổi ngôn ngữ một cách linh hoạt.

không có quy tắc bị phá vỡ.


0

Đối với Swift, bạn có thể ghi đè main.swifttệp và đặt chuỗi UserDefaults ở đó trước khi ứng dụng chạy. Bằng cách này, bạn không phải khởi động lại Ứng dụng để thấy hiệu quả mong muốn.

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

kết hợp với việc loại bỏ @UIApplicationMaintrong AppDelegate.swifttập tin của bạn .

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.