WP-Weatherプラグインの日本語化

当ブログのテーマを変えたついでに、ウェジットプラグインで使っているお天気情報表示プラグイン「WP-Weather」を日本語化してみました。このプラグインは国際化に対応しているので、ある程度はリソースだけを弄るだけで日本語化できます。

大雑把な手順は以下の通り。(ちなみに訳等の作業はWindowsマシンで行っています)

  1. wp-weather-en_US.poをwp-weather-ja.poにコピーし、作業マシンに引っ張ってきます。
  2. POEdit(作者さんのサイト)でこのファイルを開き、対訳を変更していきます。
  3. 対訳の変更が終わったら、カタログを作成します。(wp-weather-ja.moができあがります)
  4. wp-weather-ja.moファイルをlocalizationディレクトリにコピーします。

上記の方法で固定文字列が日本語化されます。しかし、私は欲張って日付の書式を日本式にしたり、動的に変わるお天気なども日本語化したくなり・・・「はまりました・・・orz icon_cry.gif

このプラグインの流れは、「更新→weather.comに問い合わせ→wordpress DBのデータ更新→データ表示」となるわけです。当方は、「DBはUTF-8だし、wordpressやプラグインで使用する文字列もUTF-8なので問題ないだろう」と高をくくり、がしがしと動的に変わる文字列をpoファイルに登録・対訳し、ソースを変更していきました。そして、更新~。
「・・・、対訳が出てこねぇ~ icon_evil.gif
ソース文字列をHexコードでチェックして見るも、特に変なコードが入っているわけでもなし。「う~ん、う~ん」と呻きながら小一時間。phpの文字列操作関数群を眺めながら、「おやっ、mb_~()?、もしやマルチバイト・・・」。
ここまで足突っ込んじゃっているし、とりあえずマルチバイト系文字列に変換してみるか~とゴソゴソソースを修正&更新~。
「うぉ、対訳出てきやがった icon_eek.gif
う~ん、文字列はかなり癖あるなぁ~と。(当方の知識不足は棚上げしておきます、ぉぃ)

DBのデータを一旦マルチバイト文字列に変換し、その文字列を変換用検索文字列として渡します。
変更例は以下のよう感じ。

$htmlstring .= '<br />'.__(mb_substr($xml->cc->t, 0, mb_strlen($xml->cc->t) ,'UTF-8'), 'wp-weather').'<br/>';

また、日付を海外書式(Monday, May 23)から日本書式(5月23日 月曜日)へ変換したい場合の例はこんな感じ。

$htmlstring .= date_i18n('n月j日 l', strtotime($day_forecast->lsup));

上記の例の基のデータ($day_forecast->lsup)はデータベースを覗くと[ 5/23/11 2:13 PM Local Time ]な感じなんですが、date_i18n()とstrtotime()関数の組み合わせは結構融通が利くみたい。例えば[ May 24 ]をstrtotime()に通した後、date_i18n()に通すと思い通りの書式で出力してくれる。もし、ソースを見る機会があったら覗いて見ようかと。ここまで融通が利くと結構バグがあるかもしれんしね。(ぉぃ、疑う前に知識身につけろ・・・ごもっとも)
注:date_i18n()関数はwordpress固有の関数ですのでご注意を。

さて最後に
まだ日本語化終わっていません、キリッ

と言うのも、お天気の種類がいくつあるのか分からないので、出たとこ勝負で訳しています。
作者に聞いてみようと思うので、進捗があったら更新します。
それと、この変更はまだ作者に了解を取って行っているわけではないのでファイルは公開できません。上記の問い合わせとあわせて聞いてみますので、これも進捗があったら更新します、あしからず。

可変構造データの扱い

興味本位でWindowsでOpenLDAPのデータでも操作してみようかな~とプログラミングを開始してみた。基のソースはMicrosoft MSDNのライブラリからパクってきたのだが。(興味がある方はリンクにアクセスしてね)

