블로그 이미지
생각처럼

카테고리

전체보기 (209)
TOOL (1)
다이어리 (1)
Bit (200)
android (8)
C&C++ (3)
C# (26)
VB.Net (4)
MFC (0)
Win Ce (5)
아키텍쳐 (4)
WPF (9)
Rom (0)
읽어보기 (25)
Linux (24)
Java (0)
HELP? (0)
Total
Today
Yesterday

달력

« » 2025.1
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31

공지사항

태그목록

최근에 올라온 글

C# windows form 의 경우 파라미터를 메인함수에서 받지 않고
Main Load 이벤트에서 

Environment.CommandLine() 함수를 사용하여 
처리가능하다 
커맨드라인의 첫번째는 프로그램 경로 두번째는 실행하는 파일의 경로 

이상!
Posted by 생각처럼
, |

c# 다국어 지원

Bit/C# / 2012. 2. 3. 16:55

[지디넷코리아]시간은 화살 같다는 말이 부쩍 실감나는 요즘입니다. 다들 연초에 세웠던 계획은 잘 지키고 있는지 모르겠네요. 다시 한번 숨을 돌리고 뒤를 돌아보며 앞으로 나갈 힘을 비축해야 할 때입니다.

이번에는 그동안 비주얼 스튜디오 6.0의 이런저런 테크닉을 이용해 어렵게 처리해야 했던 다국어 프로그래밍의 문제가 닷넷에서는 얼마나 쉽고 편하게 바뀌었는지 알아보도록 하겠습니다.

닷넷이 제공해 주는 여러 가지 이점
닷넷 프레임워크는 리소스와 관련된 여러 클래스와 도구들을 가지고 있습니다. 본격적으로 예제를 통해 다국어 프로그래밍을 연습해 보기 전에 우선 기본적인 지식을 쌓아보기로 할까요? 그 첫 번째로 다음에 소개하는 내용은 리소스 관련 클래스들입니다.

◆ 리소스 관련 클래스들
1. ResourceManager 클래스 : ResourceManager는 실행시 적절한 리소스에 접근할 수 있도록 해주는 클래스입니다. 지역화된 리소스가 존재하지 않을 경우에 대비해 언어 중립적인 리소스에 접근할 수 있도록 해주는 역할을 하며, CreateFileBasedResourceManager 라는 메쏘드를 이용하면 어셈블리 내부에 포함되어 있지 않은 리소스에 대한 접근도 가능하게 해줍니다. 
2. ResourceWriter 클래스 : ResourceWriter 클래스는 시스템의 기본 형식에 맞추어 리소스를 파일이나 스트림의 형태로 작성할 수 있게 해줍니다. AddResource라는 메쏘드를 이용해 이름-값의 짝의 형태로 관리할 수 있습니다.
3. ResourceReader 클래스 : ResourceReader 리소스 파일, 스트림 등을 읽어들일 수 있습니다. 
4. ResourceSet 클래스 : ResourceSet 클래스는 특정 문화에 맞춰 지역화된 모든 종류의 리소스를 저장할 수 있습니다. ResourceManager와는 달리 지역화된 리소스가 없을 경우의 대비책은 제공해 주지 않습니다. 
5. CultureInfo 클래스 : CultureInfo 클래스는 사용자의 주언어, 보조언어, 국가/지역, 문화 특성에 따른 사용자의 선호도에 따른 정보를 포함하고 있습니다. 이 클래스를 이용해 날짜 형식이나 시간 형식, 숫자 형식, 문자열의 정렬 등을 정의할 수 있습니다.
6. RegionInfo 클래스 : RegionInfo 클래스는 측정의 단위를 결정하고 지역 코드를 이름으로 변환하는 기준을 제공합니다.

또한 리소스 관련 도구들에는 어떤 것들이 있을까요?

◆ 리소스 관련 도구들
1. Resgen.exe(리소스 파일 생성 유틸리티) : 텍스트 파일과 .resx 파일을  .resources 파일로 변환시켜주는 유틸리티입니다. 또한 .resources 파일을 다시 .resx 파일로 변화시킬 수도 있습니다. 이 유틸리티에 사용되는 텍스트 파일은 ASCII, UTF-8, UTF-16 등의 형식이면 됩니다.
2. Al.exe(어셈블리 링커) : .resources 파일을 dll 파일로 변화시킬 수 있습니다.

이렇게만 말씀드리니 대체 무슨 말을 하는지 모르겠다구요? 앞에서 간략히 설명한 클래스들과 도구들은 실제 예제를 통해 그 사용법을 배워보기로 하겠습니다.

첫 번째 예제 - 다국어 폼 만들기
비주얼 스튜디오 닷넷을 시작해 LocalizationDemo1이라는 이름의 솔루션을 윈도우 응용 프로그램 형태로 만듭시다. 이후 Form1.cs를 <화면 1>과 같이 구성해 프로젝트에 새로운 폼 Form2.cs를 추가해 봅시다.

 
<화면 1> Form1의 구성   <화면 2> Form2 추가하기

‘닫기’ 버튼의 이름을 btnCloseForm으로 정하고, 클릭하면 폼이 닫히도록 다음의 코드를 추가해 둡시다.

this.Hide();

지역화를 지원할 수 있도록 Form2.cs의 속성 창에서 Localizable 속성을 True로 바꾸고, Language 특성을 기본 값에서 영어로 변경해 봅시다.

 
<화면 3> 영어를 지원할 수 있도록 속성창 변경하기   <화면 4> Language를 영어로 설정했을 경우의 Form2 구성

Lanuage를 영어로 바꾸는 순간, 리소스가 추가된다는 메시지 박스가 나타나서 이제 두 언어의 디자인 환경이 각각 다르게 관리된다는 사실을 알려주게 됩니다. 이제 다시 Form1의 디자인 환경으로 돌아가 봅시다. 프로그램 시작 버튼을 더블 클릭해서 다음의 코드를 추가합니다.

