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を右クリックし、「ブラウザで開く」メニューで確認してください。



ASP.NET国際化プログラミング – リソース管理

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

1. ASPXにヘッダー追加
カルチャ(言語と地域)の設定をASPXのヘッダー部分に追加します。

Default.aspx

  <%@ Page  Language="C#" ...%>
=><%@ Page  Culture="auto"
UICulture="auto" Language="C#" ...%>

2. 文字列の外部化(リテラルの置換)
ASP.NETは、文字列リソースの管理のために、4つの構文を持っています。

  • 構文1は ‘<%=..%>’ を必要とします。
  • ‘”‘ は構文2では切り落とされます。
  • 構文3は構文1と非常に似ていますが、
    少し違います。
  • 構文4ではmeta属性を追加することで、
    デフォルトのテキストを残すことが出来ます。
  • 構文3と4はASPタグのみにしか使えません。
Default.aspx

* 構文1. (タグテキストの置換) '<%='
+ 'Resources.Default.' + RESOURCE_KEY + '%>'
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>This is a sample site!</title>
 => <title><%=Resources.
   Default.DefaultWwnaviMsg1%></title>
</head>
<body>

* 構文2. (プログラム内のリテラルの置換)
 'Resources.Default.' + RESOURCE_KEY
<div>
    <% Response.Write("Hello, World!"); %>
 => <% Response.Write(
Resources.Default.DefaultWwnaviMsg2); %>
</div> 

* 構文3. (ASPタグ内のテキストの置換)
 '<%$' + 'Resources.Default,' +
RESOURCE_KEY + '%>'
<asp:Label ID="Label1" runat="server"
Text="This is label 1.">
=><asp:Label ID="Label1" runat="server" Text=
"<%$Resources:Default,
DefaultWwnaviMsg3%>">
</asp:Label>

* 構文4. (ASPタグ内のmeta属性の追加)
 'meta:resourcekey="' + RESOURCE_KEY + '"'
<asp:Label ID="Label1" runat="server"
Text="This is label 1.">
=><asp:Label ID="Label2" runat="server" Text=
"This is label 1." meta:resourcekey=
"DefaultWwnaviMsg4" >
</asp:Label>

各文字列の場所に応じて、適切な構文を使う必要があります。
(ASPタグでは構文3または4どちらかが使えます。)

3. Resxファイルの作成
外部化された文字列をresx(リソース)ファイルに配置します。’App_GlobalResources’フォルダをプロジェクトディレクトリの直下に作成し、resxファイルをその下に、’ASPX_FILE_NAME(拡張子なし).resx’(*1)という名前で作成します。

PROJECT_DIRECTORY
    -  App_GlobalResources
         -  Default.resx
         -  WebForm1.resx
         *1)構文4(meta属性)を使っている場合は、
拡張子を付ける必要があります(Default.aspx.resx)。
Default.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="DefaultWwnaviMsg1" xml:space="preserve">
     (RESOURCE_KEY)
    <value>This is a sample site!</value>
    (RESOURCE_STRING)
  </data>
</root>

4. コンパイルとローカリゼーション
ビルドと実行をしてください、ブラウザ内でリソース文字列が表示されるのが確認できるはずです。他の言語のリソースを追加したい場合は、ASPX_FILE_NAME.LOCALE_CODE(例:’ja’).resxをApp_GlobalResourcesに追加してください。

App_GlobalResources
  -  Default.resx
... マスターリソース (例:英語)
  -  Default.ja.resx
... ローカライズされたリソース (例:日本語)


PHP国際化プログラミング – gettextを使った国際化

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

1. ローカライズする文字列をgettext関数に配置
ローカライズしたい文字列をgettext関数(殆どの場合、’_()’)内に配置します。
これらの文字列は、各外部リソースファイルによってローカライズ(翻訳)されます。

echo "Hello from PHP!";
-> echo _("Hello from PHP!");

2. 初期化コードを記述
ユーザーロケール(言語と地域)を適用し、リソースの名前とパスをgettextの仕組みに教えます。

index.php (または他のphp)

<?php require_once(
$_SERVER["DOCUMENT_ROOT"].'/
gettext/wwnaviRs/' .
'wwnavi.gettext.php');?>
// ロケールの設定およびgettextの
読み込みのための初期化コードの実行
...
echo _("Hello from PHP!");
...
wwnavi.gettext.php // 初期化コード

require_once('wwnaviLang.php');
// 後に使う、
// 言語と地域の配列

$header = $_SERVER[
'HTTP_ACCEPT_LANGUAGE'];
// 言語情報を持った、
ユーザーのHTTPヘッダーの取得

