web-dev-qa-db-ja.com

NSLocalizedStringを使用したベストプラクティス

私は(他のすべてと同様に)NSLocalizedStringを使用してアプリをローカライズしています。

残念ながら、いくつかの「欠点」があります(必ずしもNSLocalizedString自体の障害ではありません)。

  • Xcodeの文字列の自動補完はありません。これにより、エラーが発生しやすいだけでなく、面倒な作業も可能になります。
  • 同等の文字列が既に存在することを知らなかったという理由だけで、文字列を再定義することになります(つまり、「パスワードを入力してください」対「パスワードを最初に入力してください」)
  • オートコンプリートの問題と同様に、コメント文字列を「記憶」/コピーペーストする必要があります。そうしないと、genstringが1つの文字列に対して複数のコメントになります
  • すでにいくつかの文字列をローカライズした後にgenstringを使用する場合、古いローカリゼーションを失わないように注意する必要があります。
  • プロジェクト全体に同じ文字列が散在しています。たとえば、どこでもNSLocalizedString(@"Abort", @"Cancel action")を使用した後、コードレビューにより、コードの一貫性を高めるために文字列の名前をNSLocalizedString(@"Cancel", @"Cancel action")に変更するように求められます。

私が行うこと(およびSOを検索した後、多くの人がこれを行うと考えた)は、すべてのローカライズコードをstrings.hする別個の#defineファイルを持つことです。例えば

// In strings.h
#define NSLS_COMMON_CANCEL NSLocalizedString(@"Cancel", nil)
// Somewhere else
NSLog(@"%@", NSLS_COMMON_CANCEL);

これは本質的に、コード補完、変数名を変更するための単一の場所(そのため、もはやgenstringを必要としない)、および自動リファクタリングするための一意のキーワードを提供します。ただし、これは、本質的に構造化されていない(つまり、LocString.Common.Cancelなどの)#defineステートメントの束で終わるという代償を伴います。

だから、これはいくぶんうまく機能するが、私はあなたたちがあなたのプロジェクトでどのようにそれをするのかと思っていた。 NSLocalizedStringの使用を簡素化する他のアプローチはありますか?多分それをカプセル化するフレームワークもありますか?

133
JiaYow

NSLocalizedStringにはいくつかの制限がありますが、Cocoaの中心であるため、ローカライズを処理するカスタムコードを記述するのは不合理です。つまり、使用する必要があります。とはいえ、小さなツールが役立つ場合がありますが、ここに私が進める方法があります:

文字列ファイルを更新する

genstringsは文字列ファイルを上書きし、以前の翻訳をすべて破棄します。 pdate_strings.py と書いて、古い文字列ファイルを解析し、genstringsを実行し、空白を埋めて、既存の翻訳を手動で復元する必要がないようにしました。スクリプトは、既存の文字列ファイルをできるだけ密接に一致させて、更新時に大きすぎるdiffを作成しないようにします。

文字列に名前を付ける

NSLocalizedStringを宣伝どおりに使用する場合:

NSLocalizedString(@"Cancel or continue?", @"Cancel notice message when a download takes too long to proceed");

コードの別の部分で同じ文字列を定義することになりますが、同じ英語の用語が異なるコンテキストで異なる意味を持つ可能性があるため、競合する可能性があります(OKCancelが思い浮かびます)。そのため、モジュール固有のプレフィックスと非常に正確な説明を含む無意味な全大文字文字列を常に使用します。

NSLocalizedString(@"DOWNLOAD_CANCEL_OR_CONTINUE", @"Cancel notice window title when a download takes too long to proceed");

異なる場所で同じ文字列を使用する

同じ文字列を複数回使用する場合は、マクロを使用するか、View Controllerまたはデータソースのインスタンス変数としてキャッシュすることができます。この方法では、同じローカリゼーションのインスタンス間で古くなったり矛盾したりする可能性のある説明を繰り返す必要がなく、常に混乱を招きます。インスタンス変数はシンボルであるため、これらの最も一般的な翻訳でオートコンプリートを使用し、特定の文字列に「手動」文字列を使用できます。

