WordPress のリビジョンについての間違った認識と正しい対処

WordPress のリビジョン機能は冷遇されているようで検索すれば無効化する方法がたくさん見つかります。 一方でこの機能が「使える」ものだったらやっぱり使いたいよなあ、という人も多いのではないでしょうか。 ブログ記事は書き捨てというやり方だけでなく、注目を集め反応が多かった記事に情報を追記していく場合もあるでしょう。 また、WordPress のビジネス利用も増えていますが、そうであればなおさらリビジョン管理は重要です。 そこで今回は現行のリビジョン機能の何が問題でどうすれば解決するかをまとめたいと思います。

まあ、リビジョン機能止めて別に困らないという人はそれでいいのですけれど。

まずは正しい認識を

まず、WordPress のリビジョン機能について正しい認識を持ちましょう。 あまり理解されていないように思われるのは以下の点です。

  • 自動保存は 1世代のみ
  • リビジョンが作成されるのはポストテーブル (wp_posts) 内容のみ。 wp_postmeta や wp_term_relationships などの関連テーブルは (プラグイン等を用いない限り) リビジョンが作成されない。

つまり、デフォルトの 60秒間隔自動保存でバンバンリビジョンが増えていくというわけではないです。 また、ポストテーブルのリビジョンのみ、すなわちカテゴリーやタグの付け替え履歴は残らないので、WordPress のリビジョン機能は、基本的に記事にしたテキストデータの変更履歴を取るための機能と捉えるのが良いと思います。

データベース関連についても一言言っておきます。

  • 通常の一記事表示ではインデックスを使用した検索になるので、ポスト数が増えても検索時間は比例しない。 なので、個人ブログレベルであれば、リビジョンを作成したからといって (よほど作りの悪いテーマやプラグインを使っていない限り) パフォーマンスに影響が出ることはまずない。

まあ、データベースの容量に関しては確かにリビジョンを作成することでその分使用領域は増えます。 しかし、所詮テキストデータなので、画像に比べればたかが知れています。

あっ、まさかリビジョンを作成するとそのページで使った画像まで何世代も保管されちゃう、なんて思っていたりします? 念のために言っておきますが、それはないです。 イメージデータそのものはデータベースに入らないですし。

もう一度書きますが、リビジョンが作成されるのはポストテーブルの内容のみです。 ポストテーブルの内容を知らなければこちらで確認しておきましょう。 タグやカテゴリーの情報はポストテーブルに入っておらず、別テーブルから post_id を使ってポストテーブルを参照するようになっています。

でもリビジョン多くね?

と、ここまで読んでも「実際には多数リビジョンが出来てるし、嘘書いてんじゃねーの?」と疑っている人もいるでしょう。 確かにリビジョンが多く作成されてしまうのは事実です。 これは WordPress の以下の動作によるものです。

  1. 下書きの状態 (公開前も含む) でプレビューをするたびにリビジョンが作成される。
  2. クイック編集や一括編集でもリビジョンが作成される。

1. については思い当たる方が多いのではないでしょうか? 公開前はプレビュー操作を行って最終的な見栄えをチェックしながら文章を調整することがあると思いますが、そこでプレビューする度にリビジョンができてしまうのです。公開された記事のプレビュー操作では自動保存リビジョンとなるのでどんなにプレビューしても 1世代なのですが、公開前だと何世代ものリビジョンができてしまいます。 たいていの場合は公開初版のリビジョンだけあればよいですよね。

2 については更に不要です。 このような編集方法を採るときはタグやカテゴリーを直す場合が多いと思うのですが、そうなるとポストテーブルの項目に変更は発生しないことが多いにも関わらずリビジョンが作成されてしまうということになってしまいます。 ですので、リビジョン比較をしても「同じ内容です」というちょっと悲しいリビジョンばかりできてしまいます。

どうすりゃいいのさ?

さて、これらの不要なリビジョンを保存せずに、意味のあるリビジョンだけ残すにはどうすれば良いのでしょう? ちょっと考えてもどうしようもなさそうですね。 でも簡単な解決策があるんです。 それは…。

(縦に長い空白地帯)

じゃーん。 リビジョン管理用のプラグイン「Thin Out Revisions」を使えばよいのです。 以下の手順で不要なリビジョンとおさらば!

  1. あなたのサイトのプラグイン検索画面から Thin Out Revisions を検索してインストール、有効化する
  2. 「設定」-「Thin Out Revisions」とメニューを選び 3項目全て ON にして保存する

えっ? 作者が日本人っぽい? どっかで見たアイコン?

…実は私がつくったのです。

ステマではありません。作者による告知です! それとこんな機能もあります。 気に入ったら是非つぶやいたり、「いいね!」したりしてください。お願いします! お願いします!

(後半雰囲気を変わってしまいましたが、同じ著者によるものです。セッティング項目の見出しは一応読んでね。)

WordPress で簡単に OGP の設定を行う方法

Open Graph Protocol (OGP) についての記事は小難しいものが多いように見えたので触らないでおいたのですが、とりあえず対応するにはセマンティックな meta タグを埋め込むぐらいの話のようなので、これは対応せねばと思いました。 今回は WordPress を OGP に対応させるためにしたことをまとめます。

Facebook for WordPress プラグインを使うのがお手軽

OGP 対応で一番簡単なのは公式プラグインである Fackbook for WordPress をインストールすることです。 インストールしただけで特に設定をしなくても OGP 対応タグを記事中に埋め込むようになります。 「いいね」ボタン等の機能は無効にしておくことができ、埋め込む App ID の指定もできます。 公式プラグインという安心感があるので OGP 対応が目的の場合も Facebook for WordPress を選択するのが、良いように思います。

私の場合、App ID は今回初めて取得しました。 まだ、十分に調べることが出来ていませんが、App ID を記事に埋め込むことで様々な機能が有効になるので、きちんと設定しておくべきです。

