在iOS应用中手动选择语言(iPhone和iPad)


74

我的问题:

我的iPhone应用程序如何告诉iOS,用户确实在应用程序首选项中选择了一种语言,该语言与常规设置中设置的语言不同?

相同问题的其他提法:

我该如何告诉系统,该系统NSLocalizedString (@"text", @"comment");不应访问系统范围内选择的语言,而应访问应用程序内选择的语言?

背景,例如:

请以这种情况为例:一个德国移民的儿子住在法国东北部,毗邻卢森堡和德国。他的母语是法语,因此他确实将iPhone的用户界面语言设置为法语(设置->常规->国际->语言->法语)。但是由于他的文化背景,并且因为他居住的地区是双语地区,所以他的德语也很好。但是他不会说十个英语单词。在iPhone(以及iPad)上,他没有机会选择第二种语言,因此电话只知道他会说法语。它不了解其他语言的用户技能。

现在有了我的应用程序:我确实用英语和德语开发了它(德语是我的母语,英语是IT的标准语言)。我确实是根据适用于多个iOS应用程序的所有规则和最佳实践开发的。我的应用程序的“第一”语言(默认语言)是英语。

这表示:

如果有人在“设置”中选择了英语或德语,则应用程序用户界面将自动使用所选的语言。用户甚至不会注意到还有其他语言可用。

但是,如果他在常规设置中选择了任何其他语言(例如中文,波兰语或法语),他将获得应用程序的默认语言,在我的情况下为英语。但是对于我的法国德国朋友来说,这不是最佳选择。他想使用现有的德语版本,但似乎没有办法让用户选择此版本。

添加法文翻译将为我们的法德朋友解决问题,但不会解决使用其他两种语言(例如意大利语和德语)的人们,而且我无法使用此星球上所有语言支持我的应用程序。将默认语言设置为德语也不是最佳选择,因为这对于说法语(母语)和英语(作为第二语言)的人们会产生相同的问题。

因此,我认为我的应用必须能够手动选择与预选择的语言不同的语言。向应用程序设置面板添加语言选择不是问题。但是我如何告诉系统,该系统NSLocalizedString (@"text", @"comment");不应该访问系统范围内选择的语言,而应访问应用程序内选择的语言?


2
这不是一个众所周知的功能,但实际上可以在“设置”应用中设置多种首选语言。您必须以相反的顺序选择它们,例如,您的朋友需要将其iPhone切换为德语,然后切换为法语。这将使法语成为首选语言,如果不可用,则会回退到德语,然后再返回到应用程序库本地化版本。
Taum 2014年

5
首先:这不仅不广为人知,而且完全没有文件记录。第二:作为应用程序的开发者,我无权将此功能告知所有8亿iOS用户。即使我可以:每个用户都不太可能这样做。因此,这是一个不错的解决方法,许多高级用户可能会这样做,但这不是我作为开发人员可以写入我的应用程序的解决方案。
休伯特·舍纳斯特(HubertSchölnast),2014年

3
没问题的人,很高兴我能帮上忙!
Taum 2014年

7
嗯 好。我不确定在不伤害您的情况下怎么说,但是:您没有帮助。这就是我想用“完全没有记录”和“这不是解决方案”之类的短语来表达的意思。
休伯特·谢尔纳斯特(HubertSchölnast),2014年

Answers:


85

在此期间,我确实找到了解决自己的问题的解决方案:

我创建了一个新类“ LocalizeHelper”:


标题LocalizeHelper.h

//LocalizeHelper.h

#import <Foundation/Foundation.h>

// some macros (optional, but makes life easy)

// Use "LocalizedString(key)" the same way you would use "NSLocalizedString(key,comment)"
#define LocalizedString(key) [[LocalizeHelper sharedLocalSystem] localizedStringForKey:(key)]

// "language" can be (for american english): "en", "en-US", "english". Analogous for other languages.
#define LocalizationSetLanguage(language) [[LocalizeHelper sharedLocalSystem] setLanguage:(language)]

@interface LocalizeHelper : NSObject

// a singleton:
+ (LocalizeHelper*) sharedLocalSystem;

// this gets the string localized:
- (NSString*) localizedStringForKey:(NSString*) key;

//set a new language:
- (void) setLanguage:(NSString*) lang;              

@end

iMplementation LocalizeHelper.m

// LocalizeHelper.m
#import "LocalizeHelper.h"

// Singleton
static LocalizeHelper* SingleLocalSystem = nil;

// my Bundle (not the main bundle!)
static NSBundle* myBundle = nil;


@implementation LocalizeHelper


