- PR -

ASP.NET 2.0 マスターページと多言語対応サイトについて

1
投稿者投稿内容
Youme
会議室デビュー日: 2008/02/26
投稿数: 7
お住まい・勤務地: Lexington, KY
投稿日時: 2008-02-26 16:02
こんにちは。
私は今、日本語と英語に対応したページを設計しております。
ASP.NETは初めて扱う開発ツールなので、わからない箇所は小さなプログラムを作って動作を確認しながら進めているのですが、どうしても解決できないことがあり、投稿しました。設計面も含めてご助言いただければと思います。

[状況]
マスターページにDropDownListを配置しておき、
AutoPostBackプロパティをTrueに設定、
DropDownListには、日本語(ja-JP), English(en-US) のItemがセットされており、
選んだ言語でサイトを表示する。
というイメージで設計しています。

以下のサイトを参考にさせて頂いて、InitializeCulture メソッドをオーバーライドして単一のページで言語の切り替えを行う事ができました。
InitalizeCulture メソッドのオーバーライドは、
ExtendPage : System.Web.UI.Page
を作成し、ExtendPage内で行っています。
すべてのWebFormは、マスターページを雛型として利用し、ExtendPage クラスを継承するようにしています。

[参考サイト]
方法 : ASP.NET Web ページのグローバリゼーション用のカルチャおよび UI カルチャを設定する
http://msdn2.microsoft.com/ja-jp/library/bz9tc508.aspx

