web-dev-qa-db-ja.com

プログラムでiOSを実行しているときにオンザフライで言語を変更する

私は何時間もスタックしてググリングしてきました。そして、私は今やや絶望的です。アプリ内の言語をデフォルトの言語だけでなく、アプリ内で変更したいと思います。

私が試したものから、私は再起動ステップでみんなのように立ち往生しました。つまり、リンゴはアプリを手動で再起動することを強制します。つまり、アプリを終了してから再起動する必要があります。

さて、グーグルした後、私はアラームを設定しようとしていて、後でアプリを強制的に終了させました

exit(0);

私の悪い、Appleはこれが好きではなく、開発者がそれを使用するのを妨げているようです...私は正しい方向を指していないと思います。

最後に、すべての問題にもかかわらず、私はそれについて話し合いたいと思います。

ヒントはありますか?


編集、アップルからの情報

一般に、アプリケーション内から(AppleLanguages prefキーを使用して)iOSシステム言語を変更しないでください。これは、設定アプリで言語を切り替えるための基本的なiOSユーザーモデルに反し、ドキュメント化されていない設定キーも使用します。つまり、将来、キー名が変更され、アプリケーションが破損する可能性があります。

アプリケーションで言語を切り替えたい場合は、バンドル内のリソースファイルを手動で読み込むことで切り替えることができます。この目的のためにNSBundle:pathForResource:ofType:inDirectory:forLocalization:を使用できますが、アプリケーションがローカライズされたデータのすべてのロードを担当することに注意してください。

Exit(0)の質問に関しては、Apple DTSはアプリの承認プロセスについてコメントできません。appreview@ Apple.comに連絡して、この回答を得る必要があります。質問。

さて、ここまでは選ばないといけません。

35
gabrielstuff

これはかなり古い質問ですが、私は同じ問題に苦労していて、この解決策を見つけました:

http://aggressive-mediocrity.blogspot.com/2010/03/custom-localization-system-for-your.html

これはあなたが必要とするものを正確に実行します(そして同じ問題を持つ他の人にとって役立つかもしれません:)

22
Swissdude

以下のリンクは、アプリケーションでwithを使用してカスタム言語を作成する、すてきな実装です。

iOSアプリ(iPhoneおよびiPad)での手動言語選択

Swiftバージョンをここで見つけようとした LanguageSettings_Swift

LanguageChange

-anoop

7
anoop4real

はい、同じ問題が発生した後、自分の言語設定をprefFileで管理し、言語設定の変数を設定しました。

// write a new value in file and set the var
- (void)changeLangInPrefFile:(NSString *)newLanguage {
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *path = [documentsDirectory stringByAppendingPathComponent:@"myPreference.plist"];
    NSMutableDictionary *data = [[NSMutableDictionary alloc] initWithContentsOfFile: path];
    //here add elements to data file and write data to file
    [data setObject:newLanguage forKey:@"language"];
    [data writeToFile:path atomically:YES];
    [data release];

// NSString *chosenLang; <- declared in .h file
    if (chosenLang != nil){
        [chosenLang release];
        chosenLang = nil;
    }
    chosenLang = [[NSString alloc] initWithString:(@"%@",newLanguage)];

}

// read the language from file and set the var:
- (void)readFromFileInBundleDocuments {
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *path = [documentsDirectory stringByAppendingPathComponent:@"myPreference.plist"];
    NSMutableDictionary *savedStock = [[NSMutableDictionary alloc] initWithContentsOfFile:path];

    NSString *chosenLangTemp = [savedStock objectForKey:@"language"];
    NSLog (@"read in file: %@", chosenLangTemp);
    if (chosenLang != nil){
        [chosenLang release];
        chosenLang = nil;
    }
    chosenLang = [[NSString alloc] initWithString:(@"%@",chosenLangTemp)];
    [savedStock release];
}

次に、言語に応じて異なるファイルからすべてのコンテンツをロードします。たとえば、「an_image_eng.png」または「an_image_ita.png」をロードするか、2つの異なる.xibファイルを使用して、テキストをロードするために異なる辞書ファイルを使用します。各言語に1つ、すべての単語/表現が翻訳された状態で、選択した1つをロードして、ロードするすべてのテキストに適切な式を読み込みます(ロードするコードは、この例で書いた方法と同様です。すべての式に対して正しいWordを読み取るように調整するだけです。正しい辞書ファイルでobjectForKeyの値を確認します。ここで、objectForKeyは翻訳するWordであり、その値は翻訳されるWordです)...

