アプリケーション言語を即座に変更

金融分野での私の2番目のアプリケーションである、これまで無料の通貨コンバーターを開発する過程で得られた興味深い経験についてお話したいと思います。 最初のMoney iQは、小さな会社で働いている間に書かれたもので、ロシアのApp Storeで1位を獲得することさえできました。 興味深い場合は、アプリケーションの作成に関する少しの開発ストーリーを別のブログで少し公開しますが、この記事では、アプリケーション内の言語の即時変更などの問題について説明します。

実際、問題。


おそらく、多くは多言語アプリケーションに対処しなければならなかったでしょう。 iOS向けのアプリケーションだけでなく、一般に複数の言語をサポートするアプリケーションについても話します。 これらのうち、設定には「Language / Idioma」という項目があり、ユーザーが必要とする言語を設定できます。

異なる状況では、このオプションの動作は異なります。 一部のアプリケーションでは、新しい言語をインストールするためにそれらを再起動する必要があります。 一部では、すべてが即座に発生します。 iOS用のアプリケーションを作成するときに2番目のアプローチを実装する方法については、この記事で説明します。

Appleが提供するもの。


厳密に言えば、Appleは、アプリケーションが電話のデフォルトに設定されているロケールを使用することを推奨しています。 ロシア人はロシア語を使用する可能性が高く、ロシア語でアプリケーションを見たいと思うため、これは非常に合理的です。

ただし、これは常に機能するとは限りません。 一部のアプリケーションでは、翻訳は理想からかけ離れています-単語がボタンに収まらない、不変のプロンプトを使用して翻訳されたように見えない、またはテキストを翻訳したが写真をローカライズしなかっただけで迷惑です。 アプリケーション全体を見て、英語を入れて楽しんでみたいです。 一部はこの機会を提供します、そして、私は彼らがそれをする方法を正確に理解することを提案します。

アプリケーションがロケールを設定する方法。

ここではすべてが簡単です。 NSBundle-ローカライズされたアプリケーションリソースのセットなどがあります。 アプリケーションにru.lprojという形式のディレクトリが含まれていて、電話のロケールがru_RUに設定されている場合、たとえば
[ [ NSBundle mainBundle ] loadNibNamed @ "xib_name" ... ]
最初に、ru.lprojディレクトリで対応するリソースを見つけようとします。うまくいかない場合は、ルートにあるデフォルトのリソースを返します。

次。 複数の言語をサポートするアプリケーションは、NSLocalizedStringを使用する可能性が高くなります。 この構成-NSLocalizedString(@ "string"、@ "comment")は、
[ [ NSBundle mainBundle ] localizedStringForKey @ "string" value @ "" table nil ]

ロケールをデフォルト以外に変更する場合はどうしますか? この問題を解決する比較的一般的な方法は、ユーザーが言語を選択した後にアプリケーションを強制終了し、次回起動時にNSBundleがリソースをロードするデフォルトのロケールを変更することです。 このようなもの:

main.m:
[ [ NSUserDefaults standardUserDefaults ] setObject [ NSArray arrayWithObjects @ "ru_RU"nil ]
forKey @ "AppleLanguages" ] ;
[ [ NSUserDefaults standardUserDefaults ] synchronize ] ;

@autoreleasepool {
return UIApplicationMain argc、argv、 nil 、NSStringFromClass [ YourApp class ] ;
}

以下、MAはMyAppを表すプレフィックスであり、隠された意味はありません:)

最終的にロケールは宣言されたロケールに変更されるため、このソリューションは最悪のものではありませんが、何が起こるかはユーザーフレンドリーではありません-生産コストです。

ただし、たとえば、Booking.comの優れたアプリケーション(まったく広告されていない-アプリケーションは非常に優れています)のように、アプリケーションを再起動せずにロケールが変更されるアプリケーションにますます気づきます。 ある時点で、私はそれがすべてどのように機能するかを調べ始めました。

準備作業


最初の段階。

アプリケーションをローカライズするための準備については詳しく説明しませんが、このトピックについて多くの記事が書かれています。 最初の段階で、デフォルトのアプリケーションロケールを正しく設定し、次のように記述すれば、あなたの人生がずっと楽になることを強調します。
NSLocalizedString @ "string"@ "comment"

シンプルではなく
@ 「文字列」

知らない人のために、NSLocalizedStringの2番目のパラメーターは、 genstringsコマンドによって生成された新しいローカリゼーションファイルに自動的に追加されるコメントです。 とても助かります

第二段階。

ローカライズマクロを呼び出すときに、mainBundleではなく、指定したローカライズリソースを含む「カスタム」バンドルを使用するようにします。