@IT会議室 - ASP.NET(C#) 多言語対応について
http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=8790&forum=7

@IT会議室 - ASP.NET マスターページを更新しない方法
http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=43172&forum=7

[問題点]
表示言語を変更した後、別のページに切り替えるとマスターページのDropDownListが初期化されてしまい、それに伴って言語も変わってしまいます。(既定で選択されている項目は、日本語 なのでページを切り替える毎に日本語で表示されます)。
言語を変更した場合、別のページに移っても変更後の言語にて表示する様にしたいのです。私のサイトから一度出てしまった後は、初期化されてもよいと思います。
Sessionに選択した言語を格納し、マスターページの読み込み時にセットする事を試みたのですが、InitializeCulture メソッドが、MasterPageの各種イベントの発火より前に処理が行われるので、セットするタイミングがありません。

私の周りには多言語開発の経験者がおらず、なかなか作業が進まない状態で困っております。

ご助言、ご意見等ございましたら、よろしくお願いいたします。
また、情報が不足している場合は、ご指導いただければ幸いです。

どうぞよろしくお願いいたします。
くまっち
大ベテラン
会議室デビュー日: 2008/01/18
投稿数: 169
お住まい・勤務地: 茨城県のどこか。
投稿日時: 2008-02-26 16:56
InitializeCultureメソッド中にPage.Masterにて
マスターページにアクセスし、DropDownListコントロールを取得して
SelectedIndexを適切な値にすれば良いのではないですか?
BT
ベテラン
会議室デビュー日: 2006/09/24
投稿数: 81
お住まい・勤務地: Tokyo
投稿日時: 2008-02-26 21:50
InitializeCultureメソッドが呼ばれるタイミングはマスターページというか、
サーバコントロールの生成される前なので各ページで制御するのは難しいようです。
Global.asaxファイルというのを作ってそちらにアプリケーション共通の処理を書くと
いうのがセオリーのようですね。

ちょうどそのあたりを調べていて見つけたサイトを参考までに載せておきます。

http://forums.asp.net/t/969928.aspx

私も同じ方法でやろうとしていたのですが、
途中でSEO上の問題や、キャッシュ使用に問題が出てきそうなので
別の方式に路線変更したため最後まで試してはいませんが、
おそらく上記リンクの方法でできると思います。

BT
ベテラン
会議室デビュー日: 2006/09/24
投稿数: 81
お住まい・勤務地: Tokyo
投稿日時: 2008-02-27 00:45
読み返したら、ちょっとずれていたように思いましたのでもう一度。

よく読むと言語の切り替えはできているようなので、
要はMasterPageに置いたDropDownListへの値のセットのみできないということですよね。

Global.asaxを使わなくてもできそうな感じだったので
私もちゃんと試していなかったのもあって、簡単なページを作ってやってみました。

[MasterPage.master]

<%@ Master Language="C#" AutoEventWireup="true" CodeFile="MasterPage.master.cs" Inherits="Test_MasterPage" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
 <title>無題のページ</title>
 <asp:ContentPlaceHolder id="head" runat="server">
 </asp:ContentPlaceHolder>
</head>
<body>
 <form id="form1" runat="server">
 <div>
  <asp:DropDownList ID="LanguageList" runat="server" AutoPostBack="True" meta:resourcekey="LanguageListResource1">
   <asp:ListItem Value="ja-JP" Selected="True" meta:resourcekey="ListItemResource1">日本語</asp:ListItem>
   <asp:ListItem Value="en-US" meta:resourcekey="ListItemResource2">English</asp:ListItem>
  </asp:DropDownList>
  </div>
  <div>
   <asp:ContentPlaceHolder id="ContentPlaceHolder1" runat="server">

   </asp:ContentPlaceHolder>
  </div>
  </form>
</body>
</html>

--------------------------------------------------------

[Default3.aspx]

<%@ Page Language="C#" MasterPageFile="~/Test/MasterPage.master" AutoEventWireup="true"
CodeFile="Default3.aspx.cs" Inherits="Test_Default3" Title="無題のページ" culture="auto" meta:resourcekey="PageResource1" uiculture="auto" %>

<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="Server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="Server">
 <asp:Label ID="Label1" runat="server" meta:resourcekey="Label1Resource1"></asp:Label><br />
 <asp:HyperLink ID="HyperLink1" runat="server"
NavigateUrl="~/Test/Default4.aspx" meta:resourcekey="HyperLink1Resource1">
  ページ4へ
 </asp:HyperLink>
</asp:Content>

--------------------------------------------------------

[Default3.aspx.cs]

using System;
using System.Globalization;
using System.Threading;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;

public partial class Test_Default3 : System.Web.UI.Page
{
 protected override void InitializeCulture()
 {
  String selectedLanguage = "ja-JP";

  if (Session["Language"] != null)
  {
   selectedLanguage = (String)Session["Language"];
  }

  foreach (string key in Request.Form.AllKeys)
  {
   if (key.Contains("LanguageList"))
   {
    selectedLanguage = Request.Form[key];
    Session["Language"] = selectedLanguage;
    break;
   }
  }

  UICulture = selectedLanguage;
  Culture = selectedLanguage;

  Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(selectedLanguage);
  Thread.CurrentThread.CurrentUICulture = new CultureInfo(selectedLanguage);

  base.InitializeCulture();
 }
 protected void Page_Load(object sender, EventArgs e)
 {
  DropDownList LanguageList = (DropDownList)Master.FindControl("LanguageList");
  LanguageList.SelectedValue = (String)Session["Language"];

  Label1.Text = Thread.CurrentThread.CurrentCulture.ToString();
 }
}
--------------------------------------------------------
[Default4.aspx] → Default3.aspxに同じ
--------------------------------------------------------
[Default4.aspxcs] → Default3.aspx.csに同じ
--------------------------------------------------------

次に各ページのリソースをja-JP,en-USで作り、カルチャーが切り替わるのを確認しました。
また、Default3.aspxとDefault4.aspxをリンクで行き来すると
切り替えたカルチャの設定も保持されているようなのでこれで大丈夫と思いますが、
私も多言語ページを本格的に作ったことはないのでどこか変なコードを書いているかもしれません。
そのあたりはご了承を。



Youme
会議室デビュー日: 2008/02/26
投稿数: 7
お住まい・勤務地: Lexington, KY
投稿日時: 2008-02-27 02:22
くまっちさん、BTさん
ご助言ありがとうございました。

お二人のご助言を参考に、Global.asaxを使って実現できました。
ご報告しようとサイトに戻ってきてみると、BTさんのサンプルコードが掲載されており、びっくりしました。お時間を割いて、コードの作成までして頂いて本当にありがとうございます。
VBでのコードになりますが、私のコードも載せておきたいと思います。
同様の問題を抱えていらっしゃる方の助けになればと幸いです。
私の主観ですが、Global.asaxを利用しないBTさんのサンプルコードの方が柔軟なサイトを構築できると思います。私もBTさんのコードに移行するつもりです。

------------------------------------------------------------

[Site.Master]
<%@ Master Language="VB" AutoEventWireup="false" CodeBehind="Site.master.vb" Inherits="WebApplication.Site" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Master Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
&nbsp;<asp:DropDownList ID="ddlLanguage" runat="server" AutoPostBack="True" Width="200px">
<asp:ListItem Selected="True" Value="ja-JP">日本語</asp:ListItem>
<asp:ListItem Value="en-US">英語</asp:ListItem>
</asp:DropDownList>
<asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server">
</asp:ContentPlaceHolder>
</div>
</form>
</body>
</html>

------------------------------------------------------------
[Global.asax] - 不要なイベントは載せておりません。
Imports System.Web.SessionState
Imports System.Threading.Thread
Imports System.Globalization

Public Class Global_asax
Inherits System.Web.HttpApplication

Public Shared language As String = "en-US"

Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs)
' セッションの開始時に呼び出されます。
If Request.UserLanguages.Length > 0 AndAlso Request.UserLanguages(0).Contains("ja") Then
language = "ja-JP"
End If
End Sub