4
meronix

一般に、ユーザーに表示される言語は、システム全体の設定であるロケール設定によって決まります。ユーザーだけが変更できます。変更すると、SpringBoardとデバイスで実行中のすべてのアプリケーションを再起動する必要があります。すべてのシステムアプリとフレームワークは、実行を開始するとロケールは変更されないと想定しているため、これを回避する方法はありません。再起動を必要としないようにアプリとフレームワークを変更することは、Appleで行うのは非常に困難です。

私は、システムロケール設定とは完全に独立してアプリのインターフェイスの言語を変更するか、デフォルトでシステムロケール設定を使用したいのに、ユーザーがアプリだけにそれを上書きできるようにしたいと思います。

現在のロケールを取得し、_+[NSLocale currentLocale]_を使用してさまざまな値を調べることができます。システムロケールに依存しない言語でアプリのユーザーインターフェースを表示するには、NSLocalizedString()の使用を完全に回避し、独自のカスタム状態を使用して、表示する文字列を決定する必要がありますユーザーに、そしてアプリの言語に合うようにインターフェースを変更する方法。アプリの言語状態を維持し、ユーザーインターフェイスを適切に変更するかどうかは、あなた次第です。

1
Ryan

これは古い質問ですが、言語がその場で変わるときに通知するヘルパーを開発していました。

ヘルパーのコードを見てください:

import Foundation

class LocalizableLanguage {

    // MARK: Constants

    fileprivate static let Apple_LANGUAGE_KEY = "AppleLanguages"

    /// Notification Name to observe when language change
    static let ApplicationDidChangeLanguage = Notification.Name("ApplicationDidChangeLanguage")

    // MARK: Properties

    /// An array with all available languages as String
    static var availableLanguages: [String]? = {
        return UserDefaults.standard.object(forKey: Apple_LANGUAGE_KEY) as? [String]
    }()

    /// The first element of available languages that is the current language
    static var currentLanguageCode: String? = {
        return availableLanguages?.first
    }()

    /// The current language code with just 2 characters
    static var currentShortLanguageCode: String? = {
        guard let currentLanguageCode = currentLanguageCode else {
            return nil
        }

        let strIndex = currentLanguageCode.index(currentLanguageCode.startIndex, offsetBy: 2)
        return currentLanguageCode.substring(to: strIndex)
    }()

    // MARK: Handle functions

    /// This accepts the short language code or full language code
    /// Setting this will send a notification with name "ApplicationDidChangeLanguage", that can be observed in order to refresh your localizable strings
    class func setLanguage(withCode langCode: String) {

        let matchedLangCode = availableLanguages?.filter {
            $0.contains(langCode)
        }.first

        guard let fullLangCode = matchedLangCode else {
            return
        }

        var reOrderedArray = availableLanguages?.filter {
            $0.contains(langCode) == false
        }

        reOrderedArray?.insert(fullLangCode, at: 0)

        guard let langArray = reOrderedArray else {
            return
        }

        UserDefaults.standard.set(langArray, forKey: Apple_LANGUAGE_KEY)
        UserDefaults.standard.synchronize()

        LocalizableLanguage.refreshAppBundle()

        NotificationCenter.default.post(name: ApplicationDidChangeLanguage, object: fullLangCode)
    }
}

// MARK: Refresh Bundle Helper

private extension LocalizableLanguage {

    class func refreshAppBundle() {
        MethodSwizzleGivenClassName(cls: Bundle.self, originalSelector: #selector(Bundle.localizedString(forKey:value:table:)), overrideSelector: #selector(Bundle.specialLocalizedStringForKey(_:value:table:)))
    }

