「VC PlusPlus:MUI(Multilingual User Interface)のリソースファイルの作り方 言語別リソース」の版間の差分

提供:yonewiki
 
(同じ利用者による、間の22版が非表示)
67行目: 67行目:




*<MuiCulture Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">en-US</MuiCulture>
*<MuiCulture Condition="'$(Configuration)|$(Platform)'=='De​bug|Win32'">en-US</MuiCulture>




 として、en-USなら、実行環境が英語圏の地域の設定になっていれば、MultilingualApp.en-US.rcを使うという意味を持ちます。
 として、en-USなら、実行環境が英語圏の地域の設定になっていれば、en-USというキーワードを適用してMultilingualApp.en-US.rcを使うという意味を持ちます。




76行目: 76行目:




*<GenerateLanguageNeutralResource Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</GenerateLanguageNeutralResource>
*<GenerateLanguageNeutralResource Condition="'$(Configuration)|$(Platform)'=='De​bug|Win32'">true</GenerateLanguageNeutralRe​source>




273行目: 273行目:


=== '''MSBuildフォーマットで上記の流れの処理を作る''' ===
=== '''MSBuildフォーマットで上記の流れの処理を作る''' ===
 この項目が非常に難しいことになりますが、MSBuildフォーマットで前の項目で記述したような処理の流れができるようにxmlファイルを構築しなければなりません。大きく分けて2つのTargetが必要になります。
 この項目が非常に難しいことになりますが、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コマンドを実行するという流れです。
 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つ追加してみます。
 
 
<syntaxhighlight lang="xml" line start="1">
<?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>
</syntaxhighlight>
 
 
 11行目~13行目の<PropertyGroup>~</PropertyGroup>は以下のようなプロパティ代入処理をやっているのと同じことです。
 
 
*$(ComputeLinkInputsTargets) = "$(ComputeLinkInputsTargets);MyComputeMUI&#8203;RCGeneratedLinkInputs"
 
 
 21行目~23行目の<PropertyGroup>~</PropertyGroup>は以下のようなプロパティ代入処理をやっているのと同じことです。
 
 
*$(AfterLinkTargets) = "$(AfterLinkTargets);MyAfterLinkTarget"
 
 
 これで動かしたいタイミングのターゲットが3つ作ることができました。ひとつづつ作ってみましょう。
 
 
 
=== '''rcリソースコンパイル後の再度のrcコマンド発行まで''' ===
 以下のようになります。ちょっと考えましたが(管理人もあまりやったことない作業でして、無能なため)、ファイル選択コンパイルに対応していません。道のりは長いな。ファイル選択コンパイルの時はコンパイルしないとかするとかの判断まで考えないといけないのですね。むずい。
 
 
 
==== '''Step1/3:リソースのMUI対応ファイル生成コンパイル部''' ====
 
 あまり解説はしませんが以下のようなMSBuildフォーマット記述になります。
 
 
<syntaxhighlight lang="xml">
…省略…
  <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>
…省略…
</syntaxhighlight>
 
 
 こんな感じです。ResourceOutputLNFileNameは英語のときは中立リソース条件の'%(MuiResourceCompile.GenerateLanguageNeutralRe&#8203;source)' != 'true'が成り立たないので空設定はされず、日本語のときは空に設定されます。これで、
 
 
<syntaxhighlight lang="text">
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"
rc /l"0x0409" /nologo /q Resource\MultilingualApp.rcconfig /g1 /fm "Debug\x64\MultilingualApp.ja-JP.res" "Resource\MultilingualApp.ja-JP.rc"
</syntaxhighlight>
 
 
 が実行されます。理解できない人は[[VC PlusPlus:MSBuild|MSbuild]]関連の記事を理解する必要があります。
 
 
 
==== '''Step2/3:リンク前処理でln.resをリンク対象にする''' ====
 