Sub Application_BeginRequest(ByVal sender As Object, ByVal e As EventArgs)
' 各要求の開始時に呼び出されます。
For Each key As String In Request.Form.AllKeys
If key.Contains("ddlLanguage") Then
language = Request.Form(key)
End If
Next
CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(language)
CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(language)
End Sub

End Class

------------------------------------------------------------

[ExtendPage.vb] - 親クラス
Imports System.Threading.Thread
Imports System.Globalization

Public MustInherit Class ExtendPage
Inherits System.Web.UI.Page

Protected Overrides Sub OnPreInit(ByVal e As System.EventArgs)
If Not IsPostBack Then
Me.UICulture = Global_asax.language
Dim ddl As DropDownList = DirectCast(Me.Master.FindControl("ddlLanguage"), DropDownList)
If Not ddl Is Nothing Then
ddl.SelectedValue = Global_asax.language
End If
End If
MyBase.OnPreInit(e)
End Sub
End Class

------------------------------------------------------------

[WebForm1.aspx]
<%@ Page Language="vb" AutoEventWireup="false" MasterPageFile="~/Site.Master" CodeBehind="WebForm1.aspx.vb" Inherits="WebApplication.WebForm1"
title="Form1" %>
<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
<asp:Label ID="Label1" runat="server" Text="<%$ Resources:Label %>"></asp:Label>
<asp:HyperLink ID="HyperLink1" runat="server" NavigateUrl="~/WebForm2.aspx">WebForm2</asp:HyperLink>
</asp:Content>

------------------------------------------------------------

[WebForm2.aspx]
<%@ Page Language="vb" AutoEventWireup="false" MasterPageFile="~/Site.Master" CodeBehind="WebForm2.aspx.vb" Inherits="WebApplication.WebForm2"
title="Form2" %>
<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
<asp:Label ID="Label1" runat="server" Text="<%$ Resources:Label %>"></asp:Label>
<asp:HyperLink ID="HyperLink1" runat="server" NavigateUrl="~/WebForm1.aspx">WebForm1</asp:HyperLink>
</asp:Content>


------------------------------------------------------------
最後に各ページのリソースをja-JP,en-USで作り、作業は完了です。
WebForm1.aspxとWebForm2.aspx をリンクで行き来しても、前画面と同じカルチャで表示されます。

以上、長文失礼しました。



