cocos2d-xで多言語対応させたい場合、iOSのNSLocalizedStringのようなクラスが欲しくなるのだけど、そうしたクラスは標準では用意されていないようだ。
そこで自前で用意する。
【cocos2dx】アプリをローカライズさせて海外にも売り込もう! | albatrus.com
こちらのブログを参考にさせて頂きました。
ありがとうございます。
ただ、ここに記述されたコードでは、ローカライズ対象の文章に ” を含めたい場合に、上手くいかない場合がある。
たとえば "ABC"
のように、""
で囲ったテキストを使いたい場合。
Localizable.stringsに "key" = ""ABC"";
と記述したいところだけど、これだと取り出されるテキストは ABC となってしまう。
"key" = "value";
のvalueの両端にある"
はカットされてしまうのだ。
これでは都合が悪かったので、上記のコードを元に自前で作成してみたコードが以下。
CCLocalizedString.h
class CCLocalizedString
{
public:
static void init();
static const char *localizedString(const char* searchKey, const char* comment);
};
CCLocalizedString.cpp
#include "CCLocalizedString.h"
#include "cocos2d.h"
#include <map>
USING_NS_CC;
using namespace std;
static map<string, string> localizable;
static bool initialyzed = false;
void CCLocalizedString::init() {
if(initialyzed) {
return;
}
initialyzed = true;
// 言語ディレクトリを指定する
string file;
ccLanguageType lang = CCApplication::sharedApplication()->getCurrentLanguage();
switch (lang) {
case kLanguageJapanese:
file = "ja.lproj";
break;
default:
file = "en.lproj";
break;
}
file += "/Localizable.strings";
// ファイルパス
string fullPath = CCFileUtils::sharedFileUtils()->fullPathForFilename(file.c_str());
// ファイルデータ取得
unsigned long fileSize = 0;
unsigned char * fileContents = CCFileUtils::sharedFileUtils()->getFileData( fullPath.c_str( ) , "rb", &fileSize );
if (fileContents == NULL) {
return;
}
string line, subStr;
bool isComment = false;
// 1行ずつ解析
istringstream fileStringStream( (char*)fileContents );
while ( getline( fileStringStream, line ) ) {
// 先頭と末尾のスペースを除去
line.erase(0, line.find_first_not_of(" \t"));
line.erase(line.find_last_not_of(" \t") + 1);
if (!isComment) {
// //で始まる行はコメント
subStr = line.substr(0, 2);
if (subStr.compare("//") == 0) {
continue;
}
// /* で始まる行は複数行コメント
else if (subStr.compare("/*") == 0) {
isComment = true;
}
}
// 複数行コメント中
string::size_type len = line.length();
if (isComment) {
// 末尾が */ で終わっているならコメント終了
if (len >= 2) {
subStr = line.substr(len - 2);
if (subStr.compare("*/") == 0) {
isComment = false;
}
}
continue;
}
// 最初の = でキーと値に分割する
string::size_type pos = line.find_first_of('=', 0);
if (pos != string::npos) {
string keyStr = line.substr(0, pos);
string subStr = line.substr(pos + 1, len - 1);
// キーを囲む " を除去("の外側にあるスペース等も同時に除去)
pos = keyStr.find_first_of("\"");
if (pos != string::npos) {
keyStr.erase(0, pos + 1);
}
pos = keyStr.find_last_of("\"");
if (pos != string::npos) {
keyStr.erase(pos);
}
// 値を囲む " を除去("の外側にあるスペースや;等も同時に除去)
pos = subStr.find_first_of("\"");
if (pos != string::npos) {
subStr.erase(0, pos + 1);
}
pos = subStr.find_last_of("\"");
if (pos != string::npos) {
subStr.erase(pos);
}
// \nを改行コードに
do {
pos = subStr.find("\\n");
if (pos != string::npos) {
subStr.replace(pos, 2, "\n");
}
else {
break;
}
}
while (true);
// \"を"に
do {
pos = subStr.find("\\\"");
if (pos != string::npos) {
subStr.replace(pos, 2, "\"");
}
else {
break;
}
}
while (true);
localizable.insert(pair<string, string>(keyStr,subStr));
}
}
delete [] fileContents;
fileContents = NULL;
}
const char *CCLocalizedString::localizedString(const char* searchKey, const char* comment)
{
init();
map<string, string>::iterator itr = localizable.find(string(searchKey));
if (itr != localizable.end()) {
return (itr->second).c_str();
}
return comment;
}
使い方はalbatrus.comさんのコードと同じです。
仕様としては、
-
Localizable.stringsを1行ずつパースします。
1行ごとに"key" = "value";
で記述してください。
-
各行の先頭と末尾のスペースはカットします。
-
コメントアウトに対応しています。
// で始まる行と、 /* で始まる行から */ で終わる行までを無視します。
-
keyとvalueは、最初の = で分割します。
なので、keyに=を含めることはできません。
valueには = を含めても問題ありません。
-
末尾の ; は有っても無くても同じです。
keyとvalueを""
で囲む必要もありません。
"key" = "value";
(標準)も、"key" = "value"
(末尾に;なし)も、key = value
(""
で囲まない)もkey=value
(=の前後にスペース無し)もすべて同じです。
-
"
はそのまま使えます。
"key" = ""ABC"";
も"key" = "ABC"EFG";
も問題ありません。
-
"
をそのまま使うのが気持ち悪い人は\"
にも対応しています。
"key" = "\"ABC\"";
や"key" = "ABC\"EFG";
のように。
-
事前にパースしておきたいケースもあるだろうと、CCLocalizedString::init()を用意しました。
明示的に使わない場合は、最初にCCLocalizedString::localizedStringを呼び出したときに自動で呼ばれます。
以上です。
見よう見まねで作ったので、なにか不備等あれば教えてください。