    class func MethodSwizzleGivenClassName(cls: AnyClass, originalSelector: Selector, overrideSelector: Selector) {
        let origMethod: Method = class_getInstanceMethod(cls, originalSelector);
        let overrideMethod: Method = class_getInstanceMethod(cls, overrideSelector);
        if (class_addMethod(cls, originalSelector, method_getImplementation(overrideMethod), method_getTypeEncoding(overrideMethod))) {
            class_replaceMethod(cls, overrideSelector, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
        } else {
            method_exchangeImplementations(origMethod, overrideMethod);
        }
    }
}

extension Bundle {

    func specialLocalizedStringForKey(_ key: String, value: String?, table tableName: String?) -> String {

        let availableLanguages = UserDefaults.standard.object(forKey: LocalizableLanguage.Apple_LANGUAGE_KEY) as? [String]
        let currentLanguageCode = availableLanguages?.first ?? "en-US"
        let currentShortLanguageCode = currentLanguageCode.substring(to: currentLanguageCode.index(currentLanguageCode.startIndex, offsetBy: 2))

        let path =
                Bundle.main.path(forResource: currentLanguageCode, ofType: "lproj") ??
                Bundle.main.path(forResource: currentShortLanguageCode, ofType: "lproj") ??
                Bundle.main.path(forResource: "Base", ofType: "lproj")

        guard
            self == Bundle.main,
            let bundlePath = path,
            let bundle = Bundle(path: bundlePath)
        else {
            return self.specialLocalizedStringForKey(key, value: value, table: tableName)
        }

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

そのコードをコピーしてプロジェクトに配置するだけです。

次に、次のようにリスナーを単純に実装します。

NotificationCenter.default.addObserver(forName: LocalizableLanguage.ApplicationDidChangeLanguage, object: nil, queue: nil) { notification in
            guard let langCode = notification.object as? String else {
                return
            }
            self.accountStore.languageCode.value = langCode
        } 

この行self.accountStore.languageCode.value = langCodeは、アプリの言語が変更されたときに更新する必要があるものであり、ユーザーの言語をすぐに変更するために、ViewModelのすべての文字列を簡単に変更できます。

言語を変更するには、次を呼び出すだけです。

LocalizableLanguage.setLanguage(withCode: "en")

あなたにいいかもしれない他のヘルパーは:

import Foundation

extension String {

    var localized: String {
        return NSLocalizedString(self, comment: "")
    }

}

したがって、ローカライズ可能なファイルに次のようなものがある場合:

main.view.title = "Title test";

あなたは簡単に呼び出すことができます:

"main.view.title".localized

そして、あなたはあなたの文字列を翻訳してもらいます。

1
Pincha

IOS 13では、ユーザーはアプリ固有の言語を選択できます。アプリでのみ言語を選択する機能を提供したい場合は、アプリ内の設定を開いて言語を選択する機能を提供できます。 https://developer.Apple.com/videos/play/wwdc2019/403/

0
Manish Nahar

_Apple guidelines_によれば、これはプログラムでアプリの言語を変更することはお勧めできませんが、要求された動作を変更する権限がない場合は、次のようなことを行うことができます。

  1. アプリを再起動した後でも言語を管理するためのサービスを準備する

    _enum LanguageName: String {
        case undefined
        case en
        case es
    }
    
    let DynamicLanguageServiceDidDetectLanguageSwitchNotificationKey = "DynamicLanguageServiceDidDetectLanguageSwitchNotificationKey"
    
    func dynamicLocalizableString(_ key: String) -> String {
        return LanguageService.service.dynamicLocalizedString(key)
    }
    
    class LanguageService {
    
        private struct Defaults {
            static let keyAppleLanguage = "AppleLanguages"
            static let keyCurrentLanguage = "KeyCurrentLanguage"
        }
    
        static let service:LanguageService = LanguageService()
    
        var languageCode: String {
            get {
                return language.rawValue
            }
        }
    
        var currentLanguage:LanguageName {
            get {
                var currentLanguage = UserDefaults.standard.object(forKey: Defaults.keyCurrentLanguage)
                if let currentLanguage = currentLanguage as? String {
                    UserDefaults.standard.set([currentLanguage], forKey: Defaults.keyAppleLanguage)
                    UserDefaults.standard.synchronize()
                } else {
                    if let languages = UserDefaults.standard.object(forKey: Defaults.keyAppleLanguage) as? [String] {
                        currentLanguage = languages.first
                    }
                }
                if let currentLanguage = currentLanguage as? String, 
                    let lang = LanguageName(rawValue: currentLanguage) {
                    return lang
                }
                return LanguageName.undefined
            }
        }
    
        func switchToLanguage(_ lang:LanguageName) {
            language = lang
            NotificationCenter.default.post(name: NSNotification.Name(rawValue: DynamicLanguageServiceDidDetectLanguageSwitchNotificationKey), object: nil)
        }
    
        private var localeBundle:Bundle?
    
        fileprivate var language: LanguageName = LanguageName.en {
            didSet {
                let currentLanguage = language.rawValue
    
                UserDefaults.standard.set([currentLanguage], forKey:Defaults.keyAppleLanguage)
                UserDefaults.standard.setValue(currentLanguage, forKey:Defaults.keyCurrentLanguage)
                UserDefaults.standard.synchronize()
    
                setLocaleWithLanguage(currentLanguage)            
            }
        }
    
        // MARK: - LifeCycle
    
        private init() {
            prepareDefaultLocaleBundle()
        }
    
        //MARK: - Private
    
        fileprivate func dynamicLocalizedString(_ key: String) -> String {
            var localizedString = key
            if let bundle = localeBundle {
                localizedString = NSLocalizedString(key, bundle: bundle, comment: "")
            } else {
                localizedString = NSLocalizedString(key, comment: "")
            }
            return localizedString
        }
    
        private func prepareDefaultLocaleBundle() {
            var currentLanguage = UserDefaults.standard.object(forKey: Defaults.keyCurrentLanguage)
            if let currentLanguage = currentLanguage as? String {
                UserDefaults.standard.set([currentLanguage], forKey: Defaults.keyAppleLanguage)
                UserDefaults.standard.synchronize()
            } else {
                if let languages = UserDefaults.standard.object(forKey: Defaults.keyAppleLanguage) as? [String] {
                    currentLanguage = languages.first
                }
            }
    
            if let currentLanguage = currentLanguage as? String {
                updateCurrentLanguageWithName(currentLanguage)
            }
        }
    
        private func updateCurrentLanguageWithName(_ languageName: String) {
            if let lang = LanguageName(rawValue: languageName) {
                language = lang
            }
        }
    
        private func setLocaleWithLanguage(_ selectedLanguage: String) {
            if let pathSelected = Bundle.main.path(forResource: selectedLanguage, ofType: "lproj"),
                let bundleSelected = Bundle(path: pathSelected)  {
                localeBundle = bundleSelected
            } else if let pathDefault = Bundle.main.path(forResource: LanguageName.en.rawValue, ofType: "lproj"),
                let bundleDefault = Bundle(path: pathDefault) {
                localeBundle = bundleDefault
            }
        }
    }
    _
  2. いくつかのルールを追加して、UIコンポーネントが常に更新されるようにします。

    _protocol Localizable {
        func localizeUI()
    }
    _
  3. それらを実装する

    _class LocalizableViewController: UIViewController {
    
        // MARK: - LifeCycle
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            NotificationCenter.default.addObserver(self, selector: #selector(self.localizeUI), name: NSNotification.Name(rawValue:DynamicLanguageServiceDidDetectLanguageSwitchNotificationKey), object: nil)
        }
    
        override func viewWillAppear(_ animated: Bool) {
            super.viewWillAppear(animated)
    
            localizeUI()
        }
    
        deinit {
            NotificationCenter.default.removeObserver(self)
        }
    }
    
    extension LocalizableViewController: Localizable {
        // MARK: - Localizable
    
        func localizeUI() {
            fatalError("Must Override to provide inApp localization functionality")
        }
    }
    _
  4. 動的アプリスイッチ機能に準拠したいコントローラーを継承し、localizeUI() funcを実装します

    _final class WelcomeTableViewController: LoadableTableViewController
    _
  5. 必要に応じて言語を切り替えます。

    _LanguageService.service.switchToLanguage(.en)
    _
  6. ローカライズ可能な文字列はすべて次のように設定する必要があります。

    _label.text = dynamicLocalizableString(<KEY_IN_STRINGS>)
    _

注:LanguageNameと同じコードで_Localizable.strings_を追加することを忘れないでください

enter image description here

0
gbk