private void button1_Click(object sender, System.EventArgs e)
{
  if( this.radioButton1.Checked )
  {
//Get the Default installed culture
    System.Threading.Thread.CurrentThread.CurrentUICulture = 
         System.Globalization.CultureInfo.InstalledUICulture ; 
  }
  else if( this.radioButton2.Checked )
  {
//Get the English culture
    System.Threading.Thread.CurrentThread.CurrentUICulture = 
      new System.Globalization.CultureInfo("en") ;
  }
  Form2 f2 = new Form2();
  f2.Show();

}

첫 번째 라디오 버튼이 선택될 경우 실행되고 있는 환경의 기본 로케일을 따라 리소스를 선택해 폼을 로드하는 것이고, 두 번째 라디오 버튼이 선택되면 영어 환경에 맞춰진 리소스를 선택해 폼를 로드하는 코드입니다. 앞의 코드에서는 앞서 말씀드린 CultureInfo 클래스가 사용되었으며, 현재 실행 중인 쓰레드의 UI를 결정하기 위해 System.Threading.Thread.CurrentThread.CurrentUICulture라는 값을 이용하고 있습니다.

여기까지만 살펴보아도 닷넷이 기본적으로 제공해주는 지역화 도구가 상당히 편리해졌음을 알 수 있습니다. 예전 같으면 기본적인 리소스 편집기로는 사실 다국어 프로그래밍에 필요한 리소스를 편집하는 것이 불가능했습니다. 두 가지 언어의 리소스를 같이 입력하는 것 자체가 불가능했기 때문입니다. 하지만 닷넷은 폼의 Language 프로퍼티를 바꾸는 것만으로 간단하게 각각의 Culture에 대한 리소스를 디자인 환경에서 편집할 수 있는 강력함을 보여주고 있습니다. 또한 소스코드 자체도 유니코드로 관리되는 편리함도 제공합니다.

두 번째 예제 - 문자열 지역화하기
이제 두 번째 예제를 만들어 봅시다. 프로젝트의 이름을 LocalizationDemo2라고 하고, Form1을 <화면 5>과 같이 구성해 봅시다.
<화면 5> 예제 2의 Form1의 구성

디자인 환경에서 컨트롤을 추가할 때 자동으로 생성된 코드가 <리스트 1>처럼 나타납니다. 이중에서 윈도우 폼 디자이너에서 생성한 코드 부분을 열어 코드를 구경해 보면 여러분이 디자인 환경에서 한 작업이 어떻게 코드로 표시되었는지 확인할 수 있을 것입니다.

 <리스트 1> 자동으로 생성된 Form1.cs 소스의 일부

이제 프로젝트에 새 항목을 추가해 보겠습니다. 솔루션 탐색기의 프로젝트 명에서 오른쪽 마우스 버튼을 클릭해 추가 메뉴에서 새 항목 추가를 클릭해 봅시다. 이 중 ‘어셈블리 리소스 파일’을 선택해 새로 생성합니다(resource1.resx). 그리고 <화면 6>, <화면 7>처럼 resource1.resx에 값을 추가해 봅시다.

<화면 6> resource1.resx가 추가된 모습

<화면 7> resource1.resx에 값이 추가된 모습

프로젝트에 resource1.en-US.resx라는 이름의 어셈블리 리소스 파일을 추가해 <화면 8>과 같이 편집합니다.

<화면 8> resource1.en-US.resx 파일

이제 Form1.cs의 소스를 편집해 봅시다. 우선 다음의 네임스페이스들을 추가합니다.

using System.Resources;
using System.Globalization;
using System.Threading;

또한 컨트롤들이 선언된 아래에 다음의 코드를 추가합니다. ResourceManger 클래스를 이용해 닷넷에서 제공하는 기본적인 리소스 형식인 .resx를 로드할 수 있게 해주는 코드입니다.

private ResourceManager     m_ResourceManager = new ResourceManager("LocalizationDemo2.resource1", 
                System.Reflection.Assembly.GetExecutingAssembly());
private CultureInfo         m_EnglishCulture = new CultureInfo("en-US");
private CultureInfo         m_KoreanCulture = new CultureInfo("ko-KR"); 

다음 함수를 코드에 추가해 주고, Form1의 생성자에서 이를 호출합니다. UpdateUI 함수를 호출하면 resx 파일로부터 리소스를 읽어들여 각 컨트롤의 리소스를 변경하게 됩니다.

public Form1()
{
   //
   // 윈도우 폼 디자이너 지원에 필요합니다.
   //
   InitializeComponent();

   UpdateUI();

   //
   // TODO: InitializeComponent를 호출한 다음 생성자 코드를 추가합니다.
   //
}
        :
        :
private void UpdateUI()
{
   lblName.Text = m_ResourceManager.GetString("lblName");
   btnHello.Text = m_ResourceManager.GetString("btnHello");
   optKorean.Text = m_ResourceManager.GetString("optKorean");
   optEnglish.Text = m_ResourceManager.GetString("optEnglish");
}

마지막으로 다음의 이벤트 핸들러들을 코드에 추가해 라디오 버튼을 클릭했을 때 해당 언어의 인사말과 메뉴가 나오도록 수정합니다.

private void optKorean_CheckedChanged(object sender, System.EventArgs e)
{
   Thread.CurrentThread.CurrentUICulture = m_KoreanCulture;
   UpdateUI();
}

private void optEnglish_CheckedChanged(object sender, System.EventArgs e)
{
   Thread.CurrentThread.CurrentUICulture = m_EnglishCulture;
   UpdateUI();
}