<syntaxhighlight lang="xml">
…省略…
  <PropertyGroup>
    <ComputeLinkInputsTargets>$(ComputeLinkInputsTargets);MyComputeMUIRCGeneratedLinkInputs</ComputeLinkInputsTargets>
  </PropertyGroup>
 
  <ItemDefinitionGroup>
    <MuiResourceCompile>
      <LinkCompiled>$(LinkCompiled)</LinkCompiled>
    </MuiResourceCompile>
  </ItemDefinitionGroup>
 
  <Target Name="MyComputeMUIRCGeneratedLinkInputs">
    <ItemGroup Condition="'@(MuiResourceCompile)' != ''">
      <LnResObj Condition="'%(MuiResourceCompile.ResourceOutputLNFileName)' != '' and '%(MuiResourceCompile.ExcludedFromBuild)'!='true'" Include="@(MuiResourceCompile->'%(ResourceOutputLNFileName)')">
        <LinkCompiled>%(MuiResourceCompile.LinkCompiled)</LinkCompiled>
      </LnResObj>
      <MuiResObj Condition="'%(MuiResourceCompile.ResourceOutputMuiFileName)' != '' and '%(MuiResourceCompile.ExcludedFromBuild)'!='true'" Include="@(MuiResourceCompile->'%(ResourceOutputMuiFileName)')">
        <LinkCompiled>%(MuiResourceCompile.LinkCompiled)</LinkCompiled>
      </MuiResObj>
    </ItemGroup>
 
    <ItemGroup>
      <Link Include="@(LnResObj->WithMetadataValue('LinkCompiled', 'true')->ClearMetadata())" />
      <MuiLink Include="@(MuiResObj->WithMetadataValue('LinkCompiled', 'true')->ClearMetadata())" />
    </ItemGroup>
 
  </Target>
…省略…
</syntaxhighlight>
 
 
 この部分を処理することによって、以下のようにLinkアイテムとMuiLinkアイテムに設定した状態になります。
 
 
<syntaxhighlight lang="text">
@Link = @(Link);Debug\x64\Multilingaul.en-US.ln.res
@MuiLink = @(MuiLink);Debug\x64\Multilingaul.en-US.res;Debug\x64\Multilingaul.ja-JP.res
</syntaxhighlight>
 
 
 
==== '''Step3/3:リンク後処理で、*.exe.muiを生成し、チェックサムを追記する。''' ====
 
 
<syntaxhighlight lang="xml">
…省略…
  <PropertyGroup>
    <AfterLinkTargets>$(AfterLinkTargets);MyAfterLinkTarget</AfterLinkTargets>
  </PropertyGroup>
 
  <Target Name="MyAfterLinkTarget">
    <PropertyGroup>
      <MuiOutputDirectoryName>@(Link->Metadata(OutputFile)->DirectoryName()->Distinct()->ClearMetadata())</MuiOutputDirectoryName>
    </PropertyGroup>
 
    <ItemGroup Condition="'@(MuiResourceCompile)' != ''">
      <MuiLinkDirsToMake Include="@(MuiResourceCompile->'$(MuiOutputDirectoryName)\%(MuiCulture)')" />
    </ItemGroup>
 
    <MakeDir Directories="@(MuiLinkDirsToMake)" />
 
    <ItemGroup>
      <MuiLinkDirsToMake Remove="@(MuiLinkDirsToMake)" />
    </ItemGroup>
 
    <PropertyGroup>
      <MuiOutputDirectoryName>@(Link->Metadata(OutputFile)->DirectoryName()->Distinct()->ClearMetadata())</MuiOutputDirectoryName>
      <LNOutputFileName>@(Link->Metadata(OutputFile)->'%(Filename)%(Extension)'->Distinct()->ClearMetadata())</LNOutputFileName>
      <MuiOutputFileName>@(Link->Metadata(OutputFile)->'%(Filename)%(Extension).mui'->Distinct()->ClearMetadata())</MuiOutputFileName>
      <MuiTargetMachine>@(Link->Metadata(TargetMachine)->Distinct())</MuiTargetMachine>
    </PropertyGroup>
 
    <Link
        BuildingInIDE                  ="$(BuildingInsideVisualStudio)"
        Sources                        ="%(MuiResourceCompile.ResourceOutputMuiFileName)"
 
        LinkDLL                        ="true"
        OutputFile                    ="%(MuiResourceCompile.OutputMuiFileName)"
        NoEntryPoint                  ="true"
        SuppressStartupBanner          ="true"
        TargetMachine                  ="$(MuiTargetMachine)"
 
        TrackerLogDirectory            ="$(TLogLocation)"
 
        TrackFileAccess                ="$(TrackFileAccess)"
        ToolArchitecture              ="$(LinkToolArchitecture)"
    >
    </Link>
 
  <Exec WorkingDirectory="$(MuiOutputDirectoryName)" Command="muirct -c &quot;$(LNOutputFileName)&quot; -e &quot;%(MuiResourceCompile.MuiCulture)\$(MuiOutputFileName)&quot;" />
 
  </Target>