これを行うには、新しいシングルトンMALocalizationSystemを作成し(シングルトンの実装はobjc Googleおよび現在流行しているdispatch_onceに残します);メソッドを追加します:
+ MALocalizationSystem * sharedLocalizationSystem;
- NSString * localizedStringForKey NSString * キー値 NSString * コメント;
- void setLanguage NSString * 言語;
- NSString * getLanguage;

メソッドの実装はスリッパと同じくらい簡単です:
static MALocalizationSystem * _sharedLocalizationSystem = nil ; //シングルトンインスタンス
静的 NSBundle * bundle = nil ; //現在のバンドル。 initメソッドの値[NSBundle mainBundle]で初期化する
静的 NSString * _currentLanguage = nil ; //現在の言語

- NSString * localizedStringForKey NSString * キー値 NSString * コメント
{
return [ bundlelocalizedStringForKey key value comment table nil ] ;
}

- void setLanguage NSString * lang
{
if _currentLanguage && [ lang isEqualToString _currentLanguage ]
{
帰る
}

NSString * path = [ [ NSBundle mainBundle ] pathForResource lang ofType @ "lproj" ] ;
_currentLanguage = lang;

if path == nil
{
[self resetLocalization ] ; //ローカライズファイルが見つかりませんでした-_currentLanguageをnilにリセットし、[NSBundle mainBundle]にバンドルします
}
他に
{
bundle = [ NSBundle bundleWithPath path ] ;
}

//ここで、必要に応じて、ロケールの変更の成功に関する通知を送信できます。
[ [ NSNotificationCenter defaultCenter ] postNotificationName kLocalizationChangedNotificationオブジェクト nil ] ;
}

- NSString * getLanguage
{
if _currentLanguage
{
NSArray * languages = [ [ NSUserDefaults standardUserDefaults ] objectForKey @ "AppleLanguages" ] ;
_currentLanguage = [ languages objectAtIndex 0 ] ;

NSString * path = [ [ NSBundle mainBundle ] pathForResource _currentLanguage ofType @ "lproj" ] ;

if path == nil
{
[ self resetLocalization ] ;
_currentLanguage = @ "en" ; //アプリケーションのデフォルト言語。
}
}

return _currentLanguage;
}

そして、便宜上いくつかのマクロを定義します。
#define MALocalizedString(キー、コメント)
[ [ MALocalizationSystem sharedLocalizationSystem ] localizedStringForKey key value comment ]

#define MALocalizationSetLanguage(言語)
[ [ MALocalizationSystem sharedLocalizationSystem ] setLanguage :(言語 ]

#define MALocalizationGetLanguage
[ [ MALocalizationSystem sharedLocalizationSystem ] getLanguage ]

翻訳のすべては多かれ少なかれ明確です-MALocalizationSetLanguage( "eo")を使用して言語を設定し、NSLocalizedStringの代わりにMALocalizedStringを使用した場合は、インストールされた言語が使用されます。 リソースについてはどうでしょうか。たとえば、写真やその他のファイルはどうでしょうか? そして、ここから始まります...

第三段階。

それにもかかわらず、Appleは特定のローカライズフォルダーからリソースをダウンロードしたい人の面倒をみました。 xmlファイルから通貨名のリストをロードする場合、通常、次のように実行されます。
NSString * pathToFile = [ [ NSBundle mainBundle ] pathForResource @ "currencyNames"
ofType @ "xml" ] ;
cachedCurrencyNames = [ NSMutableArray arrayWithContentsOfFile pathToFile ] ;

しかし、別の方法があります。

NSString * pathToFile = [ [ NSBundle mainBundle ] pathForResource @ "currencyNames"
ofType @ "xml"
inDirectory nil
forLocalization @ "ru" ] ;
cachedCurrencyCodes = [ NSMutableArray arrayWithContentsOfFile pathToFile ] ;

キャッチ? :)

要約すると:

kLocalizationChangedNotificationイベントをサブスクライブし、ローカライズされたリソース/タグ/画像を更新するのは、各View Controllerのみです。 このため、このすべてを1つまたは複数のメソッドに収集し、awakeFromNibの間に(それらを)呼び出し、この非常に良い通知kLocalizationChangedNotificationを受信すると便利です。

結論の代わりに。


iOSのフォロワーに誤解されたくない-アプリケーションの便利な使用のためにユーザーのアクションを最小化するAppleのアプローチが好き 同時に、私が上で説明したことがこのスキームから何らかの形でノックアウトされたとは思いません。 これは、アプリケーションがデフォルトでシステム言語を選択する場合の通常のアプローチです。その後、設定内のユーザーは「ノイズやほこりなし」に変更する機会を与えられます(c)。

読んでくれてありがとう!

参照資料


ここからのコードが取得され、わずかに書き直され/補足/修正されました。

Source: https://habr.com/ru/post/J138263/


All Articles