これらのヒントを使用して、Cocoaローカリゼーションの生産性を高めてください。

93
ndfred

Xcodeの文字列の自動補完については、 http://questbe.at/lin/ を試すことができます。

30
hiroshi

Ndfredには同意しますが、これを追加したいと思います。

2番目のパラメーターは...デフォルト値として使用できます!!

(NSLocalizedStringWithDefaultValueはgenstringで適切に動作しないため、このソリューションを提案しました)

デフォルト値としてコメントを使用するNSLocalizedStringを使用するカスタム実装は次のとおりです。

1。事前にコンパイルされたヘッダー(.pchファイル)で、「NSLocalizedString」マクロを再定義します:

// cutom NSLocalizedString that use macro comment as default value
#import "LocalizationHandlerUtil.h"

#undef NSLocalizedString
#define NSLocalizedString(key,_comment) [[LocalizationHandlerUtil singleton] localizedString:key  comment:_comment]

2。ローカライズハンドラーを実装するクラスを作成する

#import "LocalizationHandlerUtil.h"

@implementation LocalizationHandlerUtil

static LocalizationHandlerUtil * singleton = nil;

+ (LocalizationHandlerUtil *)singleton
{
    return singleton;
}

__attribute__((constructor))
static void staticInit_singleton()
{
    singleton = [[LocalizationHandlerUtil alloc] init];
}

- (NSString *)localizedString:(NSString *)key comment:(NSString *)comment
{
    // default localized string loading
    NSString * localizedString = [[NSBundle mainBundle] localizedStringForKey:key value:key table:nil];

    // if (value == key) and comment is not nil -> returns comment
    if([localizedString isEqualToString:key] && comment !=nil)
        return comment;

    return localizedString;
}

@end

。使用してください!

アプリのビルドフェーズにRunスクリプトを追加して、Localizable.stringsファイルがビルドごとに更新されるようにしてください。つまり、Localized.stringsファイルに新しいローカライズされた文字列が追加されます。

私のビルドフェーズスクリプトはシェルスクリプトです。

Shell: /bin/sh
Shell script content: find . -name \*.m | xargs genstrings -o MyClassesFolder

したがって、この新しい行をコードに追加すると、次のようになります。

self.title = NSLocalizedString(@"view_settings_title", @"Settings");

次に、ビルドを実行すると、。/ Localizable.scriptsファイルに次の新しい行が含まれます。

/* Settings */
"view_settings_title" = "view_settings_title";

そして、「view_settings_title」のキー==値なので、カスタムLocalizedStringHandlerはコメント、つまり「設定」を返します

Voilà:-)

24
Pascal

Swiftでは、以下を使用しています。この場合の「はい」ボタンの場合:

NSLocalizedString("btn_yes", value: "Yes", comment: "Yes button")

デフォルトのテキスト値のvalue:の使用に注意してください。最初のパラメーターは変換IDとして機能します。 value:パラメータを使用する利点は、デフォルトのテキストを後で変更できるが、翻訳IDは同じままであるということです。 Localizable.stringsファイルには"btn_yes" = "Yes";が含まれます

value:パラメーターが使用されなかった場合、最初のパラメーターは、翻訳IDとデフォルトのテキスト値の両方に使用されます。 Localizable.stringsファイルには"Yes" = "Yes";が含まれます。この種のローカライズファイルの管理は奇妙に思えます。特に、翻訳されたテキストが長い場合、IDも長くなります。デフォルトのテキスト値の文字が変更されるたびに、翻訳IDも変更されます。これは、外部翻訳システムを使用する場合に問題につながります。翻訳IDの変更は、新しい翻訳テキストを追加することと理解されますが、これは常に望ましいとは限りません。

3
petrsyn

Localizable.stringsを複数の言語で維持するためのスクリプトを作成しました。自動補完には役立ちませんが、コマンドを使用して.stringsファイルをマージするのに役立ちます:

merge_strings.rb ja.lproj/Localizable.strings en.lproj/Localizable.strings