//-------------------------------------------------------------
// allways return the same singleton
//-------------------------------------------------------------
+ (LocalizeHelper*) sharedLocalSystem {
    // lazy instantiation
    if (SingleLocalSystem == nil) {
        SingleLocalSystem = [[LocalizeHelper alloc] init];
    }
    return SingleLocalSystem;
}


//-------------------------------------------------------------
// initiating
//-------------------------------------------------------------
- (id) init {
    self = [super init];
    if (self) {
        // use systems main bundle as default bundle
        myBundle = [NSBundle mainBundle];
    }
    return self;
}


//-------------------------------------------------------------
// translate a string
//-------------------------------------------------------------
// you can use this macro:
// LocalizedString(@"Text");
- (NSString*) localizedStringForKey:(NSString*) key {
    // this is almost exactly what is done when calling the macro NSLocalizedString(@"Text",@"comment")
    // the difference is: here we do not use the systems main bundle, but a bundle
    // we selected manually before (see "setLanguage")
    return [myBundle localizedStringForKey:key value:@"" table:nil];
}


//-------------------------------------------------------------
// set a new language
//-------------------------------------------------------------
// you can use this macro:
// LocalizationSetLanguage(@"German") or LocalizationSetLanguage(@"de");
- (void) setLanguage:(NSString*) lang {

    // path to this languages bundle
    NSString *path = [[NSBundle mainBundle] pathForResource:lang ofType:@"lproj" ];
    if (path == nil) {
        // there is no bundle for that language
        // use main bundle instead
        myBundle = [NSBundle mainBundle];
    } else {

        // use this bundle as my bundle from now on:
        myBundle = [NSBundle bundleWithPath:path];

        // to be absolutely shure (this is probably unnecessary):
        if (myBundle == nil) {
            myBundle = [NSBundle mainBundle];
        }
    }
}


@end

对于您要支持的每种语言,您需要一个名为的文件Localizable.strings。完全按照Apple本地化文档中的描述进行操作。唯一的区别:现在,您甚至可以使用Apple不支持的印地文或世界语等语言。

举个例子,这是我的英语和德语版本Localizable.strings的第一行:

英语

/* English - English */

/* for debugging */
"languageOfBundle" = "English - English";

/* Header-Title of the Table displaying all lists and projects */
"summary" = "Summary";

/* Section-Titles in table "summary" */
"help" = "Help";
"lists" = "Lists";
"projects" = "Projects";
"listTemplates" = "List Templates";
"projectTemplates" = "Project Templates";

德语

/* German - Deutsch */

/* for debugging */
"languageOfBundle" = "German - Deutsch";

/* Header-Title of the Table displaying all lists and projects */
"summary" = "Überblick";

/* Section-Titles in table "summary" */
"help" = "Hilfe";
"lists" = "Listen";
"projects" = "Projekte";
"listTemplates" = "Vorlagen für Listen";
"projectTemplates" = "Vorlagen für Projekte";

要使用本地化,您必须在应用程序中包含一些设置例程,并在语言选择中调用宏:

LocalizationSetLanguage(selectedLanguage);

此后,您必须确保以旧语言显示的所有内容现在都以新语言重新绘制(隐藏的文本必须在它们再次显示后立即重新绘制)。

若要使本地化的文本可用于每种情况,则永远不必将修订文本写入对象标题。始终使用宏LocalizedString(keyword)

别:

cell.textLabel.text = @"nice title";

做:

cell.textLabel.text = LocalizedString(@"nice title");

并且在每个Localizable.strings版本中都有一个“ nice title”条目!


21
这似乎是代码内文本字符串的一个很好的解决方案。但是基础国际化和情节提要如何?例如,如何强制MainStoryboard.storyboard切换到新语言?您已用LocalizedString(key)替换了NSLocalizedString(key,comment),这使您可以使用适当的捆绑软件。但这并不会在情节提要上强制进行更改。有没有办法更改情节提要使用的捆绑包?
格雷戈里·希尔

1
@HubertSchölnast不用担心,我发现了一个问题。我对localizable.string文件没有使用相同的名称。那会引起问题。现在问题解决了。
MinuMaster

1
如何确保重新加载所有VC,以使用新的所选语言更新标签?我正在使用'[self viewDidLoad]; [self viewWillAppear:(BOOL)NO];' 但是有时即使所有标签都在正确的方法中设置,它也无法正常工作并且语言也不会更改。我有一个德语文件和一个基本文件(英语)。
Mexx

1
@MaheshNarla:我描述的不是翻译算法。您需要的是一台翻译机,这是数十年来深入研究的主题,但仍未带来令人满意的解决方案。但似乎很明显,2017年的手机将无法为翻译机提供资源。
休伯特·舒纳斯特(HubertSchölnast),