$lang='';
// HTTPヘッダーからロケールを解析
// ロケールの表現は、各Webブラウザによって異なりますが
// ('en'、'en-us'、'en_US'...)、
// setlocaleの引数は、
// 'language_REGION'である必要があります。
if(strpos($header, ',')>0) {
    $hds = explode(',', $header);
    $lang = $hds[0];
    $lang = str_replace('-', '_',
    $lang);
    if(strpos($lang, '_')>0) {
        $ls = explode('_', $lang);
        if(count($ls)>1) $lang =
      $ls[0] . '_' . strtoupper($ls[1]);
    }else if(!empty($wwnaviLang[$lang]
        )) {
        $lang .= '_' . $wwnaviLang[
        $lang];
        // 言語コード(例: 'en')
       // のみの場合、地域コード(例: 'US')を、
        // 予め定義された配列から追加します。
       // setlocaleは、完全なロケール名を必要と
      // するからです。
    }
}
//echo $lang . '
';
//putenv("LC_ALL=$lang");

setlocale(LC_ALL, $lang);
// $langは少なくとも、
//  言語と地域(例:"en_US")である必要があります。

$domain = 'wwnaviBundle';
// gettextのリソース名と
// パスを教えます。
bindtextdomain($domain,
dirname(__FILE__));
textdomain($domain);
bind_textdomain_codeset(
$domain, 'UTF-8');
wwnaviLang.php // 予め定義された、
// 言語とデフォルトの地域のコードの配列

$wwnaviLang = array(
...
'de'=>'DE',
'en'=>'US',
'es'=>'ES',
'et'=>'EE',
'eu'=>'ES',
'fa'=>'IR',
'fi'=>'FI',
'fo'=>'FO',
'fr'=>'FR',
...
'ja'=>'JP',
...

3. gettextのマスターテンプレートリソース(.POT)の作成
msgidとmsgstrのペアを持つ、マスターリソース(.pot)を、リソースパスの下に作成する必要があります。
xgettextコマンドは、’_()’文字列をスキャンし、potファイルを作成するのに便利です。

YOUR_DOCUMENT_ROOT/gettext/
wwnaviRs/(*1)
 ... wwnaviBundle.pot(*1)

*1)上記コードで
指定されたものと同じディレクトリ
wwnaviBundle.pot
...

#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-03-05 17:32+0900\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"

#: /...../examples/php/gettext/index.php:10
msgid "Hello from PHP!"
msgstr ""

#: /...../examples/php/gettext/index.php:13
msgid "This is a DIV text."
msgstr ""
...

#: /root/wwnavi/workspace/examples/php/
gettext/sub/sub2/sub2.php:31
#, php-format
msgid "My local time is %c!"
msgstr ""
xgettext -j -L php  -k"_" --from-code=UTF-8 -o
/..../examples/php/gettext/wwnaviRs/
wwnaviBundle.pot  \
   /..../examples/php/gettext/index.php

4. .POTを各言語リソース(.PO)にローカライズ
マスターテンプレートを、対象ロケールのディレクトリ(LOCALE(例:’ja_JP’)/LC_MESSAGES)に、.poファイルとしてコピーし、msgstrを翻訳します。

日本語への翻訳の場合

YOUR_DOCUMENT_ROOT/gettext/
wwnaviRs/ja_JP/LC_MESSAGES
 ... wwnaviBundle.po
ja_JP/LC_MESSAGES/wwnaviBundle.po
...

#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-03-05 17:32+0900\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"

#: /...../examples/php/gettext/index.php:10
msgid "Hello from PHP!"
msgstr "PHPからこんにちは!"

#: /...../examples/php/gettext/index.php:13
msgid "This is a DIV text."
msgstr "これはDIVのテキストです。"
...

#: /root/wwnavi/workspace/examples/php/gettext/
sub/sub2/sub2.php:31
#, php-format
msgid "My local time is %c!"
msgstr "わたしの現地時間は%cです!"

***注意: ‘%c’のようなフォーマット文字を修正(翻訳)してはいけません。


5. .POを.MO(リソースバイナリ)にコンパイル

msgfmtコマンドを実行し、リソースバイナリ(.mo)を対象ロケールディレクトリに作成します。

msgfmt -o wwnaviBundle.mo wwnaviBundle.po

これで、ブラウザの言語設定を変更しながら、英語と日本語のメッセージを切り替えられるようになります。

これらの処理は、World Wide Navi内のPHPの文字列外部化のサンプルで確認できます。

POT/POのローカリゼーションのプロセスは、ローカリゼーションツールSisulizerを使うことで、より簡単に効果的になります。