詳細については、 https://github.com/hiroshi/merge_strings を参照してください

あなたの何人かはそれが役に立つと思います。

2
hiroshi

私自身は、多くの場合、エントリを.stringsファイルに入れるのを忘れて、コーディングに夢中になります。したがって、.stringsファイルに戻して翻訳する必要があるものを見つけるためのヘルパースクリプトがあります。

NSLocalizedStringで独自のマクロを使用しているため、使用する前にスクリプトを確認して更新してください NSLocalizedStringの2番目のパラメーターとしてnilが使用されていると仮定しました。変更したい部分は

NSLocalizedString\(@(".*?")\s*,\s*nil\) 

マクロとNSLocalizedStringの使用法に一致するものに置き換えてください。

ここにスクリプトがあります。実際に必要なのはパート3だけです。残りは、すべてがどこから来たのかを簡単に確認することです:

// Part 1. Get keys from one of the Localizable.strings
Perl -ne 'print "$1\n" if /^\s*(".+")\s*=/' myapp/fr.lproj/Localizable.strings

// Part 2. Get keys from the source code
grep -n -h -Eo -r  'NSLocalizedString\(@(".*?")\s*,\s*nil\)' ./ | Perl -ne 'print "$1\n" if /NSLocalizedString\(@(".+")\s*,\s*nil\)/'

// Part 3. Get Part 1 and 2 together.

comm -2 -3 <(grep -n -h -Eo -r  'NSLocalizedString\(@(".*?")\s*,\s*nil\)' ./ | Perl -ne 'print "$1\n" if /NSLocalizedString\(@(".+")\s*,\s*nil\)/' | sort | uniq) <(Perl -ne 'print "$1\n" if /^\s*(".+")\s*=/' myapp/fr.lproj/Localizable.strings | sort) | uniq >> fr-localization-delta.txt

出力ファイルには、Localizable.stringsファイルではなく、コードで見つかったキーが含まれています。サンプルを次に示します。

"MPH"
"Map Direction"
"Max duration of a detailed recording, hours"
"Moving ..."
"My Track"
"New Trip"

確かにもっと磨くことができますが、共有したいと思いました。

Swiftソリューションを探している場合。私がここにまとめた私のソリューションを確認することをお勧めします:SwiftyLocalization

いくつかの設定手順を行うだけで、Googleスプレッドシートで非常に柔軟なローカライズが可能になります(コメント、カスタムカラー、ハイライト、フォント、複数シートなど)。

要するに、手順は次のとおりです。Googleスプレッドシート-> CSVファイル-> Localizable.strings

さらに、Localizables.Swiftも生成します。これは、キーの取得とデコードのインターフェイスのように動作する構造体です(キーからStringをデコードする方法を手動で指定する必要があります)。

なぜこれが素晴らしいのですか?

  1. あらゆる場所に単純な文字列としてのキーが必要なくなりました。
  2. コンパイル時に間違ったキーが検出されます。
  3. Xcodeはオートコンプリートを実行できます。

ローカライズ可能なキーをオートコンプリートできるツールがありますが。実際の変数への参照は、それが常に有効なキーであることを保証します。そうでなければ、コンパイルされません。

// It's defined as computed static var, so it's up-to-date every time you call. 
// You can also have your custom retrieval method there.

button.setTitle(Localizables.login.button_title_login, forState: .Normal)

このプロジェクトは、Google App Scriptを使用してSheets-> CSVを変換し、Pythonスクリプトを使用してCSVファイルを変換します-> Localizable.strings.

0
aunnnn
#define PBLocalizedString(key, val) \

[[NSBundle mainBundle] localizedStringForKey:(key) value:(val) table:nil]
0
baozhifei

iOS 7およびXcode 5では、「Localization.strings」メソッドの使用を避け、新しい「base localisation」メソッドを使用する必要があります。 「ベースローカリゼーション」をグーグルで検索する場合、いくつかのチュートリアルがあります

Apple doc: ベースのローカライズ

0
Ronny Webers