UIView のローカライズ作業を簡単にする : Objective-C プログラミング
PROGRAM
UIView のローカライズ作業を簡単にする
iPhone プログラミングでは、ローカライズ機能を使って簡単に、各国の言語に対応できるようになっています。
やりかたとしては、View の情報を格納した NIB ファイルや Storyboard を Xcode の "File Inspector" を使って "Localization" に対応させたい言語を登録してあげることで、View の設計を各言語ごとに作成して、実行時には環境に合わせて自動的にそれらが選択されるようになります。
ただ、このようにローカライズ自体はとても簡単なのですけど、各言語ごとに行わなければいけない設計が、とても大変なように感じます。
完成後にローカライズを設定して、UILabel 等の表示内容を各言語に翻訳して行くならまだいいのですけど、変更後にレイアウトを修正したり、新たなオブジェクトを配置したりするようなときに、全ての言語ごとに対しても、同じように配置修正や IBAction の設定等をして行かないといけません。
そこで、もっと手軽にローカライズできないものかと考えてみた結果、良さそうな方法が見つかりました。
UILabel や UITextField といったオブジェクトの派生クラスを作成して、そこで、設定された文字列を勝手にローカライズして保持する方法を取ると、とても原始的な Strings ファイルによるローカライズによって、View の表示についてもローカライズできるようになりました。
この方法ならレイアウトに関与しないため、レイアウト設計を 1 ファイルだけで済ませることができ、NIB や Storyboard をローカライズして調整するのと比べて、断然楽になるように思います。
派生クラスを活用して UIView をローカライズする
基本的な考え方としては、UILabel や UITextField であれば、それらの派生クラスを作成して、テキストを設定する際に NSLocalizedString マクロを使って、設定値を自動的にローカライズするという感じです。
また、Interface Builder で NIB や Storyboard のインスタンスを設計することが多いでしょうから、awakeFromNib メソッドをオーバーライドして、そこで Interface Builder で設定されたテキストを、改めて NSLocalizedString マクロを通してローカライズするようにします。
そのように実装した派生クラスを、あとは Interface Builder で貼り付けたラベルやテキストの "Class" として登録することで、Strings ファイルによるローカライズが可能になります。
UILabel のローカライズ実装
UILabel の表示テキストを、暗黙的に Strings ファイルを使ってローカライズさせたい場合は、次のような実装になりました。
EzLocalizedLabel.h
#import <UIKit/UIKit.h>
@interface EzLocalizedLabel : UILabel
@end
ヘッダーファイルについては、派生元を UILabel にするくらいで、そのほかに何かを実装する必要はなさそうでした。
EzLocalizedLabel.m
@implementation EzLocalizedLabel
- (void)awakeFromNib
{
// Nib からロードされた直後に、設定されているテキストをローカライズします。
[self setText:self.text];
}
- (void)setText:(NSString*)text
{
// 渡された文字列をローカライズして設定します。
[super setText:NSLocalizedString(text, nil)];
}
このように実装して、Interface Builder で UILabel を張り付けた際に、Class としてこのクラスを指定してあげることで、ラベルに設定したテキストが自動的にローカライズされるようになりました。
UITextField のローカライズ実装
UITextField の場合、入力してもらった文字列を勝手にローカライズするということも通常はおかしな話ですから、何も入力されていないときに表示される Placeholder テキストを、Strings ファイルで暗黙的にローカライズする感じでしょうか。
EzPlaceholderLocalizedTextField.h
#import <UIKit/UIKit.h>
@interface EzPlaceholderLocalizedTextField : UITextField
@end
ヘッダーファイルについては、派生元を UITextField にするくらいで、そのほかに何かを実装する必要はなさそうです。
EzPlaceholderLocalizedTextField.m
@implementation EzPlaceholderLocalizedTextField
- (void)awakeFromNib
{
// Nib からロードされた直後に、設定されているプレイスホルダーのテキストをローカライズします。
[self setPlaceholder:self.placeholder];
}
- (void)setPlaceholder:(NSString*)placeholder
{
// 渡された文字列をローカライズして設定します。
[super setPlaceholder:NSLocalizedString(placeholder, nil)];
}
このように実装して、Interface Builder で UITextField を張り付けた際に、Class としてこのクラスを指定してあげることで、プレイスホルダーに設定したテキストが自動的にローカライズされるようになりました。
UIButton のローカライズ実装
UIButton の表示文字もローカライズしたかったのですけど、これまでと同じように UIButton から派生クラスを作成して "setTitle:forState:" を調整しても、表示テキストをローカライズできませんでした。
厳密には、UIButton の派生クラスを作成して、特に何も実装しない状態でも、なぜかボタンの淵や、タップされた時のテキストが消えてしまいました。また、"setTitle:forState:" をオーバーライドしても、表示テキストがローカライズされない感じです。
このため、UIButton の表示テキストをローカライズしたい場合には、今のところは手間になってしまいますけど、それが配置されている UIViewController 等で、"setTitle:forState:" メソッドを使用して、手作業でローカライズ文字列を設定してあげる必要がありそうです。
それか、ボタン程度の短いテキストですから、可能であれば、見ればすぐにその用途が分かるような英語を使って、ローカライズせずに済ますのも有かもしれないですね。
UITableView のローカライズ実装
動的配置するセルは、暗黙的にローカライズする必要はなさそうです。
UITableView の場合、セルを動的配置するのであれば、セル内のテキストは UITableViewController で必ず制御することになるので、暗黙的にローカライズを行う必要は特にはないでしょう。
静的配置するセルは、初期値のみ自動でローカライズする形を採ってみます。
静的配置をする場合については、Interface Builder での設計の際に "Table View Section" というものを使用しますけど、この中で UITableViewCell の Class を変更できるようになっているので、それを差し替える形で実装するのが良さそうです。
UITableViewCell の実際の値は、それに配置されている textLabel と detailTextLabel の text プロパティで行いますけど、"Table View Section" の "UITableViewCell" では、それらの Class を変更することができないので、その配置元の "UITableViewCell" の派生クラスを作成します。
EzLocalizedTableViewCell.h
#import <UIKit/UIKit.h>
@interface EzLocalizedTableViewCell: UITableViewCell
@end
ヘッダーでは、派生元を UITableViewCell とします。
EzLocalizedTableViewCell.m
@implementation EzLocalizedTableViewCell
- (void)awakeFromNib
{
// textLabel と detailTextLabel の初期設定値をローカライズします。
self.textLabel.text = NSLocalizedString(self.textLabel.text, nil);
self.detailTextLabel.text = NSLocalizedString(self.detailTextLabel.text, nil);
}
textLabel や detailTextLabel の text プロパティに直接代入されたときのローカライズはできませんけど、もともと静的配置されているセルですから、そこまでは制御しなくても良いでしょう。
その後にプログラムから値を変更するのであれば、その時にローカライズしたテキストを設定すればいいですしね。
セクションヘッダーとフッターの値は UITableViewController でローカライズします。
UITableView のセクションヘッダーやセクションフッターの値は、その値を取得する際に呼び出される UITableViewController の "tableView:titleForHeaderInSection:" メソッドと "tableView:titleForFooterInSection:" メソッドをオーバーライドして、暗黙的にローカライズを行うのが良さそうです。
EzSectionLocalizedTableViewController.h
#import <UIKit/UIKit.h>
@interface EzSectionLocalizedTableViewController : UITableViewController
@end
ヘッダーでは、派生元を UITableViewController とします。
EzSectionLocalizedTableViewController.m
@implementation EzLocalizedTableViewCell
- (NSString*)tableView:(UITableView*)tableView titleForHeaderInSection:(NSInteger)section
{
// 派生元が返そうとするヘッダーテキストを、ローカライズして返します。
return NSLocalizedString([super tableView:tableView titleForHeaderInSection:section], nil);
}
- (NSString*)tableView:(UITableView*)tableView titleForFooterInSection:(NSInteger)section
{
// 派生元が返そうとするフッターテキストを、ローカライズして返します。
return NSLocalizedString([super tableView:tableView titleForFooterInSection:section], nil);
}
動的に生成する UITableView であれば、その都度ローカライズしてテキストを設定すればいいので、わざわざこのようなクラスを作成する必要もないと思いますけど、Interface Builder での静的生成で "Table View Section" を使用する場合には、Interface Builder で UITableViewController の Class をこのように実装したクラスに変えることで、Interface Builder で設定したセクションテキストが自動的にローカライズされて表示されるようになります。
設定値を暗黙的にローカライズするクラスに差し替える
Interface Builder で、上記で作成した "設定値を暗黙的にローカライズするクラス" に差し替えたい場合には、Interface Builder の "Identitiy Inspector" を使って行います。
ここで、目的のオブジェクトを配置・選択したら、"Identitiy Inspector" の "Class" のところで、上で作成したクラスの中から、元のオブジェクトに適した派生クラスを選択します。
あとは、通常通りに、UILabel であれば text の値を、UITextField であれば placeholder の値を Interface Builder で設定すれば、実行時には、そこに設定された値が自動的にローカライズされて表示されるようになると思います。
なお、上記で紹介したような独自クラスを静的ライブラリ内で実装した場合には、その静的ライブラリをリンクする際に "-ObjC" オプションを付けてリンクする必要があるようでした。
この設定方法については 静的ライブラリ内のクラスを Interface Builder で利用できない のところで触れていますので、必要に応じてそちらのほうもご覧ください。
[ もどる ]