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


140

私は(他のすべてと同様に)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は不要)、および自動リファクタリングのための一意のキーワードを提供します。ただし、これは、#define本質的に構造化されていない(つまり、LocString.Common.Cancelなどの)ステートメントの集合全体を犠牲にすることになります。

ですから、これはいくらかうまく機能しますが、あなたのプロジェクトでどのようにそれを行うのかと思っていました。NSLocalizedStringの使用を簡略化する他のアプローチはありますか?それをカプセル化するフレームワークさえあるのでしょうか?


私はあなたと同じようにそれをします。しかし、私はNSLocalizedStringWithDefaultValue makroを使用して、さまざまなローカリゼーションの問題(コントローラー、モデルなど)のためにさまざまな文字列ファイルを作成し、初期デフォルト値を作成しています。
anka

xcode6のExport to localizationは、ヘッダーファイルでマクロとして定義されている文字列をキャッチしないようです。誰かが私が欠けているかもしれないものを確認したり教えたりできますか?ありがとう...!
ジャッドスター2014

@Juddster、確認できます。新しいファンドのEditor-> Export for Localizationでもヘッダーファイルでピックアップされません
Red

回答:


100

NSLocalizedStringにはいくつかの制限がありますが、Cocoaの中心であるので、ローカリゼーションを処理するカスタムコードを書くのは不合理です。つまり、ローカリゼーションを使用する必要があります。とは言っても、小さなツールが役立ちます。ここに私が進む方法があります:

文字列ファイルの更新

genstrings文字列ファイルを上書きし、以前のすべての翻訳を破棄します。私が書いたupdate_strings.pyを古い文字列ファイル、実行解析するためにgenstrings、手動で既存の翻訳を復元する必要がないように空白でフィルを。スクリプトは、既存の文字列ファイルを可能な限り厳密に照合して、更新時に差分が大きくなりすぎないようにします。

文字列に名前を付ける

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

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

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

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

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

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

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


あなたの答えをありがとう、私は間違いなくあなたのpythonファイルを見ていきます。あなたの命名規則に同意します。最近、他のiOS開発者と話しましたが、彼らはマクロではなく静的文字列の使用を推奨しており、これは理にかなっています。私はあなたの回答を賛成しましたが、解決策がまだ少し不器用なので、受け入れる前に少し待ちます。たぶんもっと良いものがやって来るでしょう。再度、感謝します!
JiaYow 2012

いいえ、どいたしまして。ローカリゼーションは退屈なプロセスであり、適切なツールとワークフローを用意することで、世界を変えることができます。
ndfred 2012

17
gettextスタイルのローカリゼーション関数が翻訳の1つをキーとして使用する理由を私は理解していません。元のテキストが変更された場合はどうなりますか?キーの変更とローカライズされたすべてのファイルで、キーに古いテキストが使用されています。それは私には意味がありませんでした。私は常に「home_button_text」のようなキーを使用してきたので、それらは一意であり、決して変更されません。すべてのLocalizable.stringsファイルを解析し、適切な文字列をロードする静的メソッドを含むクラスファイルを生成するbashスクリプトも作成しました。これでコードが完成しました。ある日、これをオープンソースにするかもしれません。
マイクウェラー

2
そうではgenstringsないと思いますgestring
hiroshi 2013

1
@ndfredのコンパイル時チェックでは、文字列を間違って入力していないかどうかを確認することが最大の利点です。とにかく追加するコードはわずかに多くなります。また、リファクタリング、静的分析の場合、シンボルがあると、物事がとても簡単になります。
Allen Zeng

31

Xcodeの文字列のオートコンプリートに関しては、https://github.com/questbeat/Linを試すことができます


3
これは実際にはかなり驚くべきことです。マクロを作成する必要はありません。
Beau Nouvelle


1
@Juanmiリンク切れについて言及していただきありがとうございます。リンクをgithub urlに置き換えました。
ヒロシ

24

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

3.使用してください!

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」のkey ==値なので、カスタムLocalizedStringHandlerはコメント、つまり「Settings」を返します

ボイラ:-)


ARCエラーの取得、セレクタ 'localizedString:comment ::(
Mangesh

LocalizationHandlerUtil.hがないためだと思います。コードを見つけることができません...ヘッダーファイルLocalizationHandlerUtil.hを作成してみてください。問題はありません
Pascal

ファイルを作成しました。フォルダパスの問題が原因だと思います。
Mangesh 14

3

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を変更すると、新しい翻訳テキストが追加されると理解されますが、これは常に望ましいとは限りません。


2

Localizable.stringsを複数の言語で維持するのに役立つスクリプトを書きました。オートコンプリートには役立ちませんが、コマンドを使用して.stringsファイルをマージするのに役立ちます。

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

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

皆さんの中には、それが役立つと思う人もいます。


2

Swiftソリューションをお探しの方。私がここにまとめた私のソリューションをチェックしたいかもしれません:SwiftyLocalization

設定するいくつかの手順で、Googleスプレッドシートで非常に柔軟なローカリゼーション(コメント、カスタムカラー、ハイライト、フォント、複数のシートなど)ができます。

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

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

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

  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を使用してシートを変換します-> CSV、Pythonスクリプトを使用してCSVファイルを変換します-> Localizable.stringsこのサンプルシートをざっと見て、何が可能かを知ることができます。


1

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

Apple doc:ベースのローカリゼーション


はいスティーブは正しいです。また、動的に生成される文字列には.stringsファイルメソッドが必要です。ただし、これらの場合にのみ、Appleが推奨する方法はベースローカリゼーションです。
ロニーウェバーズ2014

新しいメソッドにリンクしますか?
双曲線2014

1
基本のローカリゼーションメソッドであるImoは無意味です。動的文字列用に他の場所ファイルを保持する必要があり、それによって文字列が多くのファイルに分散されます。Nibs / Storyboards内の文字列は
Rafael Nobre

0
#define PBLocalizedString(key, val) \

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

0

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

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

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"

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.