…省略…
</syntaxhighlight>
 
 
 この部分を処理することによって、以下のようにLink処理とMUIRCT処理をした状態になります。
 
 
<syntaxhighlight lang="text">
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"
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"
muirct -c "MultilingualApp.exe" -e "en-US\MultilingualApp.exe.mui"
muirct -c "MultilingualApp.exe" -e "en-US\MultilingualApp.exe.mui"
</syntaxhighlight>
 
 
 単体コンパイルに対応するには、もう少し改善が必要となりましょうか?他にもいろいろまだわかっていないキーワードもありますので、次回更新のときには、そのあたりも理解して、前に進みたいものです。ちなみに毎回自動でMUI関連のビルドをするためにMSBuildフォーマットでスクリプトを書いたわけですが、毎回、手でコマンドを打って、国際化をはかる部分の処理を吐き出してもいいわけです。
 
 
 完全に出来上がってから、国際化を図ったリソースファイルを作ってちょちょいっとやるだけなので、大したコマンド入力の回数ではありません。MSBuildフォーマットの難しさは半端ないです。誰に迷惑をかけるわけでもありません。オープンソースにするときも手でコマンドを打ちますってヘルプファイルを付けておけば、このようなMSBuildフォーマットを書く手間は省けます。と、このような向上心の無い管理人の言葉を真に受けず、理解する努力をした方が素晴らしいに決まっています。
 
 
 ちなみに、大きな山場を越えましたが、MUIリソースファイルの作り方の手順は終わっていません。まだ続きます。やばいっしょこれ。疲れる。国際化のための手法のたったひとつの手段にすぎないのに理解するのにおそろしく時間がかかるし、説明するのにも時間がかかる。こんなもの理解してる人が世界でどれくらいいるんだろう。ググっても、こんなことやってる人に出会えなかったし、まったくヒットしません。MUIのやり方を説明しているわけではないですが、MUIを取り込んでいるオープンソースはありまして、FFFTPを受け継いでメンテしてる[https://twitter.com/haxe 倉田佐祐理(KURATA Sayuri)]氏だけですね。知らない人ですけど。この記事もほとんど、著作権無視してるくらいコードをパクパクして、パクって作ってるだけですし、すごいよ、この人。どうやって知識を得ているのか気になる木。絵師であり、コーダーでもあり、変態でもあるという勝手な印象です。
 
 
=== '''.vcxprojからmuiファイルをあやつる''' ===
 


 
 


[[VC_PlusPlus#記事|VC Cpp記事]]に戻る。
[[VC_PlusPlus#記事|VC Cpp記事]]に戻る。

2024年3月9日 (土) 22:11時点における最新版

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)'=='De​bug|Win32'">en-US</MuiCulture>


 として、en-USなら、実行環境が英語圏の地域の設定になっていれば、en-USというキーワードを適用してMultilingualApp.en-US.rcを使うという意味を持ちます。


 さらに英語のリソースファイルの子タグにだけ


  • <GenerateLanguageNeutralResource Condition="'$(Configuration)|$(Platform)'=='De​bug|Win32'">true</GenerateLanguageNeutralRe​source>


 という設定をしているので、他のリソースで定義されていないものがあれば、この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.resDebug\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);MyComputeMUI​RCGeneratedLinkInputs"


 21行目~23行目の<PropertyGroup>~</PropertyGroup>は以下のようなプロパティ代入処理をやっているのと同じことです。


  • $(AfterLinkTargets) = "$(AfterLinkTargets);MyAfterLinkTarget"


 これで動かしたいタイミングのターゲットが3つ作ることができました。ひとつづつ作ってみましょう。

 

rcリソースコンパイル後の再度のrcコマンド発行まで

 以下のようになります。ちょっと考えましたが(管理人もあまりやったことない作業でして、無能なため)、ファイル選択コンパイルに対応していません。道のりは長いな。ファイル選択コンパイルの時はコンパイルしないとかするとかの判断まで考えないといけないのですね。むずい。

 

Step1/3:リソースのMUI対応ファイル生成コンパイル部

 あまり解説はしませんが以下のようなMSBuildフォーマット記述になります。


…省略…
  <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.GenerateLanguageNeutralRe​source)' != 'true'が成り立たないので空設定はされず、日本語のときは空に設定されます。これで、


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"
rc /l"0x0409" /nologo /q Resource\MultilingualApp.rcconfig /g1 /fm "Debug\x64\MultilingualApp.ja-JP.res" "Resource\MultilingualApp.ja-JP.rc"


 が実行されます。理解できない人はMSbuild関連の記事を理解する必要があります。

 

Step2/3:リンク前処理でln.resをリンク対象にする

…省略…
  <PropertyGroup>
    <ComputeLinkInputsTargets>$(ComputeLinkInputsTargets);MyComputeMUIRCGeneratedLinkInputs</ComputeLinkInputsTargets>
  </PropertyGroup>

  <ItemDefinitionGroup>
    <MuiResourceCompile>
      <LinkCompiled>$(LinkCompiled)</LinkCompiled>
    </MuiResourceCompile>
  </ItemDefinitionGroup>

  <Target Name="MyComputeMUIRCGeneratedLinkInputs">
    <ItemGroup Condition="'@(MuiResourceCompile)' != ''">
      <LnResObj Condition="'%(MuiResourceCompile.ResourceOutputLNFileName)' != '' and '%(MuiResourceCompile.ExcludedFromBuild)'!='true'" Include="@(MuiResourceCompile->'%(ResourceOutputLNFileName)')">
        <LinkCompiled>%(MuiResourceCompile.LinkCompiled)</LinkCompiled>
      </LnResObj>
      <MuiResObj Condition="'%(MuiResourceCompile.ResourceOutputMuiFileName)' != '' and '%(MuiResourceCompile.ExcludedFromBuild)'!='true'" Include="@(MuiResourceCompile->'%(ResourceOutputMuiFileName)')">
        <LinkCompiled>%(MuiResourceCompile.LinkCompiled)</LinkCompiled>
      </MuiResObj>
    </ItemGroup>

    <ItemGroup>
      <Link Include="@(LnResObj->WithMetadataValue('LinkCompiled', 'true')->ClearMetadata())" />
      <MuiLink Include="@(MuiResObj->WithMetadataValue('LinkCompiled', 'true')->ClearMetadata())" />
    </ItemGroup>

  </Target>
…省略…


 この部分を処理することによって、以下のようにLinkアイテムとMuiLinkアイテムに設定した状態になります。


@Link = @(Link);Debug\x64\Multilingaul.en-US.ln.res
@MuiLink = @(MuiLink);Debug\x64\Multilingaul.en-US.res;Debug\x64\Multilingaul.ja-JP.res

 

Step3/3:リンク後処理で、*.exe.muiを生成し、チェックサムを追記する。

…省略…
  <PropertyGroup>
    <AfterLinkTargets>$(AfterLinkTargets);MyAfterLinkTarget</AfterLinkTargets>
  </PropertyGroup>

  <Target Name="MyAfterLinkTarget">
    <PropertyGroup>
      <MuiOutputDirectoryName>@(Link->Metadata(OutputFile)->DirectoryName()->Distinct()->ClearMetadata())</MuiOutputDirectoryName>
    </PropertyGroup>

    <ItemGroup Condition="'@(MuiResourceCompile)' != ''">
      <MuiLinkDirsToMake Include="@(MuiResourceCompile->'$(MuiOutputDirectoryName)\%(MuiCulture)')" />
    </ItemGroup>

    <MakeDir Directories="@(MuiLinkDirsToMake)" />

    <ItemGroup>
      <MuiLinkDirsToMake Remove="@(MuiLinkDirsToMake)" />
    </ItemGroup>

    <PropertyGroup>
      <MuiOutputDirectoryName>@(Link->Metadata(OutputFile)->DirectoryName()->Distinct()->ClearMetadata())</MuiOutputDirectoryName>
      <LNOutputFileName>@(Link->Metadata(OutputFile)->'%(Filename)%(Extension)'->Distinct()->ClearMetadata())</LNOutputFileName>
      <MuiOutputFileName>@(Link->Metadata(OutputFile)->'%(Filename)%(Extension).mui'->Distinct()->ClearMetadata())</MuiOutputFileName>
      <MuiTargetMachine>@(Link->Metadata(TargetMachine)->Distinct())</MuiTargetMachine>
    </PropertyGroup>

    <Link
        BuildingInIDE                  ="$(BuildingInsideVisualStudio)"
        Sources                        ="%(MuiResourceCompile.ResourceOutputMuiFileName)"

        LinkDLL                        ="true"
        OutputFile                     ="%(MuiResourceCompile.OutputMuiFileName)"
        NoEntryPoint                   ="true"
        SuppressStartupBanner          ="true"
        TargetMachine                  ="$(MuiTargetMachine)"

        TrackerLogDirectory            ="$(TLogLocation)"

        TrackFileAccess                ="$(TrackFileAccess)"
        ToolArchitecture               ="$(LinkToolArchitecture)"
    >
    </Link>

   <Exec WorkingDirectory="$(MuiOutputDirectoryName)" Command="muirct -c &quot;$(LNOutputFileName)&quot; -e &quot;%(MuiResourceCompile.MuiCulture)\$(MuiOutputFileName)&quot;" />

  </Target>
…省略…


 この部分を処理することによって、以下のようにLink処理とMUIRCT処理をした状態になります。


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"
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"
muirct -c "MultilingualApp.exe" -e "en-US\MultilingualApp.exe.mui"
muirct -c "MultilingualApp.exe" -e "en-US\MultilingualApp.exe.mui"


 単体コンパイルに対応するには、もう少し改善が必要となりましょうか?他にもいろいろまだわかっていないキーワードもありますので、次回更新のときには、そのあたりも理解して、前に進みたいものです。ちなみに毎回自動でMUI関連のビルドをするためにMSBuildフォーマットでスクリプトを書いたわけですが、毎回、手でコマンドを打って、国際化をはかる部分の処理を吐き出してもいいわけです。


 完全に出来上がってから、国際化を図ったリソースファイルを作ってちょちょいっとやるだけなので、大したコマンド入力の回数ではありません。MSBuildフォーマットの難しさは半端ないです。誰に迷惑をかけるわけでもありません。オープンソースにするときも手でコマンドを打ちますってヘルプファイルを付けておけば、このようなMSBuildフォーマットを書く手間は省けます。と、このような向上心の無い管理人の言葉を真に受けず、理解する努力をした方が素晴らしいに決まっています。


 ちなみに、大きな山場を越えましたが、MUIリソースファイルの作り方の手順は終わっていません。まだ続きます。やばいっしょこれ。疲れる。国際化のための手法のたったひとつの手段にすぎないのに理解するのにおそろしく時間がかかるし、説明するのにも時間がかかる。こんなもの理解してる人が世界でどれくらいいるんだろう。ググっても、こんなことやってる人に出会えなかったし、まったくヒットしません。MUIのやり方を説明しているわけではないですが、MUIを取り込んでいるオープンソースはありまして、FFFTPを受け継いでメンテしてる倉田佐祐理(KURATA Sayuri)氏だけですね。知らない人ですけど。この記事もほとんど、著作権無視してるくらいコードをパクパクして、パクって作ってるだけですし、すごいよ、この人。どうやって知識を得ているのか気になる木。絵師であり、コーダーでもあり、変態でもあるという勝手な印象です。


.vcxprojからmuiファイルをあやつる

 

 

VC Cpp記事に戻る。