Eto.Formsプルダウンメニューの翻訳対応

Eto.Formsの記事を書いてきましたが、実はEto、プルダウンメニューに若干の問題があります。それは、「ファイル」メニューと「ヘルプ」メニュー。

プルダウンメニューの項目名を普通に日本語(正しくは英語以外)にすると、思わぬ結果になってしまいます。しかし、MLで簡単な対策が出ていたのでご紹介します。

メニューの生成コードは、デフォルトのHello, World!では(コメントを省くと)次のようになっています。

Menu = new MenuBar
{
    Items =
    {
        new ButtonMenuItem { Text = "&File", Items = { clickMe } },
    },
    ApplicationItems =
    {
        new ButtonMenuItem { Text = "&Preferences..." },
    },
    QuitItem = quitCommand,
    AboutItem = aboutCommand
};

日本語なら、ファイルメニューの名前は「ファイル」か「ファイル (&F)」でしょう。そこで、ButtonMenuItemの部分をnew ButtonMenuItem { Text = "&File", Items = { clickMe } },として実行すると、次のようになってしまいます。

f:id:mokake:20170923183508p:plain

「ファイル (&F)」の左側に参禅と輝く「File」メニュー……。

これは、Etoがもつ特殊メニュー項目のためです。

  • ApplicationMenu : macOS(OS X)のアプリケーションメニューの項目。他OSでは「File」メニューに自動的に入る。
  • QuitItem : 終了コマンド。macOSではアプリケーションメニュー、他ではFileメニューに入る。
  • AboutItem : 「About」「このアプリケーションについて」メニュー。「Help」メニューに入る。

このため、Etoでは自動的に「File」「Help」メニューが作られてしまいます。しかも、ソースを見ると分かる通り、文字列リテラルとして定義されています。

ちなみに、LinuxではFileメニューがないと落ちてしまうようです。

対策ですが、これら特殊アイテムの処理はMenuBarのコンストラクタで行われているため、コンストラクタでは「File」メニューとしておき、「構築後」にテキストを書き換えればOKです。

デフォルトのHello, World!アプリケーションを、少しウインドウサイズを縮めた上で、「File」「Help」メニューに対策を適用すると、次のようなコードになります。なお、大事な部分はコードのほぼ末尾にある2行だけです。そこまではただのHello, World!なのでスルーしてください。

using System;
using Eto.Forms;
using Eto.Drawing;

namespace EtoMenuTest
{
    public class MainForm : Form
    {
        public MainForm()
        {
            Title = "My Eto Form";
            ClientSize = new Size(300, 150);

            Content = new StackLayout
            {
                Padding = 10,
                Items =
                {
                    "Hello World!",
                }
            };

            var clickMe = new Command { MenuText = "Click Me!", ToolBarText = "Click Me!" };
            clickMe.Executed += (sender, e) => MessageBox.Show(this, "I was clicked!");

            var quitCommand = new Command { MenuText = "Quit", Shortcut = Application.Instance.CommonModifier | Keys.Q };
            quitCommand.Executed += (sender, e) => Application.Instance.Quit();

            var aboutCommand = new Command { MenuText = "About..." };
            aboutCommand.Executed += (sender, e) => MessageBox.Show(this, "About my app...");

            Menu = new MenuBar
            {
                Items =
                {
                    new ButtonMenuItem { Text = "File", Items = { clickMe } },
                },
                ApplicationItems =
                {
                    new ButtonMenuItem { Text = "&Preferences..." },
                },
                QuitItem = quitCommand,
                AboutItem = aboutCommand
            };
            Menu.ApplicationMenu.Text = "ファイル (&F)";
            Menu.HelpMenu.Text = "ヘルプ (&H)";

            ToolBar = new ToolBar { Items = { clickMe } };
        }
    }
}

f:id:mokake:20170923191839p:plain

もちろん、実際には設定するテキストはgettextなどから与えれば、各国語対応もできます。