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を呼び出したときに自動で呼ばれます。
以上です。
見よう見まねで作ったので、なにか不備等あれば教えてください。