質問者
多言語対応のWinformアプリケーションを単一のexeで作成したい

質問
-
多言語対応したWinformを作成しています。
ベースの言語は日本語で、他言語として英語(en-US)を利用します。
リソースファイルは、日本語と英語の2つを用意しています。
●プロジェクトのファイル構成
MyProject/
+ Resources.en-US.resx (英語リソースファイル)
+ Resources.resx (日本語リソースファイル)
ビルドすると、MyProject.exeの他に、en-USフォルダに入ったdllが作成されます。
●ビルド後のファイル
+ bin/Debug/
+ MyProject.exe
+ en-US/
+ MyProject.resources.dll
exeとdllを単純にILMergeでマージすると、英語表示ができません。
うまくexeとdllをマージして、シングルのexeにしたいのです。
WPFの例では、こちらに同様な例がありました。https://social.msdn.microsoft.com/Forums/ja-JP/a68caf09-4ec3-421c-86fc-1a7b4936b504/22810353283548623550245401237512383124501250312522124341123881?forum=wpfja
残念ながら、この手法がWinformにも応用できるのかどうか、できるとしてもそのやり方がわかりません。
どなたかアドバイスいただければ幸いです。
よろしくお願いいたします。
すべての返信
-
ResourceManagerがリソースの管理を行っているので、独自のResourceManagerで埋め込みリソースから読み込むようにしてやればいいです。
FormのLocalizableプロパティをtrueにしてLanguageを規定とen-USで作ってあって切り替える場合は、ComponentResourceManagerがリソースの適用を行っているので、同様に埋め込みリソースから読み込むようにしてやればいいです。
My ProjectフォルダにあるResources.Designer.vb
Namespace My.Resources Friend Module Resources Private resourceMan As Global.System.Resources.ResourceManager Private resourceCulture As Global.System.Globalization.CultureInfo <Global.System.ComponentModel.EditorBrowsableAttribute(Global.System.ComponentModel.EditorBrowsableState.Advanced)> Friend ReadOnly Property ResourceManager() As Global.System.Resources.ResourceManager Get If Object.ReferenceEquals(resourceMan, Nothing) Then 'ここで独自のResourceManagerを使うように書き換える Dim temp As Global.System.Resources.ResourceManager = New ResourceManagerEx("WindowsApp1.Resources", GetType(Resources).Assembly) resourceMan = temp End If Return resourceMan End Get End Property '以下省略
Class ResourceManagerEx Inherits System.Resources.ResourceManager Dim _baseName As String Dim _asm As System.Reflection.Assembly Public Sub New(baseName As String, assembly As System.Reflection.Assembly) MyBase.New(baseName, assembly) Me._baseName = baseName Me._asm = assembly End Sub Protected Overrides Function InternalGetResourceSet(culture As CultureInfo, createIfNotExists As Boolean, tryParents As Boolean) As ResourceSet '要求されるカルチャを元に、埋め込まれているリソースから探して返す Dim retval As ResourceSet = Nothing Dim temp As String = Me._baseName + "." For Each name As String In Me.MainAssembly.GetManifestResourceNames().Where(Function(x) x.StartsWith(temp) AndAlso x.EndsWith(".resources")).OrderBy(Function(x) x.Length) Dim nameafter = name.Substring(temp.Length) Dim parts = nameafter.Split(".") Dim key As System.Globalization.CultureInfo If parts.Length <> 2 Then Continue For End If key = System.Globalization.CultureInfo.GetCultureInfo(parts(0)) If key.Equals(culture) Then Dim q = Me.MainAssembly.GetManifestResourceInfo(name) Dim stream = Me.MainAssembly.GetManifestResourceStream(name) retval = New ResourceSet(stream) Exit For End If If tryParents Then key = key.Parent If key.Equals(culture) Then Dim q = Me.MainAssembly.GetManifestResourceInfo(name) Dim stream = Me.MainAssembly.GetManifestResourceStream(name) retval = New ResourceSet(stream) Exit For End If End If Next If retval Is Nothing Then retval = MyBase.InternalGetResourceSet(culture, createIfNotExists, tryParents) End If Return retval End Function End Class
Form.Desugber.vb
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _ Partial Class Form1 Inherits System.Windows.Forms.Form Private Sub InitializeComponent() 'ここで独自のComponentResourceManagerを使うように書き換える Dim resources As System.ComponentModel.ComponentResourceManager = New ComponentResourceManagerEx(GetType(Form1)) '省略
Imports System Imports System.Globalization Imports System.Resources Public Class Form1 Sub New() System.Diagnostics.Debugger.Launch() 'System.Globalization.CultureInfo.CurrentCulture = System.Globalization.CultureInfo.GetCultureInfo("en-US") 'System.Threading.Thread.CurrentThread.CurrentUICulture = System.Globalization.CultureInfo.GetCultureInfo("en-US") ' この呼び出しはデザイナーで必要です。 InitializeComponent() ' InitializeComponent() 呼び出しの後で初期化を追加します。 MessageBox.Show(My.Resources.TestKey) End Sub Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click Dim f = New Form2() f.ShowDialog() End Sub End Class Class ComponentResourceManagerEx Inherits System.ComponentModel.ComponentResourceManager Dim targetType As Type Sub New(targetFormType As Type) MyBase.New(targetFormType) If Not GetType(Form).IsAssignableFrom(targetFormType) Then Throw New ArgumentException(NameOf(targetFormType)) End If Me.targetType = targetFormType End Sub Protected Overrides Function InternalGetResourceSet(culture As CultureInfo, createIfNotExists As Boolean, tryParents As Boolean) As ResourceSet '要求されるカルチャを元に、埋め込まれているリソースから探して返す Dim retval As ResourceSet = Nothing Dim temp As String = targetType.FullName + "." For Each name As String In Me.MainAssembly.GetManifestResourceNames().Where(Function(x) x.StartsWith(temp) AndAlso x.EndsWith(".resources")).OrderBy(Function(x) x.Length) Dim nameafter = name.Substring(temp.Length) Dim parts = nameafter.Split(".") Dim key As System.Globalization.CultureInfo If parts.Length <> 2 Then Continue For End If key = System.Globalization.CultureInfo.GetCultureInfo(parts(0)) If key.Equals(culture) Then Dim q = Me.MainAssembly.GetManifestResourceInfo(name) Dim stream = Me.MainAssembly.GetManifestResourceStream(name) retval = New ResourceSet(stream) Exit For End If If tryParents Then key = key.Parent If key.Equals(culture) Then Dim q = Me.MainAssembly.GetManifestResourceInfo(name) Dim stream = Me.MainAssembly.GetManifestResourceStream(name) retval = New ResourceSet(stream) Exit For End If End If Next If retval Is Nothing Then retval = MyBase.InternalGetResourceSet(culture, createIfNotExists, tryParents) End If Return retval End Function End Class Class ResourceManagerEx Inherits System.Resources.ResourceManager Dim _baseName As String Dim _asm As System.Reflection.Assembly Public Sub New(baseName As String, assembly As System.Reflection.Assembly) MyBase.New(baseName, assembly) Me._baseName = baseName Me._asm = assembly End Sub Protected Overrides Function InternalGetResourceSet(culture As CultureInfo, createIfNotExists As Boolean, tryParents As Boolean) As ResourceSet '要求されるカルチャを元に、埋め込まれているリソースから探して返す Dim retval As ResourceSet = Nothing Dim temp As String = Me._baseName + "." For Each name As String In Me.MainAssembly.GetManifestResourceNames().Where(Function(x) x.StartsWith(temp) AndAlso x.EndsWith(".resources")).OrderBy(Function(x) x.Length) Dim nameafter = name.Substring(temp.Length) Dim parts = nameafter.Split(".") Dim key As System.Globalization.CultureInfo If parts.Length <> 2 Then Continue For End If key = System.Globalization.CultureInfo.GetCultureInfo(parts(0)) If key.Equals(culture) Then Dim q = Me.MainAssembly.GetManifestResourceInfo(name) Dim stream = Me.MainAssembly.GetManifestResourceStream(name) retval = New ResourceSet(stream) Exit For End If If tryParents Then key = key.Parent If key.Equals(culture) Then Dim q = Me.MainAssembly.GetManifestResourceInfo(name) Dim stream = Me.MainAssembly.GetManifestResourceStream(name) retval = New ResourceSet(stream) Exit For End If End If Next If retval Is Nothing Then retval = MyBase.InternalGetResourceSet(culture, createIfNotExists, tryParents) End If Return retval End Function End Class
おまけ:ビルド後のイベントのコマンドライン
set ilmerge="%ProgramFiles%\Microsoft\ILMerge\ILMerge.exe" set exe="$(TargetPath)" set exeTemp="$(TargetDir)_$(TargetFileName)" if not exist %ilmerge% ( set ilmerge="%ProgramFiles(x86)%\Microsoft\ILMerge\ILMerge.exe" ) cd $(TargetDir) setlocal enabledelayedexpansion for /d %%d in ("$(TargetDir)*.*") do call :work "%%d" exit /b :work set dll="%~1\$(TargetName).resources.dll" if not exist %dll% exit /b if not exist $(TargetPath) exit /b move %exe% %exeTemp% %ilmerge% /ndebug /v4 /out:%exe% "%exeTemp%" %dll% del %exeTemp% exit /b
個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)
- 編集済み gekkaMVP 2019年8月29日 10:39 おまけ追加
- 回答の候補に設定 Haruka6002Microsoft contingent staff, Moderator 2019年9月2日 8:34
-
まや358さん、こんにちは。フォーラムオペレーターのHarukaです。
MSDNフォーラムにご投稿くださいましてありがとうございます。
ご質問いただいた件ですが、その後いかがでしょうか。
gekkaさんから寄せられた投稿はお役に立ちましたか。
参考になった投稿には [回答としてマーク] をお願い致します。
設定いただくことで、
他のユーザーもお役に立つ回答を見つけやすくなります。
お手数ですが、ご協力の程どうかよろしくお願いいたします。
MSDN/ TechNet Community Support Haruka
ご協力くださいますようお願いいたします。また、MSDNサポートに賛辞や苦情がある場合は、MSDNFSF@microsoft.comまでお気軽にお問い合わせください。~