2012年5月
« 4月    
 123456
78910111213
14151617181920
21222324252627
28293031  

カテゴリー

.NET国際化プログラミング – リソース管理 (Form、WPF、Silverlight)

ソフトウェア国際化ツールWorld Wide Navi(ワールドワイドナビ)の国際化プログラミング参考情報から、.NETの国際化の抜粋です。

※後半には、ローカリゼーションツールSisulizer(シスライザー)によるXAML編集なしでの直接WPF、Silverlightのバイナリをローカライズする方法を記述しています。

1. ResourceManagerを作成する
初期処理(MainやInitializeComponent)でResourceManagerのインスタンスを作成します。以下はWorld Wide Naviのサンプルコードです。

WwnaviResource.cs

namespace Wwnavi {
    using System;
    public class Resource {
        public static void Init(){

           // Set the user interface to
display in the same culture as that set in Control Panel.
           System.Threading.Thread.CurrentThread.
           CurrentUICulture =
               System.Threading.Thread.
            CurrentThread.CurrentCulture;
           // (*コントロールパネルの
            地域と言語の設定を反映させるための重要なコードです。)

            // Assembly
            wwnaviRs(*1) = new System.Resources.
           ResourceManager(
                 "WindowsFormsApplication.(
             Properties.)wwnavi_string",
                  (*'Properties' は
              2005以降のC# プロジェクトのみ必要です)
                 System.Reflection.Assembly.
               GetExecutingAssembly());
            // File Based
            // wwnaviRs = System.Resources.ResourceManager.
            //    CreateFileBasedResourceManager(
            //    "WindowsFormsApplication.wwnavi_string",
            //    "./",null);
        }
        public static String GetString(String id){
            if (wwnaviRs == null) Init(); *3)
            return wwnaviRs.GetString(id);
        }
        public static System.Resources.ResourceManager
GetResourceManager(){
            return wwnaviRs;
        }
        private static System.Resources.
ResourceManager wwnaviRs = null;
    };
}

*1)wwnaviRs … RsourceManagerのインスタンス

このサンプルは、埋めこみリソースまたはサテライトDLLを読む
‘Assembly’ RsourceManagerを作成しています。
2番目のコメントアウトされたコードは’FileBased’ RsourceManagerを作成しており、
.resourcesファイルを読み込むものです。

static void Main()
{
    Wwnavi.Resource.Init(); *2)
    ...
}

*2)上記*1)のResourceManager作成処理を呼んでいます。

ResourceManagerの作成は初期処理で行うのが望ましいですが必須ではありません。
*3)のコードはリソースデータを返す前にResourceManagerが作成されていない場合、’Init’を呼んでいます。
これはクラスライブラリなどの初期処理を持たないプロジェクトのためです。

フォームアプリケーションの場合は、Main関数内の冒頭で、InitializeComponent()が呼ばれる前にInitを呼ぶ必要があります。

[STAThread]
static void Main()
 {
 // System.Threading.Thread.
CurrentThread.CurrentUICultureの設定を
初めに行う必要があります。
 Wwnavi.Resource.Init(); 

VBの場合は、Sub New()の冒頭。

※WPF、およびSilverlightの場合は、
こちらをごらんください。

3. 文字列取得のコードを埋めこむ
ResourceManager.GetStringメソッドでresxファイルから文字列情報を取得します。

MessageBox.Show(
Wwnavi.Resource.GetString(
"wwnavi.Msg.Id1")...

WwnaviResource.cs
...
public static String GetString(String id){
    ...
    return wwnaviRs.GetString(id);
}

4. 文字列をresxファイルに記述する
メッセージIDと共に文字列をresxファイルの以下のエレメントに記述します。
このファイル名はResourceManager作成メソッド(上記1-*1を参照、”WindowsFormsApplication.
(Properties.)wwnavi_string”)に対応している必要があり、 このケースでは’wwnavi_string.resx’でなければいけません。

*Visual Studioアドインの実装では、”resources”という名前を使う必要があります。
(例: ResourceManager “MyAddin.resources”、resx ファイル “resources.resx”)

<?xml version="1.0" encoding="utf-8"?>
<root>
  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    ...
  </xsd:schema>
  ...
  <resheader name="writer">
    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <data name="wwnavi.Msg.Id1" xml:space="preserve">
        *1)
    <value>Hello, how are you? This is a sample.</value>
  </data>
</root>

*1)’data’エレメントに文字列を記述する必要があります。
“wwnavi.Msg.Id1″は’GetString’のメッセージIDに対応しています。