private void btnHello_Click(object sender, System.EventArgs e)
{
   string message = String.Format(m_ResourceManager.GetString("greetingMessage"), txtName.Text);
   MessageBox.Show(this, message, this.Text, MessageBoxButtons.OK, MessageBoxIcon.Information);
}

완성된 프로그램을 실행하면 <화면 9>, <화면 10>처럼 한국어 옵션을 선택했을 때는 한국어 인사말이, 영어 옵션을 선택했을 때는 영어 인사말이 나옴을 확인할 수 있습니다.

 
<화면 9> 한국어 실행화면   <화면 10> 영어 실행화면

닷넷이 가진 다국어 프로그래밍의 힘
지금까지 살펴본 방법은 윈도우 환경 내에서 리소스를 편집하고 이를 불러오는 방법입니다. 이외에도 resgen.exe를 이용해  기존의 .resx 파일을 .resource 파일로 변경해 새로운 리소스 파일을 만들어낸 후 이를 Al.exe를 이용해 애플리케이션과 별개의 리소스 dll로 리소스를 관리하기도 합니다. 또한 텍스트 파일에 리소스를 저장하고 이를 불러오는 방법에 대비한 클래스들도 준비되어 있습니다.

앞서 말씀드린 것처럼 닷넷은 기존의 비주얼 스튜디오 6.0이 불완전하게 지원하던 다국어 프로그래밍을 설계 단계에서부터 완벽하게 지원할 수 있도록 되어 있습니다. 누군가 제게 다국어 프로그램을 하나 짜야한다고 말한다면 저는 주저없이 닷넷을 개발도구로 택할 것 같습니다. 그 편리함을 한 번 맛보고 나면 다시는 예전으로 돌아갈 수 없어서 말이죠.

지금까지 다섯번에 걸쳐 비주얼 베이직 6.0, 비주얼 C++ 6.0, 비주얼 스튜디오 닷넷을 이용해 다국어 프로그래밍을 하는 방법에 대해 간략하게 살펴보았습니다. 사실 연재에서 다뤄진 부분보다 훨씬 더 많은 것을 고려해야만 제대로 된 다국어 프로그램을 하나 만들어낼 수 있겠지만, 이번 글로 독자 여러분들께서 다국어 프로그래밍에 대한 기본적인 이해와 지식을 가져갔으면 하는 것이 필자의 작은 소망입니다. 그동안 읽어주셔서 감사합니다. 독자 여러분들 매일매일 행복한 날 되시고, 여러분의 프로그래밍 실력이 부쩍부쩍 늘어나길 기원해 봅니다. @
Posted by 생각처럼
, |

Resources and Localization

Bit/C# / 2012. 2. 3. 16:54
Resources and Localization
Ted Pattison

Code download available at: BasicInstincts05.exe (146 KB) 
Browse the Code Online
There are two ways you can utilize resources such as strings, images, and text-based files from your Microsoft® .NET Framework-based application. You can embed them directly in the app or you can load them from an external file. When you choose to load from an external source rather than from an embedding resource, you must distribute the files along with the assembly. You must also make sure that code inside the application can determine the correct path and load the resource files at run time. This approach causes problems if the .exe ever becomes separated from the files it depends upon.
Taking the embedded option and compiling the resources you need directly into the assemblies that use them makes distribution more reliable and less error prone. This month I will discuss the use of resources, how and why you might embed a resource, and the role of resource files in .NET.

Embedding a Resource
Let's start with a simple example to see how embedding is accomplished. Assume you would like to embed a graphic image named LitwareLogo.png into your Windows® Forms-based application. You begin by adding the file to the Visual Studio® project. Then, from within the property sheet for the file, you set the Build Action to Embedded Resource, as shown in Figure 1. By doing this, you have instructed Visual Studio to embed the file into the physical image of the output assembly .exe file.
Figure 1 Set Build Action 
Once you have embedded a file as a resource, you must learn how to access it at run time. Examine the following code fragment, which obtains a reference to the current assembly object and then calls the GetManifestResourceStream method to acquire stream-based access to the embedded resource file. This code assumes that you have imported the System.Reflection namespace and the System.IO namespace:
'*** get current Assembly object.
Dim asm As Assembly = Assembly.GetExecutingAssembly()
'*** load embedded resource into stream
Dim ResourceName As String = "LitwareSmartClient.LitwareLogo.png"
Dim str As Stream = asm.GetManifestResourceStream(ResourceName)
    '*** convert stream into image and load in    '*** picture box
    Dim img As Image = Image.FromStream(str)
    PictureBox1.Image = img
As you can see, an Assembly object exposes the GetManifestResourceStream method, which allows you to pass a string name that identifies the embedded resource. Note that the resource name is case-sensitive even when you are using a case-insensitive language like Visual Basic®. In the example, the code calls the Image.FromStream method to convert the stream containing the image file into an Image object that can be loaded into a PictureBox control.
In addition to embedding image files, it can be convenient to embed text-based files containing XML, SQL, or JavaScript. This can make your life a lot easier if you find it tedious to concatenate large string fragments of XML, SQL, or JavaScript using Visual Basic.
For example, let's say you have large XML documents, SQL statements, or JavaScript functions your application needs. You can maintain these as standalone .xml files, .sql files, and .js files within a Visual Studio project. With this you get the benefit of Visual Studio color coding and statement completion. You can also take advantage of Visual Studio schema-driven IntelliSense® for XML files. All that's required is that you embed these source files into the output assembly and access them using the technique you have already seen. For example, if you have embedded a SQL file and an XML file in a Windows Forms-based application, you can access them using code like that in Figure 2.
'*** get current Assembly object.
Dim asm As Assembly = Assembly.GetExecutingAssembly()

'*** load embedded SQL resources file
Dim SqlResourceName As String = "LitwareSmartClient.GetProducts.sql"
Dim strSQL As Stream = asm.GetManifestResourceStream(SqlResourceName)
Dim reader As New StreamReader(strSQL)
Dim sql As String = reader.ReadToEnd
reader.Close()