1
@HubertSchölnast好的,但是我非常感谢您的宝贵回应。
Bhumesh Purohit

40

只需将以下内容添加到屏幕上即可选择语言:

    NSString *tempValue = //user chosen language. Can be picker view/button/segmented control/whatever. Just get the text out of it
    NSString *currentLanguage = @"";
    if ([tempValue rangeOfString:NSLocalizedString(@"English", nil)].location != NSNotFound) {
        currentLanguage = @"en";
    } else if ([tempValue rangeOfString:NSLocalizedString(@"German", nil)].location != NSNotFound) {
        currentLanguage = @"de";
    } else if ([tempValue rangeOfString:NSLocalizedString(@"Russian", nil)].location != NSNotFound) {
        currentLanguage = @"ru";
    }
    [[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:currentLanguage, nil] forKey:@"AppleLanguages"];
    [[NSUserDefaults standardUserDefaults]synchronize];

然后要求他们重新启动应用程序,该应用程序将使用其他语言。

希望能帮助到你


我没有尝试过您的建议,但我保证它不会解决我的问题。它似乎可以做以下两件事之一(我不敢肯定):将全局的系统范围语言选择设置为一种新语言,或者只是将选择的语言存储在应用程序UserDefaults中。更改全局语言设置很不好,因为我的应用程序中的选择一定不会影响其他应用程序!将选择存储在应用程序UserDefaults中只会将其存储在其中。IOS将不知道应该使用此应用程序的所选语言。
休伯特·舒纳斯特(HubertSchölnast)2012年

7
@HubertSchölnast如果您没有尝试过,如何确定它不会解决您的问题呢?另外,您无法在应用中更改“系统范围的语言选择”。而且您确定它不会执行任何操作吗?我确定它将更改应用程序的语言。
Novarg 2012年

我尝试了一下,它确实有效!谢谢!但是仍然存在一个问题:必须将应用程序完全关闭,然后重新启动。仅按主页按钮将其移至后台是不够的。我希望有一个更加用户友好的解决方案。
休伯特·肖纳斯特(HubertSchölnast)2012年

3
@HubertSchölnast,如果您想在单击主页按钮后关闭该应用程序,则可以将其添加Application does not run in backgroundinfo.plist文件中。然后,每次您从应用程序中单击主页按钮时,您的应用程序都会关闭。并将答案标记为正确,以鼓励其他用户将来回答您的问题。
Novarg 2012年

2
+1表示“如果您希望在单击主屏幕按钮后关闭应用程序,则可以将应用程序不在后台运行到info.plist文件中。”-避免强制关闭该应用程序以重新加载所有视图控制器
thomers

24

这是有关如何在Swift 3中使用Novarg的方法的即用型分步指南


步骤1:实施语言选择器

如何做到最好取决于您,具体取决于项目。但是用

Bundle.main.localizations.filter({ $0 != "Base" }) // => ["en", "de", "tr"]

以编程方式获取所有支持的语言环境代码列表。你也可以用

Locale.current.localizedString(forLanguageCode: "en") // replace "en" with your variable

以应用程序当前语言显示语言名称

作为一个完整的示例,您可以在单击如下按钮后显示弹出操作表

@IBOutlet var changeLanguageButton: UIButton!

@IBAction func didPressChangeLanguageButton() {
    let message = "Change language of this app including its content."
    let sheetCtrl = UIAlertController(title: "Choose language", message: message, preferredStyle: .actionSheet)

    for languageCode in Bundle.main.localizations.filter({ $0 != "Base" }) {
        let langName = Locale.current.localizedString(forLanguageCode: languageCode)
        let action = UIAlertAction(title: langName, style: .default) { _ in
            self.changeToLanguage(languageCode) // see step #2
        }
        sheetCtrl.addAction(action)
    }

    let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
    sheetCtrl.addAction(cancelAction)

    sheetCtrl.popoverPresentationController?.sourceView = self.view
    sheetCtrl.popoverPresentationController?.sourceRect = self.changeLanguageButton.frame
    present(sheetCtrl, animated: true, completion: nil)
}

步骤#2:向用户解释该怎么做+重新启动后更改语言

您可能已经注意到,步骤#1中的代码调用了名为的方法changeToLanguage(langCode:)。无论用户如何设计选择器当用户选择一种要更改的新语言时这也是您应该执行的操作。这是其实现,只需将其复制到您的项目中即可:

private func changeToLanguage(_ langCode: String) {
    if Bundle.main.preferredLocalizations.first != langCode {
        let message = "In order to change the language, the App must be closed and reopened by you."
        let confirmAlertCtrl = UIAlertController(title: "App restart required", message: message, preferredStyle: .alert)

        let confirmAction = UIAlertAction(title: "Close now", style: .destructive) { _ in
            UserDefaults.standard.set([langCode], forKey: "AppleLanguages")
            UserDefaults.standard.synchronize()
            exit(EXIT_SUCCESS)
        }
        confirmAlertCtrl.addAction(confirmAction)

        let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
        confirmAlertCtrl.addAction(cancelAction)

        present(confirmAlertCtrl, animated: true, completion: nil)
    }
}

这将询问并通知用户他是否要进行更改以及如何进行更改。此外,它还会在下一次启动时使用以下命令设置应用程序语言:

UserDefaults.standard.set([langCode], forKey: "AppleLanguages")
UserDefaults.standard.synchronize() // required on real device

步骤#3(可选):本地化字符串

您可能想通过使用NSLocalizedString宏(或任何其他增强方法)来对诸如“立即关闭”之类的字符串进行本地化。


现实世界的例子

我正在针对iOS 10的应用程序中使用此确切的实现,我可以确认它在模拟器和设备上均适用。该应用程序实际上是开源的,因此您可以在此处找到上述代码,并将其分发到不同的类中。


嗨,如何本地化来自服务器的动态文本,在我的应用程序中,我必须以简体中文显示每个文本,但是如何显示以英语显示的中文文本。帮我。
Mahesh Narla

1
正如您所说:文本是动态的,来自服务器,是英文的。通常的方法是告诉服务器您希望在请求中使用哪种语言,如果指定,则由服务器负责以简体中文文本进行响应。通常这是通过locale在请求中的某个位置进行设置来完成的。如果您无权访问服务器,则可能希望使用Google翻译之类的机器翻译服务–但结果可能不是最好的……
Jeehut

它为我工作了一段时间。您正在使用哪个版本的iOS?也许您做得不好,如果您上传了要实现此功能的代码部分,我可以看看...
Jeehut

如果我将使用退出(EXIT_SUCCESS)代码并尝试在iTunes Connect中上载应用程序。苹果会拒绝申请吗?
Bhavesh Nayi

2
因此,基本上,您建议在这2个警报控制器后面进行的操作是重新启动一个应用程序。不太有用
rommex

4

从iOS 13开始,现在有一种为iOS本身支持的每个应用设置单独语言的方法。在“ Apple设置”应用中,打开特定应用的设置,然后在“首选语言”下选择语言。您可以从应用程序支持的语言中进行选择。


1
是的,这很令人兴奋,如果有人给用户无缝的体验,则可以在应用程序中打开设置以选择所需的语言。developer.apple.com/videos/play/wwdc2019/403
Manish Nahar

3

我的回答可能是一个偏爱的问题,因为我不愿在应用程序中选择手动语言:您将不得不覆盖所有系统提供的按钮,并确保不使用它们。它还增加了另一层复杂性,并可能导致用户困惑。

但是,由于这必须是一个答案,因此我认为您的用例可以在不修改语言选择的情况下解决。

在iOS偏好设置中,您可以设置其他语言:

iOS语言选择首选项

您的示例移民儿子可能将法语设置为主要语言,将德语设置为其他语言。

然后,当您的应用程序本地化为英语和德语时,那个年轻人的iPhone就会选择德语资源。

这样可以解决问题吗?



0

手动更改语言非常简单容易。首先,您需要本地化应用程序,然后可以使用以下代码在应用程序中手动更改语言。

UIAlertController *actionSheet = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"App restart required", @"App restart required") message:NSLocalizedString(@"In order to change the language, the App must be closed and reopened by you.", @"In order to change the language, the App must be closed and reopened by you.") preferredStyle:UIAlertControllerStyleActionSheet];

    [actionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"Cancel", @"Cancel") style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {


        [self dismissViewControllerAnimated:YES completion:^{


        }];
    }]];

    [actionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"Restart", @"Restart") style:UIAlertActionStyleDestructive handler:^(UIAlertAction *action) {

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

        exit(EXIT_SUCCESS);


    }]];


    [self presentViewController:actionSheet animated:YES completion:nil];
}

1
如果我能做到,如您的回答,苹果将接受我的申请吗?
Bhavesh Nayi

是的,Apple对此方法没有问题,因为它的范围仅是您的应用程序。我已经分发了应用程序
Spydy

0

我在当前的工作项目中遇到了类似的问题。我想出了一种简单的方法来完成此操作,因此我必须向我的合作伙伴进行解释,以便讨论该解决方案。我用两个按钮(英语||西班牙语)制作了一个简单的应用程序以选择应用程序内的语言,代码在Objective-C中(因为我们当前的应用程序在Objective-C中),这是代码:https:// bitbucket .org / gastonmontes / gmlanguageselectiondemo

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.