読者です 読者をやめる 読者になる 読者になる
スポンサーリンク

Railsで,簡単にメッセージ管理する方法 (メッセージ定義書からメッセージ処理クラスを自動生成するVBAマクロ)

Ruby on Rails vba ドキュメント 実行可能ドキュメント

アプリケーション開発で,表示文言やエラーメッセージを一元管理したい場合,GetTextを使うのがスタンダードだ。

Ruby on RailsでRuby-GetText-Packageを使う (Rails-2.1.x以前)
http://www.yotabanana.com/hiki/ja/rub...

しかしこの方法は汎用的である反面,結構面倒だったりする。

.po/.moの処理系を別途組み込む必要があるためだ。

また,単にメッセージ管理をしたいだけなら,国際化(i18n)の機能は不要になる。


かわりに

  • メッセージを処理するためのシンプルなクラスを利用することにして
  • そのクラスを,プロジェクトのメッセージ定義書から自動生成する

という方法がある。




Ruby on Rails では,例えば下記のようなクラスを作ればよい。

置き場は lib/simple_msg.rb。

# システム中で利用されるメッセージを保持するクラス
class SimpleMsg


  # メッセージ一覧


  # メインメニュー:正常
  MENU_NORMAL_SELECT = "動作を選択してください。"
  MENU_NORMAL_WELCOME_USER = "ようこそ%{1}さん"

  # メインメニュー:異常
  MENU_ERR_MAINTENANCE = "ただいまメンテナンス中です。"
  MENU_ERR_CANT_LOGIN = "ログインできません。"

  # 入力フォーム:正常
  FORM_NORMAL_PLEASE_INPUT = "下記の項目を入力してください。"
  FORM_NORMAL_NOTICE_HISSU = "※がついた項目は入力必須です。"

  # 入力フォーム:異常
  FORM_ERR_STRLEN_MAX = "%{1}文字以内で入力してください。"
  FORM_ERR_SELECT_MAX = "%{1}は%{2}個まで選択可能です。"
  FORM_ERR_FORBID_DOUBLEQUOTE = "「\"」は使用できません。"


  
  # 置換済みメッセージを出力
  def self.plain(msg_id, args = [])
    str = self.const_get(msg_id)
    args.each_with_index{ |value, index| str = str.sub("%{#{ ( index + 1 ).to_s }}", value.to_s)}
    str
  end
  
  # HTML要素として出力
  def self.span(msg_id, args = [])
    "<span class='#{ msg_id }'>#{ self.plain(msg_id, args) }</span>"
  end

end

このクラスの中に,メッセージID=メッセージ内容 の形式で,メッセージ・文言を保持しておく。

置換文字列は%{n}の形式で埋め込むことが可能。

※メッセージのキーとしてとりあえず,NORMALが正常系,ERRが異常系という分け方をしている。

これは別に,メッセージ呼び出し部分のコードを見た時にそれが何のメッセージなのか伝わりさえすれば何でもよい。

まさかとは思うが,メッセージIDを数字で振るなどの愚行は犯さないように。

使い方は,たとえばビュー(rhtml)の中で


<%= SimpleMsg.span("MENU_NORMAL_WELCOME_USER", [ "ゲスト" ]) %>

<%= SimpleMsg.span("FORM_ERR_SELECT_MAX", [ "カテゴリー", 3 ]) %>

<%= SimpleMsg.span("FORM_ERR_FORBID_DOUBLEQUOTE") %>

とすれば,HTMLとして


<span class='MENU_NORMAL_WELCOME_USER'>ようこそゲストさん</span>

<span class='FORM_ERR_SELECT_MAX'>カテゴリーは3個まで選択可能です。</span>

<span class='FORM_ERR_FORBID_DOUBLEQUOTE'>「"」は使用できません。</span>

が出力される。



そして,そのようなSimpleMsgクラスをExcelのメッセージ定義書から生成するためのシートは,下記からダウンロードできる。

メッセージ処理クラスを生成可能なメッセージ定義書
http://www.name-of-this-site.org/codi...