'*** load embedded XML resources file
Dim XmlResourceName As String = "LitwareSmartClient.Customers.xml"
Dim strXML As Stream = asm.GetManifestResourceStream(XmlResourceName)
Dim xmlDoc As New XmlDocument()
xmlDoc.Load(strXML)
strXML.Close()

Resource Files
The techniques you have just seen involve embedding resource files directly into an assembly and loading them using the GetManifestResourceStream method supplied by the Assembly class. But there is another alternative in .NET, resource files, which can make it even easier to handle resources in many scenarios. Plus, as you will see, Visual Studio 2005 provides some conveniences when it comes to working with resources and localizing apps.

Working with Resource Files
In .NET, resource files can be used to embed resources into assemblies. One of the key benefits of using resource files is that all the language and locale-specific elements in an application or class library DLL such as captions and user messages can be factored out of your application code. To do this you need to create a separate resource file for each spoken language that you need to support. The actual resource file is a text-based file containing XML with a .resx extension. Figure 3 shows a skimmed-down example of the XML data found inside a resource file.
<root>

 <data name="MainFormCaption">
   <value>Litware Customer Manager</value>
 </data>

 <data name="UserWelcome">
   <value>Good day</value>
 </data>

 <data name="ErrorMessage1">
   <value>Oh no, Something went wrong!</value>
 </data>

</root>
While the XML fragment in the figure isn't a complete resource file, it gives you a general idea of what's inside one. You can compile a resource file into a binary image using a .NET-based utility called the resource file generator (Resgen.exe). Compiled resource files typically have a .resources extension. For example, a developer at the company named Litware can create a resource file named LitwareStrings.resx and compile it into a binary image named LitwareStrings.resources by executing the following command within a batch file or from the Visual Studio 2005 Command Prompt:
RESGEN.EXE LitwareStrings.resx LitwareStrings.resources
After you compile a text-based .resx file into a binary .resource file, it is still not yet ready to use. Instead, you must further compile this binary image into a .NET assembly before you can use it from an application. This can be accomplished using another .NET tool called the Assembly Linker (Al.exe). For example, to compile LitwareStrings.resources into its own assembly DLL, you can run the following command-line instruction from a batch file or from the Visual Studio 2005 command prompt:
AL.EXE /t:library 
 /out:LitwareStrings.resources.dll 
 /link:LitwareStrings.resources
 
Once you have compiled a resource file into a .NET assembly, you can access the resources inside using the ResourceManager class that is defined in the System.Resources namespace. The following shows a simple example of code that accesses a string resource using the ResourceManager:
Dim asm As Assembly = Assembly.Load("LitwareStrings.resources")
Dim rm As New System.Resources.ResourceManager("LitwareStrings", asm)
Dim caption As String = rm.GetString("MainFormCaption")
Resgen.exe can also be used to generate a strongly typed resource class that exposes properties providing easy access to the resource inside. For example, to a generate a strongly typed resource class in Visual Basic, you can add the /str parameter and a value of "vb" to the command line when calling Resgen.exe:
RESGEN.EXE LitwareStrings.resx LitwareStrings.resources /str:vb 
This command-line instruction generates a Visual Basic source file named LitwareStrings.vb. This source file contains a class named LitwareStrings. Inside the class, there is code that uses the ResourceManager to implement strongly typed properties that look like this:
Shared ReadOnly Property MainFormCaption() As String
  Get
    Return ResourceManager.GetString("MainFormCaption", resourceCulture)
  End Get
End Property 
I have just quickly stepped through a high-level explanation of how resource files are compiled into assemblies and how they can be accessed using the ResourceManager class and strongly typed resource classes.This should give you a better idea of how the individual pieces fit together.
I don't want to spend any more time here on the low-level details of resource files because you will not be required to deal with them when you begin localizing applications and class library DLLs. That's because Visual Studio 2005 and Visual Basic supply many valuable conveniences behind the scenes. However, keep in mind that you might have to work directly with Resgen.exe, Al.exe, and some of the other .NET-based utilities and classes when you are localizing large-scale development projects.

Resource Files in Visual Studio 2005
Now I am going to focus on using resource files in a Windows Forms-based application. Most of the concepts I explain will apply to using resource files in a class library DLL as well. Visual Studio 2005 makes it easy to work with resource files by supplying a visual editor. You can create a new resource file using the Add New Item command and choosing Resources file, as shown in Figure 4.
Figure 4 New Item Template for Adding Resource Files 
Once you have added a resource file to a project, there's no need to work directly with the XML format required inside a .resx file. Instead, Visual Studio supplies the friendly visual resource designer that is shown in Figure 5. This resource file designer makes it easy to add and maintain strings as well as other types of file-based resources such as graphics and XML documents.
Figure 5 Visual Studio Resource Editor 
When you compile a project that contains a resource file, Visual Studio compiles this .resx file into a .resources file and then links it inside the physical image of the resulting output assembly. That means that all the details involved in compiling the resource file and embedding it into the image of the target assembly are handled for you by Visual Studio.
Visual Studio also builds in a strongly typed resource class and exposes it in Visual Basic projects using the My namespace features introduced in Visual Basic 2005. That means you get the benefit of having the .NET ResourceManager class loading your resources, yet you never have to program directly against this class. For example, if you want to access resource strings you have added to LitwareStrings.resx, you can simply write the following code:
Sub Main_Load(sender As Object, e As EventArgs) 
  Handles MyBase.Load
  Me.Text = _
    My.Resources.LitwareStrings.MainFormCaption
  Me.lblUserWelcome.Text = _
    My.Resources.LitwareStrings.UserWelcome
End Sub