以下 Facebook のサイト上で行うアプリ設定についての話です。 記事に埋め込んで機能させるためには、その記事を公開するサイトのドメイン (ここなら hetarena.com) を App Domains として設定しておかねばなりません。 また、App Domains に設定するためには、そのアプリと Facebook 統合方法でその URL が使われていなければなりません。 何にも設定しないと App Domains 登録でエラーになるので、例えば「Website with Facebook Login」のサイト URL として https://hetarena.com/ を設定しておきます。 こうすることで App Domains にも hetarena.com を設定できるようになります。

できるだけシェイプアップしたい

本当に簡単に済ませるのであればここまでで、ここから先はこだわりの話です。 OGP 対応のみが目的のとき、Facebook for WordPress の出力する余分な JavaScript が記事に埋め込まれるのが気になります。 そこで、Facebook for WordPress のソースを見てみると、OGP 対応は open-graph-protocol.php というファイルの Facebook_Open_Graph_Protocol というクラスだけを使えば済むことに気が付きました。 app_id と locale は呼び出し元クラスの情報を使っているのですが、以下のように強引に書き換えてしまいます。 my app id は自分 App ID です。 locale については特にデバッガーもエラーを吐かないので、省略しました。

--- a/includes/open-graph-protocol.php
+++ b/includes/open-graph-protocol.php
@@ -121,12 +121,7 @@ class Facebook_Open_Graph_Protocol {
                        self::OGP_NS . 'type' => 'website'
                );

