tmytのらくがき

個人の日記レベルです

VSCTをローカライズする

Visual Studio拡張でボタンとか追加する時はVSCTというXMLで配置先とか、ラベルとかを定義して、それがコンパイラでバイナリになって、リソースに押し込まれます。 で、それをローカライズするにはどうすればいいか、というとdocs.com のここに書いています。

docs.microsoft.com

この通りにいくらやってもうまくいかなくて、うまくいくcsprojができたので書き残しておきます。

AssemblyInfo.cs を編集

csprojを編集する前にまずAssemblyInfo.csにひとつ追加。これでフォールバックロケールの検索先が変わるそうです。

// これを追加する。この場合は en-us がデフォルト言語。それ以外をデフォルトにしたいときは、そのロケール文字列を指定するとOK
[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]

これを書いておかないと、デフォルトのフォールバックリソースにVSCTのコンパイル結果が含まれていないので、フォールバックリソースが使われたVisual StudioでVSCTが読み込まれません。

VSCTをローカライズする

VSCTのXMLローカライズ済み文字列定義を参照するみたいな賢いことはできないので、全ロケール分コピーします。

例えば、Menus.vsctというファイルがある状態で、en-USとja-JPに対応したい場合はMenus.en-US.vsct、Menus.ja-JP.vsct をコピーして作ります。それぞれ中身はよさげにローカライズします。

f:id:tmyt:20200908063511p:plain

コピー元のMenus.vsctは消しても消さなくても大丈夫。もし、カスタムツールに"VsctGenerator"を指定している場合は残してもいいし、使ってない場合は削除でOK。 カスタムツールを使っている場合でも、ビルドアクションは"なし"にしてOK。

Resxをローカライズする

Resxもローカライズします。しないとうまく動きません。Resxのローカライズはいつもと同じ感じで、Strings.resxをStrings.en-US.resxとStrings.ja-JP.resxにコピーしてそれぞれローカライズみたいな感じでOK*1

ただし、フォールバック言語(ここではen-US)も個別言語としてつくらないとだめ。ここでフォールバック言語用のresxは消してはいけない。消すと動かない。

f:id:tmyt:20200908063645p:plain

この時、Resxがプロジェクトにひとつもない場合は空で大丈夫なので追加しておく。追加しておかないとうまく動かない

csprojを調整する

ここでcsprojをアンロードして右クリック→編集する。そして、こんな感じにする。

<!-- vsct -->
<ItemGroup>
  <!-- PackageGuidsとかを自動生成するのであればこれが必要 -->
  <None Include="ExpandAllPackage.vsct">
    <Generator>VsctGenerator</Generator>
    <LastGenOutput>ExpandAllPackage1.cs</LastGenOutput>
  </None>
  <!-- 各言語版をコピーして作る -->
  <VSCTCompile Include="ExpandAllPackage.ja-JP.vsct">
    <!-- ResourceNameはすべての言語でMenus.ctmenuにする -->
    <ResourceName>Menus.ctmenu</ResourceName>
  </VSCTCompile>
  <!-- ほかの言語と同じ。対応する言語分編集する。 -->
  <VSCTCompile Include="ExpandAllPackage.en-US.vsct">
    <ResourceName>Menus.ctmenu</ResourceName>
  </VSCTCompile>
</ItemGroup>
<ItemGroup>
  <Content Include="Resources\ExpandAll.png" />
</ItemGroup>
<!-- resx -->
<ItemGroup>
  <!-- 必須。ないとvsctがロードされない。リソースをコードから参照するときもこのリソースからバインディングが生成されるので消してはだめ -->
  <EmbeddedResource Include="Resources\Strings.resx">
    <Generator>ResXFileCodeGenerator</Generator>
    <LastGenOutput>Strings.Designer.cs</LastGenOutput>
    <!-- なんか適当に設定する。resxをresourcesに書き換えたものでOK。 -->
    <LogicalName>Strings.resources</LogicalName>
    <!-- ここには MergeWithCTOを書かない。書くとビルドできない。 -->
  </EmbeddedResource>
  <!-- 普通にローカライズするときと同じ。全言語作る。 -->
  <EmbeddedResource Include="Resources\Strings.en-US.resx">
    <!-- これをつけるとVSCTのコンパイル結果がこのresxとマージされる。ローカライズ済みリソースですべて必須。 -->
    <MergeWithCTO>true</MergeWithCTO>
    <!-- おなじ。resxをresourcesに書き換えたものでOK -->
    <LogicalName>Strings.en-US.resources</LogicalName>
  </EmbeddedResource>
  <!-- ほかの言語と同じ。対応する言語分編集する。 -->
  <EmbeddedResource Include="Resources\Strings.ja-JP.resx">
    <MergeWithCTO>true</MergeWithCTO>
    <LogicalName>Strings.ja-JP.resources</LogicalName>
  </EmbeddedResource>
</ItemGroup>

MergeWithCTOとLogicalNameは、resxの組のいずれかでOK。*2

編集したら、保存してプロジェクトを再読み込みする。あとはビルドすればおそらく複数言語に対応したVisual Studio拡張が出来上がっているはず。

ちなみに、VSCTが不要な場合はResxだけなのでこんな面倒なことをしなくてもローカライズできます*3

Visual Studioの言語を切り替えると、正しくローカライズされているように見える。

f:id:tmyt:20200908064008p:plain

*1:docs.comには、Strings.resxをStrings.en-US.resxにリネームするって書いてるけどここがたぶん間違い。

*2:たとえば、Strings.resxとBitmaps.resxがある場合は、Strings.resx(ja-JP, en-US)か、Bitmaps.resx(ja-JP, en-US)のいずれかでOK。ここではResxが1グループしかないので、こういうcsprojになっている。

*3:AssemblyInfo.csの編集もいらないしcsprojの編集もいらないし、ロケールのついてないresxがフォールバック先として読み込みもできる。