The Project-Level Resource File
While you can explicitly add one or more resource files to a Visual Studio project, that's often unnecessary because Visual Studio automatically includes a project-level resource file each time you create a new project. The project-level resource file can be accessed through the Project Properties dialog in the Visual Studio editor.
When you want to access resources such as strings from the project-level resource file, you can access them directly from within the My.Resources class:
Private Sub LoadResources()
  '*** load project-level resources
  Me.Text = My.Resources.MainFormCaption
  Me.lblWelcomeMessage.Text = My.Resources.UserWelcome
End Sub
As you can see, accessing strings from a resource file using a strongly typed resource class is pretty easy. You can take things a step further and get the same sort of strongly typed access to file-based resources when you have added graphics images and files containing things like XML, SQL, and JavaScript to a resource file. For example, assume you have added a graphics file named LitwareLogo.png and an XML file named Customers.xml to your project-level resource file. These resources will be automatically embedded into the project's output assembly and you can access them in a strongly typed fashion using the following code:
Me.picLogo.Image = My.Resources.LitwareLogo

Dim xmlDoc As New Xml.XmlDocument
xmlDoc.LoadXml(My.Resources.Customers)
You can observe that the strongly typed resource class automatically converts a.png file into an Image object that can be directly loaded into a PictureBox. The strongly typed resource class also converts the embedded XML file into a string that can be easily loaded into an XmlDocument object.

Culture Settings and Localization
It's a common requirement for software projects to be localized so they can be used by people who speak different languages. For example, imagine you are developing the Litware Customer Manager application with Visual Basic and Visual Studio 2005 and you are required to write localized versions of the application for users who speak English as well as users who speak French. Fortunately, the Microsoft .NET Framework and Visual Studio have many features geared toward localizing applications and class library DLLs.
When you first start designing and writing .NET-based software projects that need to support localization, you must quickly become familiar with the CultureInfo class defined inside the System.Globalization namespace. A CultureInfo object tracks a culture name that identifies a spoken language.
The culture name for English is "en" and the culture name for French is "fr". The culture name of a CultureInfo object can also carry additional information that identifies a particular region in the world such as "en-US" for US English, "en-GB" for British English, and "fr-BE" for Belgian French. Here's an example that creates and initializes CultureInfo objects with valid culture names:
Dim culture1 As CultureInfo = New CultureInfo("en-US")
Dim culture2 As CultureInfo = New CultureInfo("en-GB")
Dim culture3 As CultureInfo = New CultureInfo("fr")
Dim culture4 As CultureInfo = New CultureInfo("fr-BE")
There are actually two CultureInfo objects associated with the current thread that require your attention. The first CultureInfo object listed represents the current culture while the second CultureInfo object represents the current UI culture. You can determine the culture names for each of these two CultureInfo objects with the following code:
'*** determine current culture and current UI culture
Dim t As Thread = Thread.CurrentThread
Dim currentCulture As CultureInfo = t.CurrentCulture
Dim currentUICulture As CultureInfo = t.CurrentUICulture

'*** display cultures in console
Console.WriteLine("Current Culture: " & currentCulture.Name)
Console.WriteLine("Current UI Culture: " & currentUICulture.Name)
The first CultureInfo object known as the CurrentCulture is not used to localize strings in applications. Instead, it affects how the .NET Framework formats dates, numbers, and currency. For example, if you modify the CurrentCulture object back and forth between en-US and en-GB, it can lead to strange side effects such as a single currency value switching back and forth between US dollars ($100.00) and British Pounds ($100.00). As you can see, switching the currency formatting on a currency value can lead to incorrect results. For this reason, it's a common practice to keep the CurrentCulture static even when localizing your application for use with different languages.
If you determine that you do need to programmatically change the CurrentCulture, you should remember that you must use a culture name that includes a regional identifier. You can use a culture name of en-US, en-GB, or fr-BE. However, you will receive run-time errors if you try to change the CurrentCulture so that it has a culture name of en or fr because there is no regional information and formatting requirements become too ambiguous.
The second CultureInfo object known as the CurrentUICulture is far more important to the discussion of .NET-based application localization. Modifications to the CurrentUICulture object associated with the current thread influence how the .NET Framework and the ResourceManager class load assemblies that contain embedded resources. In particular, it can have the effect of loading the set of resources that have been localized to the language preferred by the current user.
To begin localization, you make multiple copies of resource files-one copy of each resource file for each language you want to support. For example, start by taking the project-wide resource file named Resources.resx and making a copy. You can do this with a simple copy-and-paste operation right inside the Visual Studio Solution Explorer.
Once you have copied a resource file such as Resources.resx, rename the copied resource file by adding the culture name just before the .resx extension, as shown in Figure 6. For example, you should have resource files named Resources.fr.resx for generic French localized strings and another resource file named Resources.fr-BE.resx for French strings localized specifically for Belgium.
Figure 6 Separate Resource Files for Each Language 

Satellite Assemblies
When you compile the project with localized resource files like the one that is shown in Figure 6, Visual Studio does not compile all the resources into a single output assembly. Instead, it compiles each of these localized resource files into its own separate assembly. Each of these localized assemblies contains only resources and no code. This type of localized, resource-only assembly is known as a satellite assembly.
Each satellite assembly is associated with a master assembly known as the neutral assembly. The neutral assembly contains all the code and it loads whatever satellite assembly is necessary to get the localized resources required by the current user. In my example, LitwareSmartClient.exe is the neutral assembly which contains all the application code. Then there are several satellite assemblies associated with LitwareSmartClient.exe. Each of these has the same file name, LitwareSmartClient.resources.dll.
When satellite assemblies are deployed along with the neutral assembly in the AppBase directory, they must be deployed according to the rules of the .NET assembly loader. In particular,each satellite assembly must be deployed in a directory named after its localized culture name. For example, the AppBase directory that contains LitwareSmartClient.exe should contain a subdirectory named fr-BE which holds the satellite assembly localized for Belgian French named LitwareSmartClient.resources.dll. As long as these rules are followed, the .NET Framework assembly loader along with the assistance of the ResourceManager class will load the correct set of resources when required.
Fortunately, Visual Studio knows how to name the satellite assemblies correctly and how to deploy them in the correct directory structure expected by the .NET assembly loader. To gain a better understanding of how all of the various pieces fit together, you can simply compile your project and then examine the resulting structure of the AppBase directory and the subdirectories inside that hold the satellite assemblies.