で、いきなりこのソースの注意点ですが、構造体:SEC_WINNT_AUTH_IDENTITYのPasswordは素のパスワードでは通用しないと思われ、HASH化した文字列が必要と思われます。パスワードのHASH化はslappasswdで得ることが可能ですので調べてくださいな。素のパスワードでアクセスしたい場合は、ldap_simple_bind_s()もしくはldap_simple_bind()を利用してくださいな。

さて、順調にこのソースを咀嚼していき、何とか情報を引き出し表示させるまでに。そこで、表示されているデータをジッと見つめていると、アトリビュート(属性)数は可変だし、配下のデータ数も可変。
さて、どうやってローカルに取り込もうか思いあぐねた結果、構造体+std::vectorにすることに。理由は、vectorを利用することで可変データに対応するため。(無駄なメモリ使いたくないし、解放操作も面倒だしw)
構造体は下記のような入れ子。(データ構造は本文最後の方で図を掲載してあります)

struct LDAP_ITEM
{
	string					attr;		// 属性名
	vector<string>		item;	// 属性配下のアイテム(vectorで可変に)
};

struct LDAP_DATA
{
	int						entrynum;	// エントリ番号
	vector<LDAP_ITEM>	data;		// エントリデータ内容(上記入れ子構造体をvectorで可変に)
};