5. リソースファイルをコンパイルし配置する
.NETアプリケーションをVisual Studio(C#プロジェクト、VC++ CLRプロジェクトなど)で開発している場合、 自動国際化プロセスを使うことができます。手順は以下です。

1. コンポーネントプロパティをセットする

   フォームデザイナビューで以下のプロパティをセットします。

   Form.Localizable = true
   Form.Language = Default
   (Label.AutoSize = true)

2. resxファイルをプロジェクトに追加する

   resxファイル(例、上記の'wwnavi_string.resx')をプロジェクトに追加します。
   VC++プロジェクトを使っている場合プロジェクトの直下に追加し、
   2005以降のC#プロジェクトの場合は、ドラッグアンドドロップで'Properties'ディレクトリの
   下に追加します。
   (World Wide Naviはこれらの手順を自動的に行います)

   project-
           Form1.h (or .cs)
<- *Localizableをtrueにセット
           Form1.resx
<- *Visual Studioにより作成
           wwnavi_string.resx or
           Properties/wwnavi_string.resx
 (C#のみ)

3. ソリューションをリビルドする

   ソリューションをリビルドし実行ファイルを生成します。

   これらの手順では、リソース(Visual Studioのリソース'Form1.resx'と自分自身のリソース
   'wwnavi_string.resx')は実行ファイルにデフォルトのものとして埋めこまれるため、
   DLLは作成されません。

   Debug/Release-
                 WindowsFormApplication.exe
 <- *Form1.resxとwwnavi_string.resx
 のリソースを含む

4. 他の言語リソースを追加する

   他の言語リソースをプロジェクトに追加した場合Visual Studioは自動的にサテライトDLLを
   作成します。
   例えば、Form1.resxとwwnavi_string.resxを日本語に翻訳されたリソースにコピーし
   (名前は'*.ja.resx'となります)、それらをプロジェクトに追加しリビルドすると、
   自動的に日本語リソースのDLLが作成されます。  

   project-
           Form1.h (or .cs)
 <- *Localizableをtrueにセット
           Form1.resx
  <- *Visual Studioにより作成
           Form1.ja.resx
  <- *コピーし翻訳されたもの *3)
           wwnavi_string.resx
           wwnavi_string.ja.resx
<- *コピーし翻訳されたもの
           Properties/wwnavi_string.resx
   (C#のみ)
        /wwnavi_string.ja.resx
  (C#のみ)

          *3)Form1.ja.resxは、フォームデザイナビューで
Form.Languageを'ja'にすることで
             Visual Studioで自動的に作成することもできます。

   Debug/Release-
                 WindowsFormApplication.exe
   <- *Form1.resxとwwnavi_string.resx
のリソースを含む

     ja/WindowsFormApplication.resources.dll
   <- *Form1.ja.resxとwwnavi_string.ja.resx
のリソースを含む

   === 注意 ===
   これらの手順は'Assembly' ResourceManagerを
使った場合のみ有効です。
   'FileBased'のマネージャを作る場合は、
.resourcesファイルを実行ディレクトリに
   手動でコピーし、常にそれらを実行ファイルに
添付する必要があります。


WPFとSilverlightの場合
WPFスタンドアプリケーションとSilverlightの場合は、UIロケールの設定の仕方が異なります。

WPFスタンドアプリケーションの場合:
デフォルトではMain関数が自動生成され、UICultureの設定ができないため、
自分でMain関数用のクラスを記述し、Main関数の自動生成をOFFにします。

1. App.xamlのプロパティの「ビルドアクション」を「Page」に変更。

2.以下のようなMain関数用のクラスを作成し、
Wwnavi.Resource.Init()(CurrentUICultureの設定)
を呼び出します。

using System;
using System.Collections.Generic;
namespace Wwnavi{
    class TempMain{
        [STAThread]
        static public void Main(string[] args) {

           Wwnavi.Resource.Init();
// CurrentUICultureの設定

           YOUR_APP_NAME.App app =
new YOUR_APP_NAME.App();
           // YOUR_APP_NAME is your application name
 (e.g. WpfApplication1)
           app.InitializeComponent();
           app.Run();
        }
    }
}

3.あとは正常にビルドされればOKです。

*各国語のdllはビルドされたexeからSisulizerを使って、
XAMLファイルの変更なしに容易に作成できます。

YOUR_PROJECT_PATH/bin/Debug/WpfApplication1.exe
     ja/WpfApplication1.resources.dll
 (*Sisulizerを使って作成された日本語のdll)

Sisulizerの詳細については、
World Wide Naviのヘルプをごらんください。

Silverlightの場合:
Silverlightでは、Silverlightのオブジェクト(xap)を埋めこんだHTML(ASP、PHPなど)でユーザーのロケール(言語)を認識し、それをSilverlightのパラメータとして渡し、適切なxapファイルを読み込むのが一般的です。

以下は、Visual Studioのテスト用ASPを修正した、World Wide Naviのサンプルです。

SilverlightApplication1TestPage.aspx
...
<%--

//// These are Silverlight i18n/l10n part. ////

--%>

    <script runat="server">
    // Get the current locale.
    //ユーザーのHTTPリクエストから言語情報を取得します。
    string sLang = System.Web.HttpContext.
Current.Request.UserLanguages[0];
    string sPath = System.Web.HttpContext.
Current.Request.UserLanguages[0];
    string root = System.Web.HttpContext.
Current.Server.MapPath("/ClientBin/");
    </script>

    <%
        // Set Silverlight file (xap) path.
        //ユーザーの言語情報に対応するxapファイルのパスを生成します。
        // ClientBin/SilverlightApplication1.xap
... デフォルトxap
        // ClientBin/ja/
SilverlightApplication1.xap ... 日本語xap
        // ClientBin/ko/
SilverlightApplication1.xap ... 韓国語xap

        // *各国語のxapはSisulizerを使って、
XAMLファイルの変更なしに容易に作成できます。
        // Sisulizerの詳細については、
World Wide Naviのヘルプをごらんください。

        if (!System.IO.Directory.Exists(root + sPath))
        {
            if (sPath.Length > 2)
sPath = sPath.Substring(0, 2);
        }
        if (!System.IO.Directory.
Exists(root + sPath))
        {
            sPath = "";
        }
        if (sPath != "") sPath = sPath + "/";
     %>

    ...
        <object data="data:application/x-silverlight-2,"
type="application/x-silverlight-2" width="100%" height="100%">

         <%--
         Switch xap files to load on the current locale.
         These are directly localized files
by Sisulizer WITHOUT XAML sources.
          --%>
		  <param name="source" value="ClientBin/
<%=sPath %>SilverlightApplication1.xap"/>

		  <param name="onError" value="onSilverlightError" />
		  <param name="background" value="white" />
		  <param name="minRuntimeVersion" value="3.0.40624.0" />
		  <param name="autoUpgrade" value="true" />

		  <%--
		  Set the current locale to the Silverlight code.
		   --%>
		  <param name="uiculture"
value="<%=sLang %>" />
         <param name="culture"
value="<%=sLang %>" />
         //これら2つのパラメータが
Silverlightオブジェクトのカルチャとして
引き継がれます。

*デバックには、デバックメニューではなく、Visual Studio内でこのASPを右クリックし、「ブラウザで開く」メニューで確認してください。



WordPressテンプレートの日付書式の変更

先日、Carrington Mobileというテンプレートを使って、当サイトをモバイル対応しました。

スマートフォンでもきれいに表示されてなかなか優れものですが、1点難点が。

記事の日付がことごとく文字化け、または表示されない

原因は、テンプレートのコメントの日付、時間のフォーマット処理がうまく働いていない。

該当箇所を見つけ出し、とりあえず固定のyyyy.mm.dd形式にして解決。

ちなみに、当サイトのPC版テンプレートAtahualpaでも同様の現象が起こります。

該当箇所の見つけ方としては、ベタですが、

themes/… 以下の該当テンプレートディレクトリを”date(”などでgrepする。

見つかったPHPのget_comment_dateやcomment_datedateなどを呼び出し箇所を探して以下のように直す。

<p>by <cite><?php comment_author_link() ?></cite> on <a href=”#comment-<?php comment_ID() ?>” title=”"><?php comment_date(‘Y.m.d ‘) ?> <?php comment_time(‘H:i:s‘) ?></a> <?php edit_comment_link(‘e’,”,”); ?></small></p>

ここに別の書式文字が指定されていたり、引数なしの場合、うまく表示されないことが多いです。

※但し、これらの関数の挙動はホスティングしているサーバーのPHPの設定やロケールで変わるので、一概にそうとは言えない。

該当ファイルは、Atahualpaの場合、

bfa_custom_comments.php

※ちなみに、このファイルが関係するのはコメントの日付。記事の日付などは、Atahualpaのテーマオプション画面で変更できます。(%date()% の部分を%date(‘Y.m.d H:i:s’)%などに変える。)

<?php printf(__(‘%1$s  ‘at’を削る %2$s’,'atahualpa’), get_comment_date(__(‘Y.m.d‘,’atahualpa’)),  get_comment_time(__(‘H:i:s‘,’atahualpa’))) ?>

Carrington Mobileの場合は、

comment-default.php

など数ファイルです。

PHPの書式フォーマットについては、こちら

本当は、タイムゾーンをUTCで管理して、アクセス元のIPなどで国判別して、時差計算して表示するとかしたいんだけど(せっかく多言語ブログなので)、そういったプラグインないかな?

Atahualpaの日付バグについては、こちらの記事も参照してみてください。



フリー

社内でクリス・アンダーソン著の「フリー」が回覧され、私も読んだ。現在世の中に起こっていることを同じ角度から切り取って丁寧に考察しており、勉強になる。「安い」と「ただ」では、購買者が持つ意識がまったく違う。ゼロの力学は、これまでのビジネスを根底から覆すパワーを持っている。

テクノロジーは、人間の労働力や時間を、機械やプログラムそしてそれを動かすための電力に換算して数値化する仕組みを創りだすことができる。最初のうちは、人間がやったらこのくらいという人月の呪縛から離れるのは難しいかもしれないが、人手でやらないことが当たり前になると価格は劇的に低下していくだろう。グローバル化の波はそれをさらに加速する。人件費を基準にしても、それは世界で一番安いところをベースに計算されてしまうからである。

弊社の World Wide Navi の文字列外部化機能は、数週間から数カ月分の作業を数時間から数日で終わらせることができる。その作業を1度きり使うだけでも日本での人件費に換算すると数十万から数百万分の価値となるが、一番安い価格だと人件費の数日分にもならない。あれこれ勉強したり調べたりする時間を費やすコストの方が高くつく。試用してみて使えそうなら即購入。そんな流れが作れないかと準備中である。

会社も5期目に入り、国際化の専門企業としてそれなりの信用も得てきた。最近多いのはやはり中国語対応の話である。日本語、英語だけだと力技でできても、中国語が加わると仕組みを作った方が圧倒的に有利である。国際化JPの製品やサービスが今、ちょうど求められているのではないかと思う。国際化JPの製品、サービスをなるべく多くの人に知ってもらい、役立ててもらう、それをこれからやっていかねばならない。そして、もうすぐ自らも海外市場に向けて歩みだす予定である。乞うご期待。