Loading Localized Resources
Once you have created and edited all the required localized resource files and compiled your project, it's time to concentrate on how you are going to make your application load the correct set of localized strings that are preferred by the current user. One way you can accomplish this is to obtain a reference to the current thread and assign a new created CultureInfo object to the CurrentUICulture property. If you are writing a Windows Forms-based application in Visual Basic, you can also use the following code:
My.Application.ChangeUICulture("fr-BE")
In the sample app that accompanies this column, I added support for the users to select their preferred language and to have the application track user language preferences when the application is shut down and restarted. While it is possible to maintain these sorts of user preferences using registry keys, Visual Studio 2005 makes it possible to avoid using the registry by adding an application setting that is tracked on a user-by-user basis. The sample app tracks a user-scoped application setting named UserLanguagePreference for this purpose. The application also contains an application startup event (seeFigure 7).
'*** code in ApplicationEvents.vb
Namespace My
 Partial Friend Class MyApplication
   Private Sub MyApplication_Startup(ByVal sender As Object, _
       ByVal e As StartupEventArgs) Handles Me.Startup

     '*** initialize application by setting preferred language
     Dim lang As String = My.Settings.UserLanguagePreference
     My.Application.ChangeUICulture(lang)

   End Sub
 End Class
End Namespace
The sample application also provides the user with a group of radio buttons as a means to switch from one language to another. Figure 8 shows a fragment of code from the event handler that responds to a user's request to change languages.
'*** retrieve user's language preference
Dim lang As String = CType(sender, Control).Tag

'*** save user setting
My.Settings.UserLanguagePreference = lang
My.Settings.Save()

'*** change application's UI culture
My.Application.ChangeUICulture(My.Settings.UserLanguagePreference)

'*** call custom method to reload localized strings
LoadResources()
You have now seen all the fundamental code that allows the user to switch languages. The .NET Framework responds to the call to My.Application.ChangeUICulture by loading in the correct satellite assembly the next time the ResourceManager retrieves strings from the project-level resources. After the call to ChangeUICulture, the application can then requery the application-level resource strings and load them into controls on the form with the exact same code you saw earlier inside the custom LoadResources method:
Me.Text = My.Resources.MainFormCaption
Me.lblWelcomeMessage.Text = My.Resources.UserWelcome
Note that the .NET assembly loader will first attempt to find an exact match with both language and region between the requested culture name and the culture name of an available satellite assembly. If the .NET assembly loader cannot find an exact match, it will then look for an available satellite assembly with a matching language. For example, if the requested language is fr-CA for Canadian French, the .NET Framework would first look for a satellite assembly with that language. If it cannot locate a satellite assembly with fr-CA, it then looks for a satellite assembly with a culture name of fr. If the .NET Framework cannot locate a satellite assembly with a culture name of fr, it then resorts to using the resources found within the neutral assembly, which has a set of default culture resources embedded inside. As you just saw, the .NET Framework can always fall back on the default culture resources from the neutral assembly if it cannot find a more specific satellite assembly.
When you compile the neutral assembly, you can mark it with a special attribute to inform the .NET Framework that the default culture resources are sufficient for those users who require a specific language. For example, you can add an assembly-level attribute to the AssemblyInfo.vb file of a Windows Forms-based application project, as shown in the following line of code:
<Assembly: System.Resources.NeutralResourcesLanguage("en")>
As it is used here, the NeutralResourcesLanguage attribute informs the .NET assembly loader that it can use the default culture resources whenever the current user has requested that the application be localized for English.

Localizing Form and Control Settings
You have just seen how to localize string resources on a project-wide basis. This technique involves copying and maintaining localized resource files. Visual Studio 2005 provides some extra assistance when you need to localize strings specific to a form and the controls it contains.
Each form has a Localizable property that can be set to either true or false. If you set this property to true, as shown in Figure 9, Visual Studio 2005 will create and maintain a set of localized resource files for you behind the scenes.
When you set the Localizable property to true, the initial Language setting is default. As you add property values into the property sheet for the form and its controls, Visual Studio maintains them in a resource file behind the scenes that is compiled into the neutral assembly. When you change the Language property of the form to a specific language, such as "French (Belgium)," Visual Studio then creates a new localized resource file that will be compiled into a satellite assembly. Behind the scenes, things work exactly as they do with project-wide resources. It's just that Visual Studio eliminates your need to work directly with resource files and allows you to work with a standard property sheet as you add property values for things such as control Text properties.
Figure 9 Localizable Property 
Visual Studio is required to add some extra code into the form behind the scenes to support form localization. In particular, Visual Studio adds code to load the correct localized resources and assign their values to form and control properties. Instead of using the ResourceManager, the code generated by Visual Studio 2005 uses a more specialized class that derives from ResourceManager named ComponentResourceManager that is defined in the System.ComponentModel namespace.
When a localized form loads, everything required to load the localized resources is done for you by the code that is generated by Visual Studio. In particular, Visual Studio supplies the code to create an instance of the ComponentResourceManager, which loads the proper set of resources and assigns all the necessary control values.
However, if a form is already loaded and the user switches languages, you must supply some additional code to refresh the form with the resources of the requested language. The following shows a sample of code that uses the ComponentResourceManager to accomplish this goal:
Dim crm As ComponentResourceManager
crm = New ComponentResourceManager(GetType(Main))
crm.ApplyResources(cmdAddCustomer, cmdAddCustomer.Name)
crm.ApplyResources(mnuFile, mnuFile.Name)
crm.ApplyResources(mnuFileAddCustomer, mnuFileAddCustomer.Name)
As you can see, you can create and initialize an instance of the ComponentResourceManager by passing type information about the form, which in this case is named Main.
The completed sample application, which demonstrates all the localization techniques discussed in this column, is shown in Figure 10. The application now supports US English, British English, and Belgian French. Also note that since there is a satellite assembly localized to fr without any specific region, the application also supports a generic form of French for users around the world who speak French.
Figure 10 Supporting Localized Versions 
If you want to add support for additional languages in the future, it's not very complicated. It's simply a matter of creating and maintaining additional resource files. In fact, you can add support for new languages without ever having to recompile the neutral assembly that contains all the application code. This is one of the most valuable features of the .NET Framework strategy for localizing applications and class library DLLs.

