擷取 .NET 應用程式中的資源
當您在 .NET 應用程式中使用當地語系化資源時,最理想的做法是使用主要組件封裝預設或中性文化特性的資源,並針對應用程式支援的每個語言或文化特性,建立個別的附屬組件。 然後您可以使用下一節中所述的 ResourceManager 類別,來存取具名資源。 如果您選擇不將資源內嵌在主要組件和附屬組件,您也可以直接存取二進位 .resources 檔案,如本文後續的從 .resources 檔案擷取資源一節所述。
從組件擷取資源
ResourceManager 類別提供對執行階段資源的存取。 您可以使用 ResourceManager.GetString 方法來擷取字串資源,以及使用 ResourceManager.GetObject 或 ResourceManager.GetStream 方法來擷取非字串資源。 每個方法都有兩個多載:
具有單一參數的多載,此參數是包含資源名稱的字串。 此方法會嘗試擷取目前文化特性的資源。 如需詳細資訊,請參閱 GetString(String)、 GetObject(String)和 GetStream(String) 方法。
具有兩個參數的多載︰一個包含資源名稱的字串,以及一個代表所擷取資源之文化特性的 CultureInfo 物件。 如果找不到為該文化特性設定的資源,資源管理員會使用後援規則來擷取適當的資源。 如需詳細資訊,請參閱 GetString(String, CultureInfo)、 GetObject(String, CultureInfo)和 GetStream(String, CultureInfo) 方法。
資源管理員使用資源後援處理序,來控制應用程式如何擷取文化特性專屬資源。 如需詳細資訊,請參閱套件和部署資源中的<資源後援流程>一節。 如需具現化 ResourceManager 物件的資訊,請參閱 ResourceManager 類別主題中的<Instantiating a ResourceManager Object>(具現化 ResourceManager 物件) 一節。
擷取字串資料範例
下列範例會呼叫 GetString(String) 方法來擷取目前 UI 文化特性的字串資源。 其中包含英文 (美國) 文化特性的中性字串資源,以及法文 (法國) 和俄文 (俄羅斯) 文化特性的當地語系化資源。 下列英文 (美國) 資源是在名為 Strings.txt 的檔案中:
TimeHeader=The current time is
法文 (法國) 資源是在名為 Strings.fr-FR.txt 的檔案中:
TimeHeader=L'heure actuelle est
俄羅斯 (俄羅斯) 資源位於名為 Strings.ru-RU.txt 的檔案中:
TimeHeader=Текущее время —
此範例的原始程式碼 (C# 版程式碼的檔名為 GetString.cs,Visual Basic 版為 GetString.vb) 會定義一個字串陣列,其中包含四個文化特性的名稱︰可用資源的三個文化特性,以及西班牙文 (西班牙) 文化特性。 會出現一個迴圈隨機執行五次,然後選取其中一個文化特性並將其指派給 Thread.CurrentCulture 和 CultureInfo.CurrentUICulture 屬性。 然後它會呼叫 GetString(String) 方法來擷取當地語系化字串,並連同日期時間一起顯示。
using System;
using System.Globalization;
using System.Resources;
using System.Threading;
[assembly: NeutralResourcesLanguageAttribute("en-US")]
public class Example
{
public static void Main()
{
string[] cultureNames = { "en-US", "fr-FR", "ru-RU", "es-ES" };
Random rnd = new Random();
ResourceManager rm = new ResourceManager("Strings",
typeof(Example).Assembly);
for (int ctr = 0; ctr <= cultureNames.Length; ctr++) {
string cultureName = cultureNames[rnd.Next(0, cultureNames.Length)];
CultureInfo culture = CultureInfo.CreateSpecificCulture(cultureName);
Thread.CurrentThread.CurrentCulture = culture;
Thread.CurrentThread.CurrentUICulture = culture;
Console.WriteLine("Current culture: {0}", culture.NativeName);
string timeString = rm.GetString("TimeHeader");
Console.WriteLine("{0} {1:T}\n", timeString, DateTime.Now);
}
}
}
// The example displays output like the following:
// Current culture: English (United States)
// The current time is 9:34:18 AM
//
// Current culture: Español (España, alfabetización internacional)
// The current time is 9:34:18
//
// Current culture: русский (Россия)
// Текущее время — 9:34:18
//
// Current culture: français (France)
// L'heure actuelle est 09:34:18
//
// Current culture: русский (Россия)
// Текущее время — 9:34:18
Imports System.Globalization
Imports System.Resources
Imports System.Threading
<Assembly: NeutralResourcesLanguageAttribute("en-US")>
Module Example
Public Sub Main()
Dim cultureNames() As String = {"en-US", "fr-FR", "ru-RU", "es-ES"}
Dim rnd As New Random()
Dim rm As New ResourceManager("Strings", GetType(Example).Assembly)
For ctr As Integer = 0 To cultureNames.Length
Dim cultureName As String = cultureNames(rnd.Next(0, cultureNames.Length))
Dim culture As CultureInfo = CultureInfo.CreateSpecificCulture(cultureName)
Thread.CurrentThread.CurrentCulture = culture
Thread.CurrentThread.CurrentUICulture = culture
Console.WriteLine("Current culture: {0}", culture.NativeName)
Dim timeString As String = rm.GetString("TimeHeader")
Console.WriteLine("{0} {1:T}", timeString, Date.Now)
Console.WriteLine()
Next
End Sub
End Module
' The example displays output similar to the following:
' Current culture: English (United States)
' The current time is 9:34:18 AM
'
' Current culture: Español (España, alfabetización internacional)
' The current time is 9:34:18
'
' Current culture: русский (Россия)
' Текущее время — 9:34:18
'
' Current culture: français (France)
' L'heure actuelle est 09:34:18
'
' Current culture: русский (Россия)
' Текущее время — 9:34:18
下列批次 (.bat) 檔案會編譯範例,並在適當的目錄中產生附屬組件。 然後提供適用於 C# 語言和編譯器的命令。 若為 Visual Basic,請將 csc
變更為 vbc
,並將 GetString.cs
變更為 GetString.vb
。
resgen strings.txt
csc GetString.cs -resource:strings.resources
resgen strings.fr-FR.txt
md fr-FR
al -embed:strings.fr-FR.resources -culture:fr-FR -out:fr-FR\GetString.resources.dll
resgen strings.ru-RU.txt
md ru-RU
al -embed:strings.ru-RU.resources -culture:ru-RU -out:ru-RU\GetString.resources.dll
當目前 UI 文化特性為西班牙文 (西班牙) 時,請注意,此範例會顯示英文語言資源,因為西班牙文語言資源無法使用,而英文是此範例的預設文化特性。
擷取物件資料範例
您可以使用 GetObject 和 GetStream 方法來擷取物件資料。 包括基本資料類型、可序列化的物件,以及使用二進位格式儲存的物件 (例如影像)。
下列範例使用 GetStream(String) 方法來擷取應用程式開頭顯示畫面視窗中使用的點陣圖。 下列原始程式碼位於名為 CreateResources.cs (適用於 C#) 或 CreateResources.vb (適用於 Visual Basic) 的檔案中,會產生包含序列化影像的 .resx 檔。 在此情況下,會從名為 SplashScreen.jpg 的檔案載入影像;您可以修改檔案名稱以替代成您自己的影像。
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Resources;
public class Example
{
public static void Main()
{
Bitmap bmp = new Bitmap(@".\SplashScreen.jpg");
MemoryStream imageStream = new MemoryStream();
bmp.Save(imageStream, ImageFormat.Jpeg);
ResXResourceWriter writer = new ResXResourceWriter("AppResources.resx");
writer.AddResource("SplashScreen", imageStream);
writer.Generate();
writer.Close();
}
}
Imports System.Drawing
Imports System.Drawing.Imaging
Imports System.IO
Imports System.Resources
Module Example
Public Sub Main()
Dim bmp As New Bitmap(".\SplashScreen.jpg")
Dim imageStream As New MemoryStream()
bmp.Save(imageStream, ImageFormat.Jpeg)
Dim writer As New ResXResourceWriter("AppResources.resx")
writer.AddResource("SplashScreen", imageStream)
writer.Generate()
writer.Close()
End Sub
End Module
下列程式碼會擷取資源,並顯示 PictureBox 控制項中的影像。
using System;
using System.Drawing;
using System.IO;
using System.Resources;
using System.Windows.Forms;
public class Example
{
public static void Main()
{
ResourceManager rm = new ResourceManager("AppResources", typeof(Example).Assembly);
Bitmap screen = (Bitmap) Image.FromStream(rm.GetStream("SplashScreen"));
Form frm = new Form();
frm.Size = new Size(300, 300);
PictureBox pic = new PictureBox();
pic.Bounds = frm.RestoreBounds;
pic.BorderStyle = BorderStyle.Fixed3D;
pic.Image = screen;
pic.SizeMode = PictureBoxSizeMode.StretchImage;
frm.Controls.Add(pic);
pic.Anchor = AnchorStyles.Top | AnchorStyles.Bottom |
AnchorStyles.Left | AnchorStyles.Right;
frm.ShowDialog();
}
}
Imports System.Drawing
Imports System.IO
Imports System.Resources
Imports System.Windows.Forms
Module Example
Public Sub Main()
Dim rm As New ResourceManager("AppResources", GetType(Example).Assembly)
Dim screen As Bitmap = CType(Image.FromStream(rm.GetStream("SplashScreen")), Bitmap)
Dim frm As New Form()
frm.Size = new Size(300, 300)
Dim pic As New PictureBox()
pic.Bounds = frm.RestoreBounds
pic.BorderStyle = BorderStyle.Fixed3D
pic.Image = screen
pic.SizeMode = PictureBoxSizeMode.StretchImage
frm.Controls.Add(pic)
pic.Anchor = AnchorStyles.Top Or AnchorStyles.Bottom Or
AnchorStyles.Left Or AnchorStyles.Right
frm.ShowDialog()
End Sub
End Module
您可以使用下列批次檔來建立 C# 範例。 若是 Visual Basic,請將 csc
變更為 vbc
,並將原始程式碼檔案的副檔名從 .cs
變更為 .vb
。
csc CreateResources.cs
CreateResources
resgen AppResources.resx
csc GetStream.cs -resource:AppResources.resources
下列範例使用 ResourceManager.GetObject(String) 方法來還原序列化自訂物件。 此範例包含名為 UIElements.cs (若是 Visual Basic 則為 UIElements.vb) 的原始程式碼檔,其中包含名為 PersonTable
的下列結構。 此結構是為了供一般資料表顯示常式使用,以顯示資料表資料行的當地語系化名稱。 請注意,PersonTable
結構會以 SerializableAttribute 屬性標記。
using System;
[Serializable] public struct PersonTable
{
public readonly int nColumns;
public readonly string column1;
public readonly string column2;
public readonly string column3;
public readonly int width1;
public readonly int width2;
public readonly int width3;
public PersonTable(string column1, string column2, string column3,
int width1, int width2, int width3)
{
this.column1 = column1;
this.column2 = column2;
this.column3 = column3;
this.width1 = width1;
this.width2 = width2;
this.width3 = width3;
this.nColumns = typeof(PersonTable).GetFields().Length / 2;
}
}
<Serializable> Public Structure PersonTable
Public ReadOnly nColumns As Integer
Public Readonly column1 As String
Public ReadOnly column2 As String
Public ReadOnly column3 As String
Public ReadOnly width1 As Integer
Public ReadOnly width2 As Integer
Public ReadOnly width3 As Integer
Public Sub New(column1 As String, column2 As String, column3 As String,
width1 As Integer, width2 As Integer, width3 As Integer)
Me.column1 = column1
Me.column2 = column2
Me.column3 = column3
Me.width1 = width1
Me.width2 = width2
Me.width3 = width3
Me.nColumns = Me.GetType().GetFields().Count \ 2
End Sub
End Structure
下列程式碼來自於名為 CreateResources.cs (若是 Visual Basic 則為 CreateResources.vb) 的檔案,會建立用以儲存資料表標題的 XML 資源檔 UIResources.resx,以及包含針對英文語言當地語系化之應用程式資訊的 PersonTable
物件。
using System;
using System.Resources;
public class CreateResource
{
public static void Main()
{
PersonTable table = new PersonTable("Name", "Employee Number",
"Age", 30, 18, 5);
ResXResourceWriter rr = new ResXResourceWriter(@".\UIResources.resx");
rr.AddResource("TableName", "Employees of Acme Corporation");
rr.AddResource("Employees", table);
rr.Generate();
rr.Close();
}
}
Imports System.Resources
Module CreateResource
Public Sub Main()
Dim table As New PersonTable("Name", "Employee Number", "Age", 30, 18, 5)
Dim rr As New ResXResourceWriter(".\UIResources.resx")
rr.AddResource("TableName", "Employees of Acme Corporation")
rr.AddResource("Employees", table)
rr.Generate()
rr.Close()
End Sub
End Module
原始程式碼檔 GetObject.cs (GetObject.vb) 中的下列程式碼會接著擷取資源,並將其顯示到主控台。
using System;
using System.Resources;
[assembly: NeutralResourcesLanguageAttribute("en")]
public class Example
{
public static void Main()
{
string fmtString = String.Empty;
ResourceManager rm = new ResourceManager("UIResources", typeof(Example).Assembly);
string title = rm.GetString("TableName");
PersonTable tableInfo = (PersonTable) rm.GetObject("Employees");
if (! String.IsNullOrEmpty(title)) {
fmtString = "{0," + ((Console.WindowWidth + title.Length) / 2).ToString() + "}";
Console.WriteLine(fmtString, title);
Console.WriteLine();
}
for (int ctr = 1; ctr <= tableInfo.nColumns; ctr++) {
string columnName = "column" + ctr.ToString();
string widthName = "width" + ctr.ToString();
string value = tableInfo.GetType().GetField(columnName).GetValue(tableInfo).ToString();
int width = (int) tableInfo.GetType().GetField(widthName).GetValue(tableInfo);
fmtString = "{0,-" + width.ToString() + "}";
Console.Write(fmtString, value);
}
Console.WriteLine();
}
}
Imports System.Resources
<Assembly: NeutralResourcesLanguageAttribute("en")>
Module Example
Public Sub Main()
Dim fmtString As String = String.Empty
Dim rm As New ResourceManager("UIResources", GetType(Example).Assembly)
Dim title As String = rm.GetString("TableName")
Dim tableInfo As PersonTable = DirectCast(rm.GetObject("Employees"), PersonTable)
If Not String.IsNullOrEmpty(title) Then
fmtString = "{0," + ((Console.WindowWidth + title.Length) \ 2).ToString() + "}"
Console.WriteLine(fmtString, title)
Console.WriteLine()
End If
For ctr As Integer = 1 To tableInfo.nColumns
Dim columnName As String = "column" + ctr.ToString()
Dim widthName As String = "width" + ctr.ToString()
Dim value As String = CStr(tableInfo.GetType().GetField(columnName).GetValue(tableInfo))
Dim width As Integer = CInt(tableInfo.GetType().GetField(widthName).GetValue(tableInfo))
fmtString = "{0,-" + width.ToString() + "}"
Console.Write(fmtString, value)
Next
Console.WriteLine()
End Sub
End Module
您可以建立必要的資源檔和組件,並藉由執行下列批次檔來執行應用程式。 您必須使用 /r
選項將 UIElements.dll 的參考提供給 Resgen.exe,使其可以存取有關 PersonTable
結構的資訊。 如果使用 C#,請將 vbc
編譯器名稱取代成 csc
,並將 .vb
副檔名取代成 .cs
。
vbc -t:library UIElements.vb
vbc CreateResources.vb -r:UIElements.dll
CreateResources
resgen UIResources.resx -r:UIElements.dll
vbc GetObject.vb -r:UIElements.dll -resource:UIResources.resources
GetObject.exe
附屬組件的版本支援
根據預設,當 ResourceManager 物件擷取要求的資源時,它會尋找其版本號碼符合主要組件之版本號碼的附屬組件。 部署應用程式之後,您可能需要更新主要組件或特定資源附屬組件。 .NET Framework 提供主要組件和附屬組件的版本控制支援。
SatelliteContractVersionAttribute 屬性為主要組件提供了版本控制支援。 在應用程式的主要組件上指定這個屬性可讓您更新並重新部署主要組件,而不更新其附屬組件。 更新主要組件之後,主要組件的版本號碼會遞增,但附屬合約版本號碼則保持不變。 當資源管理員擷取要求的資源時,它會載入此屬性指定的附屬組件版本。
發行者原則組件提供附屬組件的版本控制支援。 您可以更新並重新部署附屬組件,而不更新主要組件。 更新附屬組件之後,其版本號碼會遞增,並隨附於發行者原則組件。 在發行者原則組件中,指定您的新附屬組件與其舊版相容。 資源管理員會使用 SatelliteContractVersionAttribute 屬性來判斷附屬組件的版本,但組件載入器會繫結至發行者原則所指定的附屬組件版本。 如需發行者原則組件的詳細資訊,請參閱建立發行者原則檔案。
若要啟用完整的組件版本控制支援,建議您在 全域組件快取 中部署強式名稱的組件,並在應用程式目錄中部署沒有強式名稱的組件。 如果您想要在應用程式目錄中部署強式名稱的組件,當您更新組件時,將無法遞增附屬組件的版本號碼。 相反地,您必須執行就地更新,以更新的程式碼取代現有程式碼,並維持相同的版本號碼。 例如,如果您想要更新 1.0.0.0 版的附屬組件,而且該組件已完整指定組件名稱 "myApp.resources, Version=1.0.0.0, Culture=de, PublicKeyToken=b03f5f11d50a3a",請覆寫成使用完整指定之相同組件名稱 "myApp.resources, Version=1.0.0.0, Culture=de, PublicKeyToken=b03f5f11d50a3a" 編譯的已更新 myApp.resources.dll。 請注意,在附屬組件檔上使用就地更新,會讓應用程式很難正確判斷附屬組件的版本。
如需組件版本設定的詳細資訊,請參閱組件版本設定和執行階段如何找出組件。
從 .resources 檔案擷取資源
如果您選擇不要部署附屬組件中的資源,您仍然可以使用 ResourceManager 物件直接從 .resources 檔存取資源。 若要這樣做,您必須正確部署 .resources 檔。 然後您可以使用 ResourceManager.CreateFileBasedResourceManager 方法具現化 ResourceManager 物件,並指定包含獨立 .resources 檔的目錄。
部署 .resources 檔案
當您將 .resources 檔嵌入應用程式組件和附屬組件時,每個附屬組件都有相同的檔案名稱,但會放在反映附屬組件之文化特性的子目錄中。 相反地,當您直接從 .resources 檔存取資源時,您可以將所有 .resources 檔放在單一目錄中,通常是應用程式目錄的子目錄。 應用程式之預設 .resources 檔的名稱只包含根目錄名稱,而不會有其文化特性指示 (例如 strings.resources)。 每個當地語系化文化特性的資源會儲存在其名稱包含根目錄名稱後面接著文化特性的檔案 (例如 strings.ja.resources 或 strings.de-DE.resources)。
下圖顯示資源檔在目錄結構中的位置。 它也會提供 .resource 檔案的命名慣例。
使用資源管理員
建立資源並放在適當目錄之後,您可以呼叫 ResourceManager 方法,建立 CreateFileBasedResourceManager(String, String, Type) 物件來使用資源。 第一個參數指定應用程式預設 .resources 檔的根目錄名稱 (這會是上一節範例中的 "strings")。 第二個參數指定資源的位置 (上一個範例中的 "Resources")。 第三個參數指定要使用的 ResourceSet 實作。 如果第三個參數是 null
,則會使用預設執行階段 ResourceSet 。
注意
請勿使用獨立 .resources 檔部署 ASP.NET 應用程式。 這會造成鎖定問題並中斷 XCOPY 部署。 建議您部署附屬組件中的 ASP.NET 資源。 如需詳細資訊,請參閱 ASP.NET 網頁資源概觀。
具現化 ResourceManager 物件之後,您可以使用稍早所述的 GetString、 GetObject和 GetStream 方法來擷取資源。 不過,直接從 .resources 檔擷取資源,與從組件擷取內嵌資源不同。 當您從 .resources 檔擷取資源時, GetString(String)、 GetObject(String)和 GetStream(String) 方法一律會擷取預設文化特性的資源,而不論目前的文化特性為何。 若要擷取應用程式目前文化特性或特定文化特性的資源,您必須呼叫 GetString(String, CultureInfo)、GetObject(String, CultureInfo) 或 GetStream(String, CultureInfo) 方法,並指定所要擷取之資源的文化特性。 若要擷取目前文化特性的資源,請將 CultureInfo.CurrentCulture 屬性的值指定為 culture
引數。 如果資源管理員無法擷取 culture
的資源,則會使用標準資源後援規則來擷取適當的資源。
範例
下列範例說明資源管理員如何直接從 .resources 檔擷取資源。 此範例是由英文 (美國)、法文 (法國) 和俄文 (俄羅斯) 文化特性的三個文字資源檔所組成。 英文 (美國) 是此範例的預設文化特性。 其資源會儲存在以下名為 Strings.txt 的檔案中:
Greeting=Hello
Prompt=What is your name?
法文 (法國) 文化特性的資源會儲存在以下名為 Strings.fr-FR.txt 的檔案中:
Greeting=Bon jour
Prompt=Comment vous appelez-vous?
俄文 (俄羅斯) 文化特性的資源會儲存在以下名為 Strings.ru-RU.txt 的檔案中:
Greeting=Здравствуйте
Prompt=Как вас зовут?
以下是範例的原始程式碼。 此範例會具現化英文 (美國)、英文 (加拿大)、法文 (法國) 和俄文 (俄羅斯) 文化特性的 CultureInfo 物件,並設定每個文化特性作為目前的文化特性。 ResourceManager.GetString(String, CultureInfo) 方法會接著提供 CultureInfo.CurrentCulture 屬性的值作為 culture
引數,來擷取適當的文化特性專屬資源。
using System;
using System.Globalization;
using System.Resources;
using System.Threading;
[assembly: NeutralResourcesLanguage("en-US")]
public class Example
{
public static void Main()
{
string[] cultureNames = { "en-US", "en-CA", "ru-RU", "fr-FR" };
ResourceManager rm = ResourceManager.CreateFileBasedResourceManager("Strings", "Resources", null);
foreach (var cultureName in cultureNames) {
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(cultureName);
string greeting = rm.GetString("Greeting", CultureInfo.CurrentCulture);
Console.WriteLine("\n{0}!", greeting);
Console.Write(rm.GetString("Prompt", CultureInfo.CurrentCulture));
string name = Console.ReadLine();
if (! String.IsNullOrEmpty(name))
Console.WriteLine("{0}, {1}!", greeting, name);
}
Console.WriteLine();
}
}
// The example displays output like the following:
// Hello!
// What is your name? Dakota
// Hello, Dakota!
//
// Hello!
// What is your name? Koani
// Hello, Koani!
//
// Здравствуйте!
// Как вас зовут?Samuel
// Здравствуйте, Samuel!
//
// Bon jour!
// Comment vous appelez-vous?Yiska
// Bon jour, Yiska!
Imports System.Globalization
Imports System.Resources
Imports System.Threading
<Assembly: NeutralResourcesLanguageAttribute("en-US")>
Module Example
Public Sub Main()
Dim cultureNames() As String = {"en-US", "en-CA", "ru-RU", "fr-FR"}
Dim rm As ResourceManager = ResourceManager.CreateFileBasedResourceManager("Strings", "Resources", Nothing)
For Each cultureName In cultureNames
Console.WriteLine()
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(cultureName)
Dim greeting As String = rm.GetString("Greeting", CultureInfo.CurrentCulture)
Console.WriteLine("{0}!", greeting)
Console.Write(rm.GetString("Prompt", CultureInfo.CurrentCulture))
Dim name As String = Console.ReadLine()
If Not String.IsNullOrEmpty(name) Then
Console.WriteLine("{0}, {1}!", greeting, name)
End If
Next
Console.WriteLine()
End Sub
End Module
' The example displays output like the following:
' Hello!
' What is your name? Dakota
' Hello, Dakota!
'
' Hello!
' What is your name? Koani
' Hello, Koani!
'
' Здравствуйте!
' Как вас зовут?Samuel
' Здравствуйте, Samuel!
'
' Bon jour!
' Comment vous appelez-vous?Yiska
' Bon jour, Yiska!
您可以執行下列批次檔來編譯 C# 版的範例。 如果使用 Visual Basic,請將 csc
取代成 vbc
,並將 .cs
副檔名取代成 .vb
。
md Resources
resgen Strings.txt Resources\Strings.resources
resgen Strings.fr-FR.txt Resources\Strings.fr-FR.resources
resgen Strings.ru-RU.txt Resources\Strings.ru-RU.resources
csc Example.cs