-               if ( isset( $facebook_loader ) ) {
-                       if ( isset( $facebook_loader->locale ) )
-                               $meta_tags[ self::OGP_NS . 'locale' ] = $facebook_loader->locale;
-                       if ( isset( $facebook_loader->credentials ) && isset( $facebook_loader->credentials['app_id'] ) && $facebook_loader->credentials['app_id'] )
-                               $meta_tags[ self::FB_NS . 'app_id' ] = $facebook_loader->credentials['app_id'];
-               }
+               $meta_tags[ self::FB_NS . 'app_id' ]  = 'my app id';

                if ( is_home() || is_front_page() ) {
                        $meta_tags[ self::OGP_NS . 'title' ] = get_bloginfo( 'name' );

あとは functions.php に以下を入れます。 open-graph-protocol.php へのパスは適切に書きましょう。

require_once( '/path_to_ogp/open-graph-protocol.php' );
add_action( 'wp_head', array( 'Facebook_Open_Graph_Protocol', 'add_og_protocol' ) );

これで余分な出力はなくなり、OGP の meta タグのみ出力されるようになりました。

…とここまで書いてアレなのですが、埋め込まれている JavaScript に重要な働きがあることがわかりました。 fb_xd_fragment がログに残る件がらみなのですが、詳細は別記事でご確認ください。 そういうわけでこのようにクラスを切り出して使うのはお勧めしません。 Facebook for WordPress をそのまま使いましょう。 (この段落は 2012/12/4 追記)

アイキャッチイメージを記事に設定する

Facebook for WordPress (Facebook_Open_Graph_Protocol クラス) の動作では、記事に設定された「アイキャッチ画像」の情報を image プロパティとして埋め込みます。 Facebook のデバッガーによれば縦横 200px 以上のイメージをきちんと設定しておくことが推奨とのことです。 これまでアイキャッチ画像は使っていなかったので、新たに 200px 四方のロゴをアップロードして全ての記事のアイキャッチ画像として設定することにします。

(2013/1/20 追記: もっと手軽な方法がありました。)

まず、記事に見える形で表示したくないので、テーマファイルから以下の行を削除しました。

<?php the_post_thumbnail(); ?>

続いて記事へのアイキャッチ画像の設定ですが、面倒なので SQL で実行することにしました。 以下の SQL 文はアイキャッチ画像が設定されていない post/page に対して ID 654 の画像をアイキャッチ画像として設定するためのものです。

START TRANSACTION;

INSERT INTO wp_postmeta (post_id, meta_key, meta_value)
SELECT id, '_thumbnail_id', 654
FROM wp_posts
WHERE post_type IN ('page', 'post')
AND NOT EXISTS
(SELECT * FROM wp_postmeta
 WHERE meta_key = '_thumbnail_id'
 AND wp_postmeta.post_id = wp_posts.id);

COMMIT;

何か失敗したときに Rollback ができるようにトランザクションを有効にして実行しています。 これで image プロパティも meta タグとして埋め込まれるようになりました。 これらを実行する時は、画像 ID だけでなくテーブル名も自分の環境に合わせてください。 マルチサイト環境ならば wp_2_postmeta、wp_3_postmeta、… となりますね。

これで「簡単な」なのかという声が聞こえそうですが、やっている内容に比べたら簡単にやっているのではないでしょうか…。(苦しい)

WordPress 用プラグイン Standard Widget Extensions を公開しました

WordPress のウィジェットの動作を拡張するためのプラグイン「Standard Widget Extensions」を公開しました。 既にWordPress.org のリポジトリに登録済みです。 私にとって Thin Out Revisionsに続く 2つめの WordPress プラグインです。

現バージョンで提供している機能はウィジェットというよりサイドバーの拡張用という感じですが、以下の内容をお手軽に実現できます。 Twenty Twelve/Twenty Eleven であればデフォルト設定のままで動きます。

  • Accordion Widget (開閉するウィジェット)
  • Sticky Sidebar (スクロールが止まるサイドバー)

スクロール関係については世の中にサンプルやら部品があふれているのですが、境界でぎこちない動作になるものが多く、レスポンシブ Web デザインやサイドバー伸縮との組み合わせを考えると結局自分で作った方がよいと思い開発しました。 ちなみにレスポンシブ Web デザイン対応という点では、ウィンドウ幅が指定された値より小さくなると機能が無効化されます。

開閉動作もありふれてますが、見出しのアイコンを変えたりできるので JavaScript の苦手な人がちょっと使うのには便利だと思います。 独自の CSS は最低限にしてテーマのスタイルをそのまま使用するようにしています。 以下は Twenty Twelve/Twenty Eleven/Twenty Ten に適用した場合の例です。 そのままではマージンが大きいので微妙かも知れませんが、最低限の追加スタイルということを理解していただけると思います。

Standard WE

動作についてはこのサイトの左側にあるサイドバーを見ていただくのが一番早いと思います。 WordPress 標準テーマの Twenty Twelve/Twenty Eleven/Twenty Ten で動作確認していますが、メインカラムとサイドバー、ウィジェットの ID/クラス名を設定することができるので、標準テーマと同じつくりのテーマであれば対応できると思います。 もし、興味があれば、是非 WordPress の管理メニューから検索してインストールしてみてください。

以下、プラグインのホームページです。

SoundCloud と WordPress

SoundCloud にアップロードしたトラックを WordPress のブログ記事に埋め込むには公式の SoundCloud Shortcode プラグインが便利です。 しかし、iOS の Safari でも表示可能なように HTML5 で出力するには気をつけなければならない点があります。 以下手順をまとめます。

SoundCloud プラグインをインストールすると「設定」-「Sound Cloud」でセットアップ画面を表示できるようになります。 まず、この中で Widget Type を HTML5 に選びます。

Setting

しかし、これだけでは不十分です。 リンクの形式は http://soundcloud.com/hetarena-com/saw-square ではなく、http://api.soundcloud.com/tracks/69089521 の形式でなければなりません。 この数字を見つける簡単な方法としては、SoundCloud.com 上の該当トラックで「Share」-「WordPress Code」で示されるコードを確認すれば良いです。

Share Track

「WordPress Code をそのまま貼り付けては?」という声も聞こえてきそうですが、油断すると「iframe="false"」というパラメータが入っていて Flash の埋め込みになってしまいます。 ちなみに私は設定画面で Auto Play = false、Show Comment = false として以下のようなタグコードを埋め込んでいます。

[soundcloud]http://api.soundcloud.com/tracks/66034316[/soundcloud]

というわけで見やすくなったので「ソフトシンセ時代のシンセサイズ入門」を是非ご覧ください。 あと、リバースシンバルの作成例つきの「Groove Agent ONE でマイキットをつくろう」も!

以下の記事を参考にさせていただきました。

Twenty Twelve を使い始める前に知っておきたいこと

Twenty Twelve は近々リリースされる WordPress 3.5 の新しい標準テーマで、3.5 のよりも一足先にリリースされています。 このブログもこの 11月より Twenty Twelve ベースのテーマに衣替えしました。 その中で気付いた点などまとめましたので、Twenty Twelve を使ってみようという方は是非ご一読ください。

Mobile-First Responsive Web Design

Twenty Twelve はレスポンシブ Web デザインを採用しており、更にモバイルファーストのコンセプトで実装されています。 具体的にどういうことかと言うと、CSS のメディアクエリを使ってブラウザのサイズによって表示内容を変えているのですが、スタイルシートファイル (style.css) の中で以下の順でスクリーン用スタイルが定義されています。

  1. 幅の狭いスクリーン用のスタイルをメディアクエリなしで定義
  2. min-width: 600px を指定して幅広いスクリーン用のスタイルを定義
  3. min-width: 960px を指定して更に幅広いスクリーン用のスタイルを定義。ここからは背景が広がるだけ。

つまり一番幅の狭いスクリーン (=モバイルデバイス) 用の定義を最初に行い、スクリーン幅の広いデバイスについては追加でスタイルを足すというやりかたです。 何故、このようにモバイルデバイスを優先するかというのは様々なところ (例えばここここ) で説明されているのでここでは繰り返しません。 注意したいのは PC 用のレイアウトを行うときはメディアクエリの条件式でくくられたスタイルに手を入れなければならないということです。

IE7/IE8 ユーザーは Twenty Twelve Version 1.0 だと悲しいことに

ここでピンと来た方もいらっしゃるかも知れませんが、Internet Explorer の 8 以下のバージョンはメディアクエリをサポートしていません。 なので、Twenty Twelve の Version 1.0 を使ったサイトを IE7/IE8 でブラウジングするとメニュー表示がモバイル用と同じになっちゃいます。 このあたりデモサイトではきちんと表示されるのですが、あれはどうやら手が入っているようです。 普通に Version 1.0 を入れると IE7/IE8 では下のような表示になります。 メニューに注目してください。 (それでも微妙な IE 個別対応はしているので 2カラムレイアウトにはなっています)

Twenty Twelve on IE7/IE8

Twenty Twelve 1.0 をリリースした時点では IE7/IE8 に関してはモバイルデバイスと同等のルックスを提供する方針だったようです。 しかし、私のサイトの場合、閲覧に使われているブラウザの中での IE8 以下バージョンの割合はサイトアクセス全体の 2割強で無視できない数字であり、きちんとしたメニューバーを表示したいところです。 これはグローバルで見ても同様だったようで、 最初の方針は不評を買い結局 IE8/IE7 のナビゲーションメニューがきちんとサポートされることになりました。 WordPress 3.5 公式リリースには反映されるはずですが、すぐに対応が必要な方は既に修正済みとなっている WordPress 3.5β2 の中の Twenty Twelve のみを取り出して使うと良いでしょう。 (3.5β2 そのものを使うことはお勧めしません。もっと言うと多分あなたがこの記事を見ている頃には 3.5 がリリースされているような気がしますw (注: 3.5 は 12/5 リリース予定))

IE6 はさらに悲しい

更にいまだに IE6 を使用されている方々もいます。 そこで手元の IE6 を使ってテストしたところ、Twenty Twelve テーマそのままではフォントの問題で日本語が表示されませんでした。

いまどき IE6 を使っている方々はきっとレイアウトなど気にしないのだろうと推測できますが、さすがに内容が見えるぐらいにはしておきたいところです。 「スターハック」という IE6 のみのスタイルを変える有名なテクニックを使ってフォント指定に ‘MS Pゴシック’ を追加します。 なお、Twenty Twelve のスタイルシートに直接手をいれるのではなく、まずは子テーマを作ってカスタマイズするのがお勧めです。

* html body {
  font-family:  "\FF2D\FF33\20\FF30\30B4\30B7\30C3\30AF", Osaka, Helvetica, Arial, sans-serif;
}

* html body.custom-font-enabled {
  font-family:  "\FF2D\FF33\20\FF30\30B4\30B7\30C3\30AF", Osaka, "Open Sans", Helvetica, Arial, sans-serif;
}

* html .entry-content code,
* html .comment-content code,
* html .entry-content pre,
* html .comment-content pre {
        font-family: "\FF2D\FF33\20\FF30\30B4\30B7\30C3\30AF", Osaka, Consolas, Monaco, Lucida Console, monospace;
}

ついでに Osaka フォントも加えて、(意味がないと思われる) 元のフォント指定も残していますが、この辺は詳しくないし、所詮 IE6 用スタイルということで深く考えないことにします。 また、style.css の先頭に念のため以下の行を入れておきました。

@charset "utf-8";

これで無事 IE6 でも日本語が表示されるようになりました。

フォントサイズ指定について

style.css の中を覗いてみるとフォントサイズの指定には rem が使われています。 これは CSS3 から追加された単位で、root + em すなわち root 要素のフォントサイズに対しての相対値になります。 最初に以下のように指定されているので、1rem = (16px * 87.5%) = 14px となります。 ここで 16px は (最近の主要な) ブラウザのデフォルトフォントサイズです。 このようにユーザー環境のフォントサイズを考慮して絶対値でなく相対値でサイズ指定するのが最近の標準的なやり方のようです。

html {
        font-size: 87.5%;
}

さて、これ以降実際のサイズ指定には 1つの要素に対して下のように px 指定と rem 指定の 2つの指定が行われています。

    font-size: 11px;
    font-size: 0.785714286rem;

このように指定方法の異なる 2行をこの順で書いておけば rem を認識しない古いブラウザは px 値、認識すれば rem 値でスタイリングされます。 rem 値が一瞬よくわからない小数値になっていますが、先の 1rem = 14px で計算した値となっています。 11/14 ということですね。 自分で手を入れるときもこのスタイルを踏襲しましょう。

幅の指定について

もう一つわけのわからない小数値に幅の指定があります。 こんな感じの記述です。

    width: 65.104166667%;

Twenty Twelve では 960px 以上になるとコンテンツの幅は広がらず背景が広がるようになっています。 レスポンシブであるために上のように width が%値で指定されていますが、これは 960px になったときのピクセル値を元に計算された値で、この例では最大表示で (960px x 0.65104166667 = 625px) ということです。

IE 対応

β2 での変更で HTML タグの記述は以下のように出力されます。

<!--[if IE 7]>
<html class="ie ie7" dir="ltr" lang="ja">
<![endif]-->
<!--[if IE 8]>
<html class="ie ie8" dir="ltr" lang="ja">
<![endif]-->
<!--[if !(IE 7) | !(IE 8)  ]><!-->
<html dir="ltr" lang="ja">
<!--<![endif]-->

ここで指定されている .ie/.ie7/.ie8 をセレクタとして使って実際のスタイリングは css/ie.css の中で行われています。 .ie は IE7 と IE8 の 2つに対してのみ適用したいスタイルを指定するときに使います。 先のスターハックと合わせれば IE6/IE7/IE8 向けの個別調整ができますね。 まあ、Mobile First Responsive Web Design の考え方からするとピクセル単位の配置にまでこだわる必要はないと思うので、ほどほどにして良いと思います。

その他

Twenty Twelve は HTML のソースも CSS ファイルも比較的読みやすいと思いますので、気に入ったら是非カスタマイズに挑戦してみてください。 それと、ページ編集画面からテンプレートとして ‘Front Page Template’ や ‘Full-width Page Template, No Sidebar’ が選べるようになっていますが、これらのソースを見ると Widget エリアの使い方などの参考になると思います。

さて、このブログのテーマも Twenty Twelve ベースのものに変えました。 しかし、広告を入れると元があっさりしすぎているのでもう少し線を濃くしたりする等の調整が必要なようです。 また、そもそも英語で見栄えが良くても日本語になっただけで印象が変わってしまう、というのもあります。 まだまだ調整は続く、という感じです。

また、さんざん Mobile First Responsive Web Design と強調した割りに、結局スマートフォン向けは WPtouch Pro の使用を継続しています。 Twenty Twelve のモバイル向け表示時のメニューボタンが気に入らないのと、モバイル回線でのレスポンスがどうなるか見えないのが理由ですが、折を見て一本化できるか検討してみようとは思っています。 ナビゲーションメニューのスタイリングの仕方を覚えねばということですかねぇ。

PHP/WordPress で国際化対応のコードを書く

PHP/WordPress で国際化対応プログラムを書くことがあったので、得た知識をまとめておきます。

gettext について

自分で gettext を使ったことがなくても .mo という拡張子のつくファイルをどこかでみたことはあると思います。 プログラムの中で


_("Enter your name:")

と書いたものを以下のようなエントリを含むソースファイル (.po) から生成した翻訳ファイル (.mo) を使って実行時に翻訳します。


msgid  "Enter your name:"
msgstr "名前を入力してください"

すると最初の _() は以下に翻訳されます。 PHP では _ は gettext 関数のエイリアスとなっており、この関数により文字列が変換されるのです。


"名前を入力してください"

一般的にプログラムで文字列出力時にならないと確定できない部分には printf のようにプレースホルダーを使用可能な関数を用います。 PHP でも printf や sprintf が用意されているのでプレースホルダーつき文字列を引数としてこれらの関数を呼び出すことができますし、以下のように番号つきプレースホルダーを使って順番を変えるテクニックも国際化対応ではよく使われます。


msgid  "Address: %1$s, %2$s"       (US は番地から)
msgstr "住所: %2$s %1$s"       (日本は市区町村から)

Gnu gettext

翻訳ファイルの作成にあたって、私が主に使っているのは Gnu gettext です。 .mo ファイルを作る場合、とりあえず使用する Gnu gettext パッケージのコマンドは以下の通りです。 作業の流れにそって紹介します。

xgettext
ソースファイルから .pot (テンプレートとして使う .po ファイル)
msginit
.pot ファイルから .po ファイルを生成
msgfmt
.po ファイルから .mo ファイルを生成
msgmerge
ソースファイルを修正したときに、修正後に再生成した .pot ファイルと旧 .po ファイルから新 .po ファイルを生成。

実行時に気をつけるのは xgettext のオプション (これは後述) と msgmerge のファイルの指定間違いぐらいで、あとはすんなり使用できると思います。 各コマンドとも出力ファイルは -o で指定できます。 それぞれのコマンドの詳細はここでは取り上げないので、公式マニュアルを参照してください。 Web 検索すれば、.pot ファイルから日本語翻訳ファイルを作る方法は、その他のツールを使う場合含め多くの事例を見つけることができると思います。

msgfmt のデフォルト動作についてちょっと追記。

  • fuzzy とマークされたエントリは無視されるので、内容を確認し fuzzy を消しておく必要がある
  • msgstr が空 ("") のエントリは無視されるのでわざわざ消す必要なし
  • どの翻訳ファイルが使用されるか

    PHP の gettext の場合、翻訳ファイルの存在するディレクトリは bindtextdomain の呼び出しによって「ドメイン」 (通常はアプリケーション名) に結びつけられ、ドメインとロケールの設定から読み出す翻訳ファイルが決まります。 指定されたドメインでベースとなるディレクトリが確定し、指定されたロケールでその配下のどの翻訳ファイルが読み出されるか決まる、ということです。 bindtextdomain で "./lang" ディレクトリを指定したとき、ロケール ja 用の翻訳ファイルをは以下の場所に置きます。 詳しくは gettext のマニュアルの実行例をご覧ください。

    
    ./lang/ja/LC_MESSAGES/modulename.mo
    

    ドメインの指定は textdomain を呼んでセットしておくか、dgettext のようにドメインつきの gettext 系関数を使用します。

    ロケールの変更

    続いてロケールの設定ですが、PHP ではデフォルトのロケール設定は php.ini の ‘intl.default_locale’ の値が使用されます。 これを変えたいときは、Locale::setDefault を呼び出します。 以下はブラウザの設定によりロケールを変更する実行例です。 ブラウザに設定されている言語を HTTP リクエストヘッダーから読み出すのに Locale::acceptFromHttp という関数が用意されています。

    
    if ( !empty( Locale::acceptFromHttp( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) ) ) {
      Locale::setDefault( Locale::acceptFromHttp( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) );
    }
    

    ソースファイルを作成する

    国際化を念頭に置くのであれば最初から文字列は英語で gettext 系関数を用いて書くべきです。 何故最初からかというと、やはり後から国際化する方が手間がかかるからです。 量としてもそうですが、質としても後付け国際化はさほど面白くない作業になると思うので、最初からやっておきましょう。

    そして、何故英語かというと、残念ながら日本語から各言語に翻訳してくれる人は英語から翻訳してくれる人に比べてずっと少ないですし、そもそも ASCII 文字でないとツールが対応していないからです。 嘆いても仕方ないので、意味が伝わることを目標に英語を使って書いてみましょう。 意味さえ通じれば、きっと誰かがまともな英語翻訳ファイルを作ってくれるはずです!

    PHP の gettext とコンテキスト

    PHP で gettext を使うには必要なモジュールを組み込んでおく必要があります。 Fedora であれば php-intl というパッケージが必要でした。 また、Windows 環境では以下の拡張モジュールが必要でした。

    • php_gettext.dll
    • php_intl.dll

    例えば、英語の「YES/NO」と日本語の「はい/いいえ」が全く同じではないことはみなさんご存知だと思います。 それは _("YES") を "はい" と訳す場合と (まれにですが) "いいえ" と訳す場合があるということですが、このような場合 _("YES", "context") のように 2つめの引数を使ってどの訳語とするかの判断に用います。 この 2つめの引数をコンテキスト情報と呼び、多くのプログラミング言語ではコンテキスト対応した i18n 関数ライブラリが用意されています。

    しかし残念なことに PHP 標準の gettext 系関数はではコンテキストつきで翻訳を行う関数が用意されていません。 従って、これをやろうとするとMozilla Developer Network に紹介されているように以下のような自作関数を用いなければなりません。 (この Mozilla Developer Network のページは一読をお勧めします)

    
    function pgettext($context, $msgid) {
        $contextString = "{$context}\004{$msgid}";
        $translation = _($contextString);
        if ($translation == $contextString)  return $msgid;
        else  return $translation;
    }
    
    function ___($message, $context ="") {
        if($context != "") {
            return pgettext($context, $message);
        } else {
            return _($message);
        }
    }
    

    これで ___("YES", "context") という呼び出しができるようになります。 ここで問題となるのは Gnu の xgettext でソースファイルから .pot を作成するときに、デフォルトではこのような独自関数を扱ってくれないということです。 従って、先の関数を使うためには以下のようなオプションをつける必要があります。

    
    --keyword=___:1 --keyword=___:1,2c
    

    これは ___() 関数を抽出対象とし、2つ以上引数を持つ時は、1番目の引数が変換対象文字列、「c」の付いている 2番目めの引数がコンテキストであることを示しています。 –keyword オプションの詳細はxgettext のマニュアルの「Language specific options」の項にあります。 各プログラミング言語ごとの対応関数もそこに一覧として出ています。

    ところで、1点ハマったのですが、理由はよくわかりませんが以下のように書いた部分が xgettext でエラーとなってしまいました。

    
    <noscript><?php echo _("Please turn on JavaScript"); ?></noscript>
    

    以下のようにコードを直すとエラーを回避できました。 どのような条件でエラーになるのか細かく見ていませんが、このようなことで回避できる場合もあるということです。

    
    (php の別の場所で)
    $msg_js_on = _("Please turn on JavaScript");
    (html の部分で)
    <noscript><?php echo $msg_js_on; ?></noscript>
    

    WordPress の gettext

    PHP 標準関数では足りないので、WordPress では以下のように多くの独自に用意された gettext 系関数が用意されています。 また、ロケールの扱いも独自に実装されており、翻訳ファイルを読み込むディレクトリも PHP の gettext と異なります。

    __(), _e(), _n(), _x(), _ex(), _nx(), esc_attr__(), esc_attr_e(), esc_attr_x(), esc_html__(), esc_html_e(), esc_html_x(), _n_noop(), _nx_noop()

    こうして見ると関数の数は多いのですが、以下のように分類できます。

    • e 付きは echo する
    • n 付きは単数形/複数形の違いを扱う
    • x 付きはコンテキストあり
    • esc_attr 付きは attribute として使うためにエスケープする
    • esc_html 付きは HTML テキスト用としてエスケープする
    • noop 付きは翻訳用配列を用意するための関数

    _n_noop()/_nx_noop() についてはちょっとわかりづらいので補足します。 といっても _n_noop() の公式マニュアルの例が全てなのでこれを説明します。

    
    $messages = array(
    	'post' => _n_noop('%s post', '%s posts'),
    	'page' => _n_noop('%s pages', '%s pages')
    );
    ...
    $message = $messages[$type];
    $usable_text = sprintf( translate_nooped_plural( $message, $count, $domain ), $count );
    

    このコードはポストタイプが投稿か固定ページによって表示するメッセージを変化させる例で、表示用の配列を作るのに _n_noop を使用しています。 同じことは別の書き方でもできるでしょうが、このように書くと実行時に翻訳されるのは実際に使用するポストタイプのメッセージのみになります。

    これだけ関数があると全てを xgettext のオプションとして指定するのは大変なのですが、実行例は見当たりませんでした。 これには理由があって、WordPress に関してはPOT ファイルを作成するためのツールが用意されているので xgettext は不要なのです。 ツールは SVN リポジトリよりチェックアウトして使用します。 以下はプラグイン用の .po (.pot) ファイル生成実行例ですが、my-work-dirmy-plugin-dir は自分の環境に合わせて変えて実行します。 テーマの場合は wp-plugin を wp-theme に変えて実行します。

    $ svn co http://i18n.svn.wordpress.org/tools/trunk/ my-work-dir
    $ cd my-work-dir
    $ php makepot.php wp-plugin my-plugin-dir
    

    WordPress の国際化を行うのであれば、このページを一通り読んでおくべきです。 ディレクトリとドメインの扱いについてもそこにまとめられています。

    まとめ

    PHP/WordPress の gettext についてまとめました。 興味のある方はこれをとっかかりにして i18n 対応のコードを書いてみてください。

    WordPress プラグインを開発して WordPress.Org に登録するまで

    今回 WordPress プラグイン「Thin Out Revisions」を書いて公式リポジトリに登録したわけですが、その辺の流れを書いてみたいと思います。 プラグインを作る動機としては、何かしら個人としてのアウトプットを世間に出したいというのがあったのと、出すならやっぱりグローバルだよね、ってのがありました。 とはいっても私の場合、決して PHP も WordPress も jQuery も熟練者とは言えない状況でした。 それでもプラグインを作ろうとしたきっかけは先のエントリで紹介した「Professional WordPress Plugin Development」で、この内容ががあまりにもよくまとまっていたので、「何か自分でも書いてみたい!」と思ってしまったのです。 そこへ手頃な大きさの課題を見つけることができたので勢いで書きあげてしまいました。

    今回作った Thin Out Revisions は、プログラムのボリュームとしては大したことがありません。 しかし、jQuery も Ajax も使ったし、きちんと Nonce も使って (CSRF対策)、I18N も対応しました。 色々な要素が詰まっていますが、「Professional WordPress Plugin Development」がこれらを包括的に取り上げていたおかげで、さほど苦労せず書き上げることができました。 この本がなければプラグインを書いて公式登録することはなかったでしょうから、著者に感謝です。

    以下時系列に書いてみます。

    9/20 に予てより気になっていた「Professional WordPress Plugin Development」の Kindle 版をダウンロード購入しました。 そして読んでいると自分でもコードを書きたくなり、「そういえばあのリビジョン管理なんとかならないのか」といつも感じていたことを思い出しました。 そこから構想を練り、コードを書き始めました。

    最初のモックアップをローカル git リポジトリにコミットしたのが 9/23 になっています。 その後、作業を続け、9/28 の深夜には終わりが見えてきたので、そろそろ公式リポジトリの登録申請をしておくかという状況になりました。

    9/29 に申請を行い、wordpress.org のチームとメールのやりとりの後 svn リポジトリが用意されたのが 9/30。 そして 10/1、初めて使う subversion のコマンド (私は cvs -> git) でファイルをアップロードし、無事公式サイト上で公開されることとなりました。

    ちょっと焦ったのが svn リポジトリが提供されるよりも先にプラグインパッケージの提出を求められたことです。 「Professional WordPress Plugin Development」での説明順もそうですし、国内開発者のブログ記事でも「まずプラグインの登録申請を行い、後から Subversion をセットアップしてコード登録」という手順になっているので、「リポジトリが用意されるまでに時間がかかりそうだし、早めに申請しておいた方がよいんだろうなあ」と思っていました。 しかし、私の場合は申請後すぐに「プラグインがダウンロード可能な URL を教えるか、ZIP ファイルでプラグインを送ってねー」というメールをもらってしまいました。 ほとんど活動がなかった私のアカウントからの登録申請だったので、チェックが入ったということかも知れません。

    ここで「7日間のうちにレスポンスせよ」と期限を切られていたので急いで最後の調整を行い、初回 ZIP を送付し、その間にマイプラグインページを準備します。 1日も経たず readme.txt がないのを指摘をするメールが届いたので、「readme.txt? 何それ?」という状態からマークダウン記法で readme.txt を仕上げて更新 ZIP を再送付しました。 申請から 2日間でこのやりとりを完結させているのですが、wordpress.org の中の人もレスポンスが良かったのは週末だったお陰かも知れません。

    まあ、日本人としては readme.txt を仕上げるのにも時間がかかるし、普通の会社勤めでは急にまとまった時間が取れないことも多いでしょうから、プラグインを完成させ、(サイトとして完成させていなくても) プラグインホームページ URL や Donate 用 URL を決めてから登録申請を行った方がよいでしょう。 それと readme.txt の内容も本当は時間をかけてプラグイン検索でヒットしやすい様な文章を狙うべきだったと後から気づきました。

    何にせよ、今回の一連のやりとりでいろいろと経験値が貯まったのは確かです。 「手頃な大きさの問題を見つけられたので登録まで行けたんだよな」とも思うので、最初は無理をせず簡単なプラグインを書いて完成させることが重要だと思います。 とは言っても Thin Out Revisions はリビジョン管理好きの人には確実に便利なプラグインに仕上がっており、多くの人に使ってもらえればと思っていますので、興味がある方は是非ダウンロードしてください (宣伝!)。 もし気に入っていただけたら、公式サポートフォーラムで怪しげな英語 (ローマ字?) のやりとりをしましょう!

    WordPress リビジョン管理のための「Thin Out Revisions」

    WordPress を使い始めて最初に気が付くのは投稿/固定ページのリビジョンがどんどん増えていくことです。 基本的にそれらが更新されるたびにリビジョンが増えていく仕様なのですが、これを気にして気軽に保存できなくなるようでは本末転倒です。 そのままだとデータベース使用量が増えるということもあって、リビジョン削除用プラグインは定番カテゴリーを形成しているように見えます。

    しかし、実際には全てのリビジョンを綺麗さっぱり削除してしまうようなプラグインが多く、それではせっかくリビジョン機能が実装されているのにもったいないと思ってしまいます。 世代数で制限をかけるプラグインもありますが、一番最初の版とどう変わったかをみたいときもあると思うので世代数による一律保管制限では機能不足という場合も多いでしょう。 そんな中 Revision Control というプラグインは個別にリビジョンを削除できるので重宝しますが、もう少しスマートにやれればと思ったのでした。

    というような考えで生まれたのがこの Thin Out Revisions プラグインです。 このプラグインを有効化するとリビジョン比較画面 (revision.php) で選択した新旧リビジョンの間にある中間リビジョン全てを一括で削除できます。 使い方は簡単で削除対象を確認してボタンを押すだけです。

    Thin Out Revisions

    また、隣り合った2つのリビジョンを選択した時は、古い方のリビジョンを削除対象とする動作にしました。 これで一番最初に自動保存されてできてしまった目障りな空リビジョンを削除することができます! (私だけ?)

    Thin Out Revisions 2

    公式リポジトリからダウンロードできますので、興味のある方は是非使ってみてください。 ちゃんと日本語対応してます。(日本語のスクリーンショットが必要か…。)


    2012.12.21 追記
    バージョン 1.1 になって余計なリビジョンを作らないための機能が追加されています。→ 関連記事

    2013.2.24 追記
    更にメモをつける機能も追加されました。

    2014.6.14 追記
    日本語公式サイトはこちら。

    WordPress は最初からマルチサイト化しておこう

    WordPress のマルチサイト化について経験に基づきもろもろをまとめておきます。

    はじめに

    「マルチサイト」は 1つの WordPress インストレーションで複数のブログを運用するための機能のことです。 各ブログごとにドメインを分ける「サブドメイン方式」と、同じホスト名でディレクトリを分ける「サブディレクトリ方式」があります。

    インストール完了直後に設定するのがお勧め

    既に運用を始めている WordPress を後からマルチサイト化することもできますが、それなりの設定変更となるので運用を始める前にマルチサイト化しておくのが良いと思います。 一人でブログを書き始めるとしても、将来複数のブログ (例えば、日本語版と英語版とか、仕事用と趣味用とか) を運用したくなるときが来るかも知れないので、とりあえず失敗してもさほど痛くない運用開始前にやっておこうということです。 そもそも最初にやっておいた方が制限も少なくすんなり設定できるはずです。 最初からマルチサイト化しておけば「マルチサイト未対応プラグインを使ってしまい切り替えるときに困る」ということもありません。 マルチサイト機能を有効化しておいて 1つのブログで記事を書き始めれば良いのです。

    運用開始後はサブドメイン方式一択

    運用開始後にマルチサイト化する際の制限として、サブディレクトリ方式はできず、サブドメイン方式でやるしかないというものがあります。 公式情報には以下のようにあります。

    You cannot choose Sub-directory Install (for a path-based network) if your existing WordPress installation has been set up for more than a month, due to issues with existing permalinks.

    一か月経つとパーマネントリンクの何がかわるのかが今一つわからないのですが、基本的にはサブディレクトリ方式でマルチサイト化したいならばインストール直後に設定しなければならないと捉えておくべきでしょう。 その他にもサブドメイン方式を選ぶときは WordPress ディレクトリがドキュメントルートでなければならないと書かれています。 以下のような URL はダメということです。

    http://example.com/wordpress/
    

    あるいは下の様に標準ポートでない場合はそもそもマルチサイト化できません。

    http://example.com:8000/
    

    ハマらないために公式情報はきちんと読んでおきましょう。

    サブドメイン方式の構成

    実際のところ、私もマルチサイト機能を知ったのは運用開始後なので、選択の余地はありませんでした。 というわけでここからはサブドメイン方式の話です。 共用レンタルサーバでは仮想ホスト機能を使いホスト名とディレクトリを対応付けすると思いますが、使用するホスト名は全て同一の WordPress ディレクトリに対応するように設定を行います。 以下のようなイメージです。

    example.com → wordpress ディレクトリ (最初のサイト)
    sub1.example.com → wordpress ディレクトリ
    sub2.example.com → wordpress ディレクトリ
    

    もちろん DNS のレコードもドメインの数だけ登録して同じ Web サーバーを指すようにしておかねばなりません。

    実際にやってみる

    実際の手順は公式情報の通りです。 wp-config.php や .htaccess の修正、blogs.dir の作成といったあたりをこなさなければなりませんが、どのように変更するかは指示されるのであまり迷うこともありません。 あちらこちらのブログにやり方が書かれていますが、中途半端な記事を読むよりは公式情報を読んで作業する方がすんなり行くように思います。 英語も難しくないんで慣れましょう。

    1点注意があるのですが、それは .htaccess の修正です。 これを失敗すると 2番目以降のサイトでアップロードした画像が表示されなかったりします。 私の場合、元の .htaccess は以下の様なものでした。

    <IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteBase /
    RewriteRule ^index\.php$ - [L]
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule . /index.php [L]
    </IfModule>
    

    その .htaccess に以下を追加するよう指示されました。 (少なくとも私にはそう読めました)

    RewriteEngine On
    RewriteBase /
    RewriteRule ^index\.php$ - [L]
    
    # uploaded files
    RewriteRule ^files/(.+) wp-includes/ms-files.php?file=$1 [L]
    
    RewriteCond %{REQUEST_FILENAME} -f [OR]
    RewriteCond %{REQUEST_FILENAME} -d
    RewriteRule ^ - [L]
    RewriteRule . index.php [L]
    

    しかし、実際は後ろに追記しても動かず、正常動作のためには指示された内容に置き替えなければなりません。 このあたり WordPress バージョンや状況によって表示される指示が異なる可能性があるので Apache の Rewrite ルールをある程度把握しておかねばなりません。 元 .htaccess の最初の RewriteCond からの 3行で以下の置き換えが行われます。

    「実体としてのファイルやディレクトリが無い URL は /index.php に書き替え処理を停止する ([L]フラグ)」

    サブドメインサイトにアップロードされたファイルを表示するための書き替えは以下の行なのですが、実体がない URL の置き換えなのでここに来る前に先のルールが適用されると /index.php に変換され処理が終わってしまいます。

    RewriteRule ^files/(.+) wp-includes/ms-files.php?file=$1 [L]
    

    ちなみに追記指示分の最初の RewriteCond からの 3行は以下の置き換えルールです。

    「実体としてファイルかディレクトリが存在する URL は変換せず ("^ -" の部分) 処理を停止する」

    ド・モルガンの法則でしたっけ? 最終行は残り全て index.php に書き替えるので結局元の .htaccess の RewriteCond の部分と同じ結果になるんですよね。 なので、元のルールは必要なく、表示されたもので置き換えれば動きます。 Rewrite ルールは奥が深いです。 私も「^ -」と「. -」の差異とか / があったりなかったり等の細部は気にしないことにしましたが、ある程度は把握していないと動かずに苦労することになるのです。

    ということもあったりなので、インストール直後のマルチサイト化がお勧めなのです。 まあ、この記事を目にする人の多くは既に WordPress を運用しているんだろうとは思いますが…。

    WordPress のプラグインを書こう

    「Kindle で技術系洋書を読もう!」シリーズ (?) の第 2弾です。 WordPress のプラグインを書くのに最適な本を見つけました。 「Professional WordPress Plugin Development」という本です。

    Amazon.com で目次が見れますが、下に各章の概要を書いておきます (章のタイトルそのままもありますが)。 ある程度経験がある方が見ればわかると思いますが、プラグイン開発で必要なものは一通り含まれています。 Kindle 版は約 2千円で、この価格で一通りの情報が得られるので PHP からしてビギナーの私としては大変助かりました。

    WordPress のカスタマイズのためにコードを書こうとしたときに最初に感じたのは情報を見つけにくいことでした。 検索をしても内容が古かったり、特に日本語の情報はテーマのカスタマイズ中心でプログラミングに関しては断片的な情報が多いように感じます。 一応プラグイン開発に関しては公式情報 (日本語英語) がありますが、手を動かし始めると他にも様々な情報が欲しくなってきます。 英語圏まで広げても状況はあまり変わらないようで、序文を読むと著者も同じように感じ、それが執筆の動機となったようです。 確かにこれだけ包括的な情報を一箇所で手に入れるのは難しいと思います。

    プラグイン開発をしなくても、WordPress の動く仕組みを知りたい、あるいはカスタマイズしてみたいというプログラマにはとても有用だと思います。 プログラミング関連書籍の英語は難しくないので興味があれば是非読んでみてください。

    ただし、テーマのカスタマイズ (CSS やマークアップ関連) の情報はありません。 そこは和書がたくさん出ていたと思います。

    1/2章
    プラグインについての基本知識。 推奨コーディングスタイルや開発チェックリストも含まれる。
    3章
    フック (Action と Filter) について。 フック関数としてクラスのメソッド (メンバ関数) を登録する方法。
    4章
    メニューの追加。ウィジェットの作成。メタボックス (投稿等の画面上のボックス区画) 作成と内容の保存。
    5章
    I18N。コードの対応と翻訳ファイルの作り方。
    6章
    セキュリティ。ユーザーのパーミッションやサニタイズ、Nonces (CSRF 対策) 等。
    7章
    設定を保存するための各種 API → *_option()、*_settings_*()、*_transient()、*_user_meta()。 カスタムテーブルへの保存。
    8章
    ユーザの管理。
    9章
    HTTP リクエストを使って Web サービス API を利用する方法。
    10章
    ショートコード。
    11章
    投稿 (post) の拡張。 カスタム投稿タイプやカスタムタクソノミー。
    12章
    JavaScript (jQuery) と Ajax。 フックを使ったサーバーサイド処理。

    13章
    Cron を使ったプログラムのスケジュール実行。
    14章
    URL の Rewrite。
    15章
    マルチサイト。
    16章
    デバッグと最適化。
    17章
    ライセンスの選択。 公式リポジトリへの登録等。
    18章
    各種情報リソース。