Conclusion
This month's column covered the fundamentals of resources and localization in the .NET Framework by working through how to localize a simple Windows Forms-based application. In my next Basic Instincts column, I will build upon what I covered this month and move on to a discussion of resource and localization in ASP.NET 2.0, which has some valuable and unique features for utilizing resources and localizing applications.


Send you questions and comments for Ted to  instinct@microsoft.com.

Posted by 생각처럼
, |
vba로 작성된 코드를 c/c++이나 vb로 바꾸려다보면 제일 먼저 걸리는 문제가 엑셀과 관련된 부분을 바꿔야하는 것이다. 특히 worksheetfunction을 사용하는 경우 더욱 그러하다. 별 고민없이 가져다 사용한 함수를 따로 만들어야 한다. 이럴때 엑셀의 편리함을 절실히 느끼게 되고 심한 경우엔 그냥 vba로 주저앉게 된다. vba로 만든 ADF(Augmented Dickey Fuller test)를 vb로 바꾸려니 통계함수가 걸리는 것이다. 

NORMSDIST()함수는 표준 정규 누적 분포 함수의 확률값을 구한다. 이 분포의 평균은 0이고 표준 편차는 1이다. NORMDIST()함수는 지정한 평균과 표준편차에 따라 정규분포값을 구한다. 따라서 NORMSDIST()함수는 NORMDIST()함수의 특별한 경우라고 볼 수 있다. 즉 NORMSDIST(x)=NORMDIST(x,0,1,TRUE)와 같다.

다음은 NORMSDIST()함수의 vb버전이다.
'// The cumulative normal distribution function
Public Function CND(X As Double) As Double
   Const PI = 3.14159265358979
   Dim L As Double
   Dim K As Double
   
   Const a1 = 0.31938153
   Const a2 = -0.356563782
   Const a3 = 1.781477937
   Const a4 = -1.821255978
   Const a5 = 1.330274429
   
   L = Abs(X)
   K = 1 / (1 + 0.2316419 * L)
   CND = 1 - 1 / Sqr(2 * PI) * Exp(-L ^ 2 / 2) _
       * (a1 * K + a2 * K ^ 2 + a3 * K ^ 3 + a4 * K ^ 4 + a5 * K ^ 5)
   If X < 0 Then
       CND = 1 - CND
   End If
End Function
Posted by 생각처럼
, |

/*Lognormdist = NORMSDIST((LN(x) – mu)/sigma)  eg:- Lognormdist(0.367879441,3,2) will give  0.022750062 with this function and in excel it will give 0.022750132*/

/* Note: Doouble quotes for “en-US” and “F” may not be properly shown while pasting this code.you need to manaully remove the double quotes and add it again*/

   public double Lognormdist( double x, double zeta, double sigma)

  {
  /*Gets a NumberFormatInfo associated with the en-US culture.*/

  System.Globalization.NumberFormatInfo nfi =new System.Globalization.CultureInfo( “en-US”, false ).NumberFormat;nfi.NumberDecimalDigits = 9;

  double u = (Math.Log(x) – zeta)/sigma; double p = this.NormalDistribution(u);

  return Convert.ToDouble(p.ToString(“F”, nfi) );
  }
  private double NormalDistribution(double X)
  {

  /* Gets a NumberFormatInfo associated with the en-US culture.*/
  System.Globalization.NumberFormatInfo nfi =new System.Globalization.CultureInfo( “en-US”, false ).NumberFormat;nfi.NumberDecimalDigits = 9;

  double L = 0.0; double K = 0.0; double dCND = 0.0; const double a1 = 0.31938153; const double a2 = -0.356563782; const double a3 = 1.781477937; const double a4 = -1.821255978; const double a5 = 1.330274429;L = Math.Abs(X);K = 1.0 / (1.0 + 0.2316419 * L);dCND = 1.0 – 1.0 / Math.Sqrt(2 * Convert.ToDouble(Math.PI.ToString())) * Math.Exp(-L * L / 2.0) * (a1 * K + a2 * K * K + a3 * Math.Pow(K, 3.0) + a4 * Math.Pow(K, 4.0) + a5 * Math.Pow (K, 5.0));

  if (X < 0){double ff = Convert.ToDouble(dCND.ToString( “F”, nfi )); return 1.0 – dCND;}

  else{ return dCND;}
  }