BT
ベテラン
会議室デビュー日: 2006/09/24
投稿数: 81
お住まい・勤務地: Tokyo
投稿日時: 2008-02-27 09:13
どういたしまして。
ちょうど同じような試験をしていたところなので、
ほぼ同じコードがあって、それを少し改良しただけですから。
ファイル名にTestが付いた上で3と4なのはそのためで、直すのが面倒だったのでそのままアップしました。

私の場合は、この状態でキャッシュを効かせた場合どうなるか等を
試しているのですがなかなか上手くいきません。

多言語化(とモバイル)はあまり事例がないのか、情報が少ないので
共有のためにもこういう場で情報交換できたら良いですね。
Youme
会議室デビュー日: 2008/02/26
投稿数: 7
お住まい・勤務地: Lexington, KY
投稿日時: 2008-02-29 11:09
BTさん
BTさんがおっしゃるように確かに情報が少ないと私も感じました。
私はPGとしては未熟ですが、せっかくの経験を通じて情報提供/問題提起をできればと思います。

ところで、キャッシュにはどのような情報を格納しようとしているのでしょうか?
もし、差し支えなければお教え頂けませんか?

思いつくのは、
DropDownListで選択した情報をキャッシュに格納->ページ遷移後にListに選択した情報をセットする
という流れでしょうか?
BT
ベテラン
会議室デビュー日: 2006/09/24
投稿数: 81
お住まい・勤務地: Tokyo
投稿日時: 2008-02-29 14:50
Youmeさんこんにちは。

引用:

Youmeさんの書き込み (2008-02-29 11:09) より:
BTさんがおっしゃるように確かに情報が少ないと私も感じました。



多言語とモバイルに情報が少ない理由は、
ASP.NETが主にイントラでの業務システムで使われているためではないかと想像しています。
インターネットに公開しているサーバはやっぱり非Windows系が多いですし。
事例はこれから増えていくにしても、最初はいろいろ試行錯誤するしかないのかな?とあきらめていますけど。
(インターネットの事例が増えるかどうかは、以前レスを立てたモバイルコントロールの動向もかなり関係してくると思うので、Microsoftさんにはそのあたりをはっきりさせて欲しいですね。)

引用:

ところで、キャッシュにはどのような情報を格納しようとしているのでしょうか?
もし、差し支えなければお教え頂けませんか?

思いつくのは、
DropDownListで選択した情報をキャッシュに格納->ページ遷移後にListに選択した情報をセットする
という流れでしょうか?



あっいや、そちら(Cacheオブジェクト)ではなくて、@OutputCacheディレクティブの方です。
サーバの負荷を軽減するために、一定時間はキャッシュからレスポンスを返すようにする仕組みですが、
これまで、動的なページはキャッシュ無しとし、
ほぼ静的なページは長めのキャッシュ時間(混在時はフラグメントキャッシュ)
で特に問題もなかったのですが、
今回のような多言語化ページに単純にキャッシュを効かせた場合には、
キャッシュ時間内は言語を切り替えてもうまく切り替わらなくなってしまいます。
そこで、ディレクティブ内でVaryByControl,VaryByHeaderとかを使って、
言語切り替え時には別ページとして認識させるようにする必要があるのですが、
DropDownListを使うと、こういう問題があったり、
http://support.microsoft.com/kb/822317/ja
そもそもMasterPage内にDropDownListを置いたらVaryByControlでどうやって指定するのか?
などなど、様々な検討事項が出てきました。

ちなみに上述のDropDownListの問題は、対応策が書いてあったので試してみたのですが、
どうも上手く動かない(キャッシュが効いたままになる)ので悩んでいたところです。
(これがDefault1.aspx,Default2.aspxというわけです)

これに、
http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=43690&forum=7&0
で質問した事項が加わってさらにややこしいことになっているというのが現状ですが、
とりあえずはキャッシュを効かせないで作業を進めることとし、最終的には言語によりURLを書き換える事でクリアしようかと考えています。
また言語選択は、
http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=43575&forum=7&4
で質問したSEOを考慮してDropDownListではなくHyperLinkを使う方向で考えています。
1

スキルアップ/キャリアアップ(JOB@IT)