マクロは下記のとおり。


Sub メッセージ処理クラスを作成()

    ' 定数
    Const adTypeText = 2
    Const adTypeBinary = 1
    
    ' 出力パス
    output_path = get_class_filepath

    ' UTF-8で書き出し(あとでBOMを除去する)
    Dim ados As Object
    Set ados = CreateObject("ADODB.Stream")
    ados.Open
    ados.Type = adTypeText
    ados.Charset = "UTF-8"
    br = vbNewLine ' 改行
    
    ados.WriteText "# システム中で利用されるメッセージを保持するクラス" & br
    ados.WriteText "class SimpleMsg" & br
    
    
    ' シート内をスキャン
    x_page = 2
    x_msgtype = 3
    x_msgid = 4
    x_msgstr = 5
    y_offset = 5
    y = y_offset
    continue_flag = True
    old_page = ""
    old_msgtype = ""
    
    ' 全メッセージに対して
    Do While (continue_flag = True)
    
        ' 行内容を取得
        msg_page = Cells(y, x_page).Value
        msg_type = Cells(y, x_msgtype).Value
        msg_id = Cells(y, x_msgid).Value
        msg_str = Replace(Cells(y, x_msgstr).Value, """", "\""")
        
        ' 有効な行か
        If (Len(msg_id) > 0) And (Len(msg_str) > 0) Then
            
            ' メッセージの種別が変わったか
            If (Not (old_page = msg_page)) Or (Not (old_msgtype = msg_type)) Then
                old_page = msg_page
                old_msgtype = msg_type
            
                ' 種別コメントを振り直し
                ados.WriteText br & "  # " & msg_page & ":" & msg_type & br
            End If
            
            ' 書き出し
            ados.WriteText "  " & msg_id & " = """ & msg_str & """" & br
            
            y = y + 1
        Else
            continue_flag = False
        End If

    Loop
    
    ' クラスメソッド
    ados.WriteText "  " & br
    ados.WriteText "  # 置換済みメッセージを出力" & br
    ados.WriteText "  def self.plain(msg_id, args = [])" & br
    ados.WriteText "    str = self.const_get(msg_id)" & br
    ados.WriteText "    args.each_with_index{ |value, index| str = str.sub(""%{#{ ( index + 1 ).to_s }}"", value.to_s)}" & br
    ados.WriteText "    str" & br
    ados.WriteText "  end" & br
    ados.WriteText "  " & br
    ados.WriteText "  # HTML要素として出力" & br
    ados.WriteText "  def self.span(msg_id, args = [])" & br
    ados.WriteText "    ""<span class='#{ msg_id }'>#{ self.plain(msg_id, args) }</span>""" & br
    ados.WriteText "  end" & br
    ados.WriteText "end" & br
    
    
    ' 先頭のBOM取り
    ados.Position = 0
    ados.Type = adTypeBinary
    ados.Position = 3
    byte_data = ados.Read
    ados.Close

    ' UTF-8Nコードのデータを保存
    ados.Open
    ados.Type = adTypeBinary
    ados.Write byte_data
    ados.SaveToFile output_path, 2
    ados.Close
    
End Sub


' 処理クラス名を返します
Function get_class_name()
    get_class_name = Sheets("設定").Cells(4, 3).Value
End Function


' クラス設置パスを返します
Function get_class_filepath()
    get_class_filepath = Sheets("設定").Cells(5, 3).Value
End Function

VBAでファイル出力をする場合,ふつうはFreeFileを使ってSJISで書き出す。

しかし,Ruby on Rails では何もかもUTF-8NでないとSyntax Errorになったりする。

上記のマクロでは,いったんUTF8(BOMあり)で書きだしたあとで,BOMを除去している。


参考:

CSVファイルをUTF-8Nで作成したい
http://www.vbalab.net/vbaqa/c-board.c...

補足

メッセージはDBに入れればいいじゃないか,と言われそうだが,「ただいま(DBが)メンテナンス中です」とかの文言を出したい時に困るかもしれないのでそれは没。