Posted by 생각처럼
, |
/// <summary>
        /// beta函数
        /// </summary>
        public static double Beta(double a, double b)
        {
            double result = 0.0;

            if (a <= 0 || b <= 0)
            {
                //报错!a,b必须同时大于零;
            }
            else
            {
                result = Math.Exp((gammaln(a) + gammaln(b) - gammaln(a + b)));
            }
            return result;
        }

        /// <summary>
        /// gammaln函数
        /// </summary>
        /// <param name="x"></param>
        /// <returns></returns>
        public static double gammaln(double x)
        {
            double res = 0.0;
            double d1 = -5.772156649015328605195174e-1;

            double[,] p1 = {{4.945235359296727046734888e0},{ 2.018112620856775083915565e2},
                           {2.290838373831346393026739e3},{ 1.131967205903380828685045e4},
                           {2.855724635671635335736389e4},{ 3.848496228443793359990269e4},
                           {2.637748787624195437963534e4},{ 7.225813979700288197698961e3}};

            double[,] q1 = {{6.748212550303777196073036e1},{ 1.113332393857199323513008e3}, 
                           {7.738757056935398733233834e3},{ 2.763987074403340708898585e4},
                           {5.499310206226157329794414e4},{ 6.161122180066002127833352e4}, 
                           {3.635127591501940507276287e4},{ 8.785536302431013170870835e3}};

            double d2 = 4.227843350984671393993777e-1;

            double[,] p2 = {{4.974607845568932035012064e0},{ 5.424138599891070494101986e2},
                           {1.550693864978364947665077e4},{ 1.847932904445632425417223e5},
                           {1.088204769468828767498470e6},{ 3.338152967987029735917223e6}, 
                           {5.106661678927352456275255e6},{ 3.074109054850539556250927e6}};

            double[,] q2 = {{1.830328399370592604055942e2},{ 7.765049321445005871323047e3},
                           {1.331903827966074194402448e5},{ 1.136705821321969608938755e6},
                           {5.267964117437946917577538e6},{ 1.346701454311101692290052e7},
                           {1.782736530353274213975932e7},{ 9.533095591844353613395747e6}};

            double d4 = 1.791759469228055000094023e0;

            double[,] p4 = {{1.474502166059939948905062e4},{ 2.426813369486704502836312e6}, 
                           {1.214755574045093227939592e8},{ 2.663432449630976949898078e9},
                           {2.940378956634553899906876e10},{ 1.702665737765398868392998e11},
                           {4.926125793377430887588120e11},{5.606251856223951465078242e11}};

            double[,] q4 = {{2.690530175870899333379843e3},{ 6.393885654300092398984238e5}, 
                           {4.135599930241388052042842e7},{ 1.120872109616147941376570e9},
                           {1.488613728678813811542398e10},{1.016803586272438228077304e11},
                           {3.417476345507377132798597e11},{ 4.463158187419713286462081e11}};

            double[,] c = {{-1.910444077728e-03},{ 8.4171387781295e-04}, 
                          {-5.952379913043012e-04},{ 7.93650793500350248e-04}, 
                          {-2.777777777777681622553e-03},{ 8.333333333333333331554247e-02}, 
                          { 5.7083835261e-03}};

            double eps = 2.2204e-016;
            if (x <= 0)
            {
                //报错!
            }
            else
            {
                double xden = 0.0;
                double xnum = 0.0;

                res = x;
                if (x > 0 && x <= eps)
                {
                    res = -Math.Log(x);
                }
                else if ((x > eps) && (x <= 0.5))
                {
                    double y = x;
                    xden = 1;
                    xnum = 0;
                    for (int i = 0; i < 8; i++)
                    {
                        xnum = xnum * y + p1[i, 0];
                        xden = xden * y + q1[i, 0];
                    }
                    res = -Math.Log(y) + (y * (d1 + y * (xnum / xden)));
                }
                else if ((x > 0.5) && (x <= 0.6796875))
                {
                    double xm1 = (x - 0.5) - 0.5;
                    xden = 1;
                    xnum = 0;
                    for (int i = 0; i < 8; i++)
                    {
                        xnum = xnum * xm1 + p2[i, 0];
                        xden = xden * xm1 + q2[i, 0];
                    }
                    res = -Math.Log(x) + xm1 * (d2 + xm1 * (xnum / xden));
                }
                else if ((x > 0.6796875) && (x <= 1.5))
                {
                    double xm1 = (x - 0.5) - 0.5;
                    xden = 1;
                    xnum = 0;
                    for (int i = 0; i < 8; i++)
                    {
                        xnum = xnum * xm1 + p1[i, 0];
                        xden = xden * xm1 + q1[i, 0];
                    }
                    res = xm1 * (d1 + xm1 * (xnum / xden));
                }
                else if ((x > 1.5) && (x <= 4))
                {
                    double xm2 = x - 2;
                    xden = 1;
                    xnum = 0;
                    for (int i = 0; i < 8; i++)
                    {
                        xnum = xnum * xm2 + p2[i, 0];
                        xden = xden * xm2 + q2[i, 0];
                    }
                    res = xm2 * (d2 + xm2 * (xnum / xden));
                }
                else if ((x > 4) && (x <= 12))
                {
                    double xm4 = x - 4;
                    xden = -1;
                    xnum = 0;
                    for (int i = 0; i < 8; i++)
                    {
                        xnum = xnum * xm4 + p4[i, 0];
                        xden = xden * xm4 + q4[i, 0];
                    }
                    res = d4 + xm4 * (xnum / xden);

                }
                else if (x > 12)
                {
                    double y = x;
                    double r = c[6, 0];// 等于:double r = repmat(c[6, 0], 1)[0,0];
                    double ysq = y * y;
                    for (int i = 0; i < 6; i++)
                    {
                        r = r / ysq + c[i, 0];
                    }
                    r = r / y;
                    double corr = Math.Log(y);
                    double spi = 0.9189385332046727417803297;
                    res = r + spi - 0.5 * corr + y * (corr - 1);
                }
            }

            return res;
        }
Posted by 생각처럼
, |

최근에 달린 댓글

최근에 받은 트랙백

글 보관함