「VC PlusPlus:MUI(Multilingual User Interface)のリソースファイルの作り方 言語別リソース」の版間の差分
410行目: | 410行目: | ||
が実行されます。理解できない人は[[VC PlusPlus:MSBuild|MSbuild]]関連の記事を理解する必要があります。 | |||
2024年2月16日 (金) 16:18時点における版
VC Cpp記事に戻る。
概要
MUIに関する情報はMicrosoft公式の情報が詳しいです。このSiteでもわかりやすく記事にしようと思っていますが、記事の作成には時間がかかるだろうなと思います。
まず、日本語と英語だけのMUIを作ってみたいと思います。以下のような形式になります。
プロジェクトの中で言語別にリソースファイル・リソーススクリプトを所持します。ここではMultilingualApp(MultilingualApp.vcxproj)というプロジェクトにMultilingualApp.exeというアプリを作成するものに対して、日本語のリソーススクリプトMultilingualApp.ja-JP.rcと英語のリソーススクリプトMultilingualApp.en-US.rcが用意されているとして説明します。
MUIを使うとMultilingualApp.exeという実行ファイルがある階層に作られるja-JPフォルダに日本語のリソースであるMultilingualApp.exe.muiやen-USフォルダに英語リソースであるMultilingualApp.exe.muiを配置します。
通常VisualStudioでプロジェクトにリソースを参加させた場合、プロジェクト名と同じリソースが生成されるので手動でリソースファイルの名前を変更したり、コピーを作って、言語に関する部分の変更を加えたリソースファイルを作ったりして、MultilingualApp.ja-JP.rcとMultilingualApp.en-US.rcのようなものを作成する必要があります。
そうして、MultilingualApp.vcxprojの中身を手動で以下のように書き換えます。リソースが複数できるので、リソースはMultilingualApp.vcxprojの階層にResourceっていうフォルダを作り、その中にいれて管理します。
.vcxprojにmuiResourceとしてリソースを定義する
以下の通りに記述します。
元のMultilingualApp.vcxprojのリソース定義部分の例
<ItemGroup>
<ResourceCompile Include="MultilingualApp.rc" />
</ItemGroup>
変更後のMultilingualApp.vcxprojのリソース定義部分の例
<ItemGroup>
<MuiResourceCompile Include="Resource\MultilingualApp.en-US.rc">
<FileType>Document</FileType>
<MuiCulture Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">en-US</MuiCulture>
<GenerateLanguageNeutralResource Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</GenerateLanguageNeutralResource>
<MuiCulture Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">en-US</MuiCulture>
<GenerateLanguageNeutralResource Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</GenerateLanguageNeutralResource>
<MuiCulture Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">en-US</MuiCulture>
<GenerateLanguageNeutralResource Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</GenerateLanguageNeutralResource>
<MuiCulture Condition="'$(Configuration)|$(Platform)'=='Release|x64'">en-US</MuiCulture>
<GenerateLanguageNeutralResource Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</GenerateLanguageNeutralResource>
</MuiResourceCompile>
<MuiResourceCompile Include="Resource\MultilingualApp.ja-JP.rc">
<FileType>Document</FileType>
<MuiCulture Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">ja-JP</MuiCulture>
<MuiCulture Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">ja-JP</MuiCulture>
<MuiCulture Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">ja-JP</MuiCulture>
<MuiCulture Condition="'$(Configuration)|$(Platform)'=='Release|x64'">ja-JP</MuiCulture>
</MuiResourceCompile>
</ItemGroup>
と、このようにします。一気に定義が増えましたがResourceCompileタグがMuiResourceCompileタグに変わって、二つのリソースファイル用に
- <MuiResourceCompile Include="Resource\MultilingualApp.en-US.rc">
- <MuiResourceCompile Include="Resource\MultilingualApp.ja-JP.rc">
という二つを定義して、その中に子タグがぶら下がっている感じになっています。子タグは
- <MuiCulture Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">en-US</MuiCulture>
として、en-USなら、実行環境が英語圏の地域の設定になっていれば、MultilingualApp.en-US.rcを使うという意味を持ちます。
さらに英語のリソースファイルの子タグにだけ
- <GenerateLanguageNeutralResource Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</GenerateLanguageNeutralResource>
という設定をしているので、他のリソースで定義されていないものがあれば、このGenerateLanguageNeutralResourceがTrueになっているリソースを参照しようとする宣言です。今回の場合は、リソース定義が当該地域に対して定義されていない場合、Resource\MultilingualApp.en-US.rcを参照しようとします。複数のリソース定義がされている中の一つにだけTrueを設定します。複数のリソースにこの子タグが設定されているとビルドエラーが発生します。こういうのが定義されているリソースファイルを言語中立のリソースファイルと呼びます。
.rcconfigで言語中立の対象となるリソースと地域設定をする対象となるリソースの種類を定義
この部分の設定はまだMUIの序章に過ぎません。そうしたいと思った程度の領域です。
vcxprojファイルにMUIのための*.rcconfigというリソース設定ファイルを各構成ファイルごとに作成、設置する作業をします。*.rcconfigには、リソースの種類ごとにどれが、言語中立のリソースファイルの対象にするか?リソースの種類のどれを地域ごと、言語ごとのリソースファイルにするかというのを指定します。例えば、リソースの種類の内、HTML、アイコン、カーソル、マニュフェストだけを言語中立の対象として、地域ごと、言語ごとはHTML、アイコン、カーソル、マニュフェスト、メニュー、ダイアログボックス、文字列テーブル、バージョン情報という種類を対象にするなら以下のようなファイルになります。
MultilingualApp.rcconfig
<?xml version="1.0" encoding="utf-8"?>
<localization>
<resources>
<win32Resources fileType="Application">
<neutralResources>
<resourceType typeNameId="#2"/><!--アイコン-->
<resourceType typeNameId="#3"/><!--カーソル-->
<resourceType typeNameId="#9"/><!--HTML-->
<resourceType typeNameId="RT_MANIFEST"/><!--マニュフェスト-->
</neutralResources>
<localizedResources>
<resourceType typeNameId="#2"/><!--アイコン-->
<resourceType typeNameId="#3"/><!--カーソル-->
<resourceType typeNameId="#9"/><!--HTML-->
<resourceType typeNameId="RT_MANIFEST"/><!--マニュフェスト-->
<resourceType typeNameId="#4"/><!--メニュー-->
<resourceType typeNameId="#5"/><!--ダイアログ-->
<resourceType typeNameId="#6"/><!--文字列-->
<resourceType typeNameId="#241"/><!--バージョン情報-->
</localizedResources>
</win32Resources>
</resources>
</localization>
というファイルを作ったらMultilingualApp.vcxprojファイルの中のそれぞれの構成(32bit・64bit Debug・リリースの組み合わせで4種類くらいはあるのが普通)に対して、以下のような、ハイライトした部分のようなXMLを挿入します。
<!--省略-->
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<!--省略-->
</ClCompile>
<ResourceCompile>
<!--省略-->
</ResourceCompile>
<Link>
<!--省略-->
</Link>
<MuiResourceCompile>
<RCConfigFileName>Resource\MultilingualApp.rcconfig</RCConfigFileName>
</MuiResourceCompile>
<!--省略-->
.targetsでファイルをインポートしてMSBuildフォーマットで.exe.muiファイルを生成するように記述
まだまだ、あります。.exe.muiファイルを生成するにはMSBuildフォーマットで処理を記載しますが、外だしにして管理した方がわかりやすいくらい複雑なので、インポートする形式を使います。仮にインポートするファイルの名前をMuiResourceCompile.targetsという名前として話を進めます。Multilingual.vcxprojファイルの中でMuiResourceCompile.targetsファイルをインポートします。MuiResourceCompile.targetsファイルにはMultilingualApp.exe.muiというファイルを生成するためのスクリプトを記述します。スクリプトの説明はかなりややこしいので、まずは、MultilingualApp.vcxprojファイルにインポートする記述から紹介します。他のimportタグの記述が既存であると思うので、最後に記述するとよいですね。
<!--省略-->
<Import…省略
<Import Project="$(MSBuildThisFileDirectory)MuiResourceCompile.targets" />
<!--省略-->
MSBuildフォーマットというものを理解しなければなりませんが、ここでは使うものだけの役割に絞って話を進めていきます。
MSBuildフォーマットでリソースをコンパイルリンク
MuiResourceCompile.targetsファイルの中でコンパイルリンク処理を設定します。実行したいコマンドは以下の流れですね。
最初にStep1です。
英語版のリソースはDebug\x64\MultilingualApp.en-US.resとDebug\x64\MultilingualApp.en-US.ln.resを以下で生成
- rc /l "0x0409" /nologo /fo "Debug\x64\MultilingualApp.en-US.ln.res" /q Resource\MultilingualApp.rcconfig /fm "Debug\x64\MultilingualApp.en-US.res" "Resource\MultilingualApp.en-US.rc"
英語版は言語中立リソースでもあります。
日本語版のリソースはDebug\x64\MultilingualApp.ja-JP.resを以下で生成
- rc /l "0x0409" /nologo /q Resource\MultilingualApp.rcconfig /g1 /fm "Debug\x64\MultilingualApp.ja-JP.res" "Resource\MultilingualApp.ja-JP.rc"
という感じの処理をします。これで、Resourceフォルダの中にあるMultilingualApp.rcconfigとMultilingualApp.en-US.rcやMultilingualApp.ja-JP.rcからDebug\x64\MultilingualApp.en-US.resやDebug\x64\MultilingualApp.en-US.ln.resとDebug\x64\MultilingualApp.ja-JP.resを生成します。以下のrcコマンドで使ったオプションを解説します。*.rcファイルはコンパイルする対象のVisualStudioで編集可能なリソースファイルであり、必須の引数です。
- /l "0x0409"でen-USの言語でコンパイル。
- /nologoは大したことありません。コマンドを実行したときに表示されるリソースコンパイルのバージョンや著作権表示を隠すというそれだけの設定です。あっても気にならない。
- /foのオプションの後に*.ln.resのような言語中立リソースを出力します。
- /qのオプションの後にMUIリソース構築定義をした *.rcconfigファイルを指定します。
- /g1で言語特有のリソースファイルを作ります。
- /fmで出力するファイルのフォルダ名やファイル名を自動化せずに指定できます。自動だと*.rcファイルと同じ場所に *.resファイルが生成されてしまいます。
次が、Step2です。英語版は以下のリンクコマンドです。
プログラムファイルを生成する際のファイル群に言語中立リソース *.ln.resファイルを追加した実行ファイルMultilingualApp.exeを生成。
link /ERRORREPO RT:QUEUE /OUT:"Debug\x64\MultilingualApp.exe" /INCREMENTAL /ILK:"Debug\x64\MultilingualApp.ilk"
/NOLOGO (library).lib … (library).lib
/DELAYLOAD:(Delayload).dll
…
/DELAYLOAD:(Delayload).dll
/MANIFEST:NO /DEBUG:FULL /PDB:"Debug\x64\(Project).pdb" /SUBSYSTEM:WINDOWS /TLBID:1 /DYNAMICBASE /NXCOMPAT
/IMPLIB:"Debug\x64\MultilingualApp.lib"
/MACHINE:X64
Debug\x64\(ProgramFiles).obj
…
Debug\x64\(ProgramFiles).obj
"Debug\x64\MultilingualApp.en-US.ln.res"
英語版リソースのリンク処理でen-US\MultilingualApp.exe.muiを生成
- link /OUT:"C:\Users\(UserID)\source\repos\(ProjectName)\Debug\x64\en-US\MultilingualApp.exe.mui" /NOLOGO /NOENTRY /MACHINE:X64 /DLL "Debug\x64\MultilingualApp.en-US.res"
日本語版リソースのリンク処理でja-JP\MultilingualApp.exe.muiを生成
- link /OUT:"C:\Users\(UserID)\source\repos\(ProjectName)\Debug\x64\ja-JP\MultilingualApp.exe.mui" /NOLOGO /NOENTRY /MACHINE:X64 /DLL "Debug\x64\MultilingualApp.ja-JP.res"
次が、Step3です。
英語版のMUIリソースにチェックサムを挿入します。
- muirct -c "MultilingualApp.exe" -e "en-US\MultilingualApp.exe.mui"
日本版のMUIリソースにチェックサムを挿入します。
- muirct -c "MultilingualApp.exe" -e "en-US\MultilingualApp.exe.mui"
muirctツールのオプションは以下の通り、
- -c リソースチェックサムの抽出または計算に使用する入力checksum_fileを指定します。 Checksum_fileは、ローカライズ可能なリソースを含むWin32バイナリファイルです。
- -e は-c スイッチで提供されるchecksum_fileに含まれるリソース チェックサムを抽出し、指定したoutput_fileに挿入します。 -e を指定すると、MUIRCT は -c スイッチ以外のすべてのスイッチを無視します。
このような3Stepを実行するようなMuiResourceCompile.targetファイルを作成する必要があります。
MSBuildフォーマットで上記の流れの処理を作る
この項目が非常に難しいことになりますが、MSBuildフォーマットで前の項目で記述したような処理の流れができるようにxmlファイルを構築しなければなりません。大きく分けて3つのTargetが必要になります。
AfterResourceCompileの部分でrcコマンドを実行して、Link処理の手前の$(ComputeMUIRCGeneratedLinkInputs)にターゲットを追加するなりして、Link処理にDebug\x64\MultilingualApp.en-US.ln.resを含めるように変更をして、$(AfterLinkTargets)にターゲットを追加するなりして、リンク処理後にDebug\x64\MultilingualApp.en-US.resとDebug\x64\MultilingualApp.ja-JP.resを生成するLink処理を実行して、最後にチェックサムを反映させるべく、muirctコマンドを実行するという流れです。
まずは3stepの処理になるので、それぞれのタイミングを考慮したターゲットを3つ追加してみます。
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- リソースのコンパイル後 *.ln.resと*.resを生成-->
<Target Name="MyAfterResourceCompile" AfterTargets="AfterResourceCompile">
</Target>
<!-- リンク処理の前のリンク入力 *.ln.resをLinkファイル対象に付け足す-->
<PropertyGroup>
<ComputeLinkInputsTargets>$(ComputeLinkInputsTargets);MyComputeMUIRCGeneratedLinkInputs</ComputeLinkInputsTargets>
</PropertyGroup>
<Target Name="MyComputeMUIRCGeneratedLinkInputs">
</Target>
<!-- リンク処理後 *.exe.mui を生成して、muirctコマンドでチェックサムを*.exe.muiに挿入-->
<PropertyGroup>
<AfterLinkTargets>$(AfterLinkTargets);MyAfterLinkTarget</AfterLinkTargets>
</PropertyGroup>
<Target Name="MyAfterLinkTarget">
</Target>
</Project>
11行目~13行目の<PropertyGroup>~</PropertyGroup>は以下のようなプロパティ代入処理をやっているのと同じことです。
- $(ComputeLinkInputsTargets) = "$(ComputeLinkInputsTargets);MyComputeMUIRCGeneratedLinkInputs"
21行目~23行目の<PropertyGroup>~</PropertyGroup>は以下のようなプロパティ代入処理をやっているのと同じことです。
- $(AfterLinkTargets) = "$(AfterLinkTargets);MyAfterLinkTarget"
これで動かしたいタイミングのターゲットが3つ作ることができました。ひとつづつ作ってみましょう。
rcリソースコンパイル後の再度のrcコマンド発行まで
以下のようになります。ちょっと考えましたが、ファイル選択コンパイルに対応していません。道のりは長いな。ファイル選択コンパイルの時はコンパイルしないとかするとかの判断まで考えないといけないのですね。むずい。
…省略…
<ItemDefinitionGroup>
<MuiResourceCompile>
<GenerateLanguageNeutralResource Condition="'%(MuiResourceCompile.GenerateLanguageNeutralResource)' == ''">false</GenerateLanguageNeutralResource>
<!-- en-US のとき ヌル、 ja-JPのときfalse -->
<ResourceOutputLNFileName Condition="'%(MuiResourceCompile.ResourceOutputLNFileName)' == ''">$(IntDir)%(Filename).ln.res</ResourceOutputLNFileName>
<!-- Debug\x64\MultilingaulApp.en-US.ln.res;Debug\x64\Multilingaul.ja-JP.ln.res -->
<ResourceOutputMuiFileName Condition="'%(MuiResourceCompile.ResourceOutputMuiFileName)' == ''">$(IntDir)%(Filename).res</ResourceOutputMuiFileName>
<!-- Debug\x64\MultilingaulApp.en-US.res;Debug\x64\Multilingaul.ja-JP.res -->
<TrackerLogDirectory Condition="'%(MuiResourceCompile.TrackerLogDirectory)' == ''">$(TLogLocation)</TrackerLogDirectory>
<!-- Debug\x64\ffftp.tlog\ -->
</MuiResourceCompile>
</ItemDefinitionGroup>
<!-- リソースのコンパイル後 *.ln.resと*.resを生成-->
<Target Name="MyAfterResourceCompile" AfterTargets="AfterResourceCompile">
<ItemGroup>
<MuiRcDirsToMake Include="@(MuiResourceCompile->Metadata('ResourceOutputLNFileName')->DirectoryName()->Distinct()->ClearMetaData())" />
<MuiRcDirsToMake Include="@(MuiResourceCompile->Metadata('ResourceOutputMuiFileName')->DirectoryName()->Distinct()->ClearMetaData())" />
</ItemGroup>
<MakeDir Directories="@(MuiRcDirsToMake)" />
<ItemGroup>
<RcDirsToMake Remove="@(MuiRcDirsToMake)" /><!-- ディレクトリを作ったらMuiRcDirsToMakeアイテムはいらないので消す。-->
</ItemGroup>
<ItemGroup>
<MuiResourceCompile>
<AdditionalOptions Condition="'%(MuiResourceCompile.ResourceOutputMuiFileName)' != ''">/fm"%(MuiResourceCompile.ResourceOutputMuiFileName)" %(MuiResourceCompile.AdditionalOptions)</AdditionalOptions>
<!-- /fm"Debug\x64\MultilingaulApp.en-US.res -->
<!-- /fm"Debug\x64\MultilingaulApp.ja-JP.res -->
</MuiResourceCompile>
<MuiResourceCompile>
<AdditionalOptions Condition="'%(MuiResourceCompile.GenerateLanguageNeutralResource)' != 'true'">/g1 %(MuiResourceCompile.AdditionalOptions)</AdditionalOptions>
<ResourceOutputLNFileName Condition="'%(MuiResourceCompile.GenerateLanguageNeutralResource)' != 'true'"></ResourceOutputLNFileName>
<!-- /fm"Debug\x64\MultilingaulApp.en-US.res -->
<!-- g1 /fm"Debug\x64\MultilingaulApp.ja-JP.res -->
</MuiResourceCompile>
<MuiResourceCompile>
<AdditionalOptions Condition="'%(MuiResourceCompile.RCConfigFileName)' != ''">/q %(MuiResourceCompile.RCConfigFileName) %(MuiResourceCompile.AdditionalOptions)</AdditionalOptions>
</MuiResourceCompile>
</ItemGroup>
<PropertyGroup>
<RCToolArchitecture Condition="'$(RCToolArchitecture)' == ''">$(WindowsSDKToolArchitecture)</RCToolArchitecture>
</PropertyGroup>
<RC
Condition ="'%(MuiResourceCompile.ExcludedFromBuild)'!='true'"
Source ="%(MuiResourceCompile.Identity)"
AdditionalOptions ="%(MuiResourceCompile.AdditionalOptions)"
Culture ="%(MuiResourceCompile.Culture)"
ResourceOutputFileName ="%(MuiResourceCompile.ResourceOutputLNFileName)"
SuppressStartupBanner ="%(MuiResourceCompile.SuppressStartupBanner)"
TrackerLogDirectory ="%(MuiResourceCompile.TrackerLogDirectory)"
ToolArchitecture ="$(RCToolArchitecture)"
TrackFileAccess ="$(TrackFileAccess)"
>
</RC>
</Target>
…省略…
こんな感じです。ResourceOutputLNFileNameは英語のときは中立リソース条件の'%(MuiResourceCompile.GenerateLanguageNeutralResource)' != 'true'が成り立たないので空設定はされず、日本語のときは空に設定されます。これで、
rc /l 0x0409" /nologo /fo "Debug\x64\ffftp.en-US.ln.res" /q Resource\ffftp.rcconfig /fm "Debug\x64\ffftp.en-US.res" "Resource\ffftp.en-US.rc"
rc /l"0x0409" /nologo /q Resource\ffftp.rcconfig /g1 /fm "Debug\x64\ffftp.ja-JP.res" "Resource\ffftp.ja-JP.rc"
が実行されます。理解できない人はMSbuild関連の記事を理解する必要があります。
VC Cpp記事に戻る。