で、次にどうやってこの構造体にデータをぶち込んでいるかは以下のソース。Microsoftのソースコードを基にしているので何を追加・変更しているかは大体察しが付くと思いますが、簡単にコメントも入れてみました。
ソースが長いので、「ソースを表示」で伸張してくださいな。

	//----------------------------------------------------------
	// Get the number of entries returned.
	//----------------------------------------------------------
	ULONG numberOfEntries;

	numberOfEntries = ldap_count_entries(pLdapConnection, pSearchResult);

	if(numberOfEntries == NULL)
	{
		printf("ldap_count_entries failed with 0x%0lx \n",errorCode);
		ldap_unbind_s(pLdapConnection);
		if(pSearchResult != NULL)
			ldap_msgfree(pSearchResult);
			return -1;
	}
	else
		printf("ldap_count_entries succeeded \n");

	printf("The number of entries is: %d \n", numberOfEntries);

	//----------------------------------------------------------
	// Loop through the search entries, get, and output the
	// requested list of attributes and values.
	//----------------------------------------------------------
	LDAPMessage* pEntry = NULL;
	PCHAR pEntryDN = NULL;
	ULONG iCnt = 0;
	char* sMsg;
	BerElement* pBer = NULL;
	PCHAR pAttribute = NULL;
	PCHAR* ppValue = NULL;
	ULONG iValue = 0;

	// コメント:入れ子構造体データの一時的な保管場所。
	// これをあとで下の元データの一時的な構造体にpush_back。
	LDAP_ITEM		tmp_item;
	// コメント:元データの一時的な保管場所。あとで実データにpash_back。
	LDAP_DATA		tmp_ldap;

	for( iCnt=0; iCnt < numberOfEntries; iCnt++ )
	{
		// Get the first/next entry.
		if( !iCnt )
			pEntry = ldap_first_entry(pLdapConnection, pSearchResult);
		else
			pEntry = ldap_next_entry(pLdapConnection, pEntry);

		// Output a status message.
		sMsg = (!iCnt ? "ldap_first_entry" : "ldap_next_entry");
		if( pEntry == NULL )
		{
			printf("%s failed with 0x%0lx \n", sMsg, LdapGetLastError());
			ldap_unbind_s(pLdapConnection);
			ldap_msgfree(pSearchResult);
			return -1;
		}

		tmp_ldap.entrynum = iCnt;    // コメント:エントリ番号を保存。

		// Get the first attribute name.
		pAttribute = ldap_first_attribute(pLdapConnection, pEntry, &pBer);

		// Output the attribute names for the current object
		// and output values.

		while(pAttribute != NULL)
		{

			// コメント:一時的なアイテム保管場所。
			//                   アイテムがいくつあるか分からないのでvector使用。
			vector<string>		tmpItem;
			// コメント:属性を保存。
			tmp_item.attr = pAttribute;    

			// Get the string values.
			ppValue = ldap_get_values(pLdapConnection, pEntry, pAttribute);

			// Print status if no values are returned (NULL ptr)
			if(ppValue == NULL)
			{
				printf(": [NO ATTRIBUTE VALUE RETURNED]");
			}

			// Output the attribute values
			else
			{
				iValue = ldap_count_values(ppValue);
				if(!iValue)
				{
					printf(": [BAD VALUE LIST]");
				}
				else
				{
					tmpItem.push_back(*ppValue);    // コメント:最初のアイテムを保存。

					// Output more values if available
					ULONG z;
					for(z=1; z<iValue; z++)
					{
						// コメント:残りのアイテムがあれば保存。
						tmpItem.push_back(ppValue[z]);
					}
					// コメント:push_backされてきたアイテムを入れ子構造体の
					//                   一時保管場所構造体データに。
					tmp_item.item = tmpItem;
					// コメント:一時的なアイテム保管場所のvector要素を消去。
					//                 (解放ではないですよ。かつ<vector>.clear()でもない)
					tmpItem.erase(tmpItem.begin(), tmpItem.end());
				}
			}
			// コメント:直前で入れ子の一時データができているので、
			//                   一時的な本営データ保管場所にpush_back。
			//                   なぜここで本営データに入れないかは本文で。
			tmp_ldap.data.push_back(tmp_item);    

			// Free memory.
			if(ppValue != NULL)
				ldap_value_free(ppValue);
			ppValue = NULL;
			ldap_memfree(pAttribute);

			// Get next attribute name.
			pAttribute = ldap_next_attribute(pLdapConnection, pEntry, pBer);
		}
		// コメント:本営データにpush_back。
		ldap_data->push_back(tmp_ldap);
		// コメント:本営データに入れたので一時データの要素を消去し、for続きへ。
		tmp_ldap.data.erase(tmp_ldap.data.begin(), tmp_ldap.data.end());    

		if( pBer != NULL )
			ber_free(pBer,0);
		pBer = NULL;
	}

	//----------------------------------------------------------
	// Normal cleanup and exit.
	//----------------------------------------------------------
	ldap_unbind(pLdapConnection);
	ldap_msgfree(pSearchResult);
	ldap_value_free(ppValue);
	return 0;

上のソースで、アイテムが全て揃ったところで、なぜ本営データにぶち込んでいないかと言えば、まだ属性データがあった場合、属性ごとに本営データができあがってしまい、意図する動作にならないためです。つまり、「1つのエントリに対し、複数の属性、1つの属性に対し複数のアイテム」といった可変データ(1xNxN)構造と微妙に異なるからなんです。

で、ここまできたところで、「ldap_data」ってどこで宣言してるんじゃ~ってお怒りかも知れませんが、本営データはこの関数に引数として渡ってきます。

int	GetLDAPData(const char* pHost, std::vector<LDAP_DATA> *ldap_data)

では、この関数の呼び元と出力事例のソースは以下の通りでやんす。
ソースが長いので、「ソースを表示」で伸張してくださいな。

	// 本営データ作成(ここではメモリ確保もせずほったらかし)
	vector<LDAP_DATA>	ldap;
	// 本営データのアドレスを引数で渡してあげます
	GetLDAPData(ldap_host.c_str(), &ldap);

	// データがあったら表示を開始します
	if(!ldap.empty())
	{
		// 本営データに対するイテレータを作成しポインターもどきにします
		for(vector<LDAP_DATA>::iterator iarray = ldap.begin(); iarray<ldap.end() ; iarray++)
		{
			cout << "ENTRY NUMBER: " << iarray->entrynum << endl;
			// 入れ子構造体データに対するイテレータを作成しポインターもどきにします
			for(vector<LDAP_ITEM>::iterator iattr = iarray->data.begin(); iattr<iarray->data.end() ; iattr++)
			{
				cout << "	ATTR: " << iattr->attr << ":";
				// アイテムデータに対しイテレータを作成しポインターもどきにします
				for(vector<string>::iterator iitem = iattr->item.begin(); iitem<iattr->item.end() ; iitem++)
				{
					cout << " " << *iitem;
					if(iitem<iattr->item.end()-1)
						cout << ",";
				}
				cout << endl;
			}
		}
	}

データ表示部分で、本営、入れ子構造体、アイテムに対しそれぞれイテレータを作成していますが、こうしておけば独立した形(表現は厳密ではないけれど)で各データにアクセス出来ます。配列カウンターで悩むことがないので便利なんです。まぁ、配列ポインタと変わりはないんですけど、プログラムを書く好みでdata[i][j][k]とか好きな方ではないので。OpenLDAPのデータ構造はこんな感じ。

OpenLDAPデータ構造

足早にざっと書いては見たけれど、OpenLDAPのアクセスなんて誰もやろうなんて考えないよね。と言うことで備忘録ということで。

DVDに焼くのはいいけれど・・・

親戚の家に遊びに行ったときのお話。
家人曰く、「このDVD、DVDプレイヤーで見れないんだよ。」
当方、ダビングされた円盤を見て絶句しながら、「・・・でしょうね」
家人曰く、「何とか見られるようにならない?」
当方曰く、(やっぱりきたかと思いつつ)「ちょっとお借りしていきますね」

親戚のDVDプレイヤーで見ることができない理由は、DVDプレイヤーが以下3つに対応していないから。

  • DVD-RAM(DVD-VR形式)に対応していない
  • CPRM形式に対応していない
  • DVD-RWの未ファイナライズに対応していない(というか別プレイヤーだとデータ認識できないはず)

とりあえず、持ち帰って当方のPCで形式をDVD-V形式に再変換して焼き直し~。

デキシージャズライブのビデオなんだが、多分主催者がビデオテープからDVDレコーダーを通して円盤にダビングしたんだろうけれど、たまたま手元にあった適当な円盤に焼いたもんだからこんなことになったんでしょう。で、焼いた本人は再生を確認しているだろうけれど、ここが問題。焼いたレコーダーで再生を確認したところで、再生できるのは当たり前で、別のプレイヤーで確認しなきゃ再生確認したことにならない。

というか、見えない作成者に、
「焼く前にレコーダーの取説読め」
と、小一時間問い詰めたい・・・w

まぁ、私も買ってきたハードの取説読まないけれど・・・ぉぃ

また煽りか

放射能物質での土壌汚染記事で基準値の何倍~(元記事)と書かれていますが、土の採取方法の注釈が無い。
「飯舘村で採った土1キロあたりから、セシウムが~」と「放射線管理区域の基準値は1平方メートルあたり4万ベクレル」が記されているが、1キログラム採取した方法は、その土地を1平方メートルに区切って均等に採取したのか?
母集団の標本採取方法が分からないのに比較して、果たしてこれが正しい情報なのか?
多分言いたいことは、後半の「長期間、土壌が汚染されることにより、人体や農作物などに影響が出る可能性がある。 」のくだりだろうけれど、人づてに広がる場合、「やばいよ、何倍なんだって~」となる。
単にセンセーショナルな記事書いて、煽るだけ煽って、言いっぱなし。それで国民の代弁者でござい~って、誰かを非難してドヤ顔。
重箱の隅をつつくようで悪いのだが、非常時だからこそ情報のデッドラインを死守しなきゃいけないんじゃないのかな~。
Page 2 of 2812345...1020...Last »