?? readme.txt
字號:
用 INI 文件實現多語言程序
一、優點
比起使用資源 DLL 實現多語言程序,INI 方式的具有更多的擴展性,任何一個普通用戶都可以做出任何一種語言界面。
二、功能
為了做到完美的多語言程序,我們設計一個 CLanguage 靜態類 (因為是靜態類,改為 C 代碼只要一點簡單的工作),應該實現如下功能:
1. 每個語言一個 INI 文件 (特別地,把語言文件擴展名定為 .lng,符合 INI 格式)。
2. 沒有外部語言情況下,使用程序內置的默認語言。
3. 能動態切換到任何一種語言 (包括內置語言),而不是告訴用戶切換語言要重新啟動程序。
4. 沒有選擇語言的狀態下 (比如程序剛安裝),自動根據當前用戶環境選擇語言文件 (如果有的話,沒有則使用默認語言)。
5. 列出程序目錄下的 .lng 文件,并加入到菜單中。
6. 不同的語言可能造成界面上不同的顯示效果,所以應該允許在語言文件中指定字體 (程序的資源中還應該考慮不同語言的情況,如 Static Text 控件是否夠寬,以容納其它語言的文本)。
7. 使原有的程序做盡量少的改動,加入 CLanguage 后,作少量改動即可實現多語言。
8. 可以定義 #define _MAKELANG 宏,這樣編譯后,使程序自動生成語言文件。
9. 支持 UNICODE 方式編譯。
三、實現
先想想,程序中與語言相關的文本有哪些……,嗯……,不過就如下四個嘛:
1. 常量文本 (任何在代碼中的 UI 文本)。
2. 資源文本。
3. 菜單文本。
4. 對話框文本。
一個一個來,各個擊破:
1. 常量文本: 放在語言文件的 [Text] 節中,每個文本對應一個 ID。程序中使用到常量文本的時候,如果語言文件中存在該 ID 的文本,則使用它,否則,使用內部的常量文本。
2. 資源文件: 放在語言文件的 [String] 節中,這個最簡單了,就使用資源文本的 ID。原來所有使用 LoadString 的地方,改用 CLanguage::TraslateString,即現嘗試從語言文件中載入文本。
3. 菜單文本: 放在語言文件的 [%菜單資源名%] 節中。當菜單被載入后,調用 CLanguage::TranslateMenu 翻譯菜單。菜單的處理有點麻煩。如果菜單是命令項,則使用命令 ID 作為標識,如果是彈出項,則使用位置作為標識 (因此彈出菜單的位置不能改變,不過這點很容易做到)。另外,切換到內置語言的時候,需要重新載入菜單資源中的菜單文本,這個實現起來挺麻煩的,還要考慮是 MENU 還是 MENUEX 資源。嘿嘿,不過我都實現就是了,只管直接使用 :) ……
4. 對話框文本: 和菜單的實現方式差不多。對話框初始化后,調用 CLanguage::TranslateDialog 翻譯對話框上的文本。對話框上的控件必須具有 ID (在此對話框內唯一,因為 不能再使用 IDC_STATIC 這種 ID 了)。同菜單一樣,對話框也有切換到內置語言的問題,不過這里我忽略了它,因為我假設切換語言是在主窗口中進行,此時沒有任何對話框顯示的。如果是基于對話框的程序,可以仿造菜單的代碼寫一個從對話框資源中載入文本的代碼,不過挺煩、難的,還是算了吧,變通一下,想法子重新 DialogBox 一遍,嘿嘿,搞定……
好了,開始寫代碼……
.......
四、使用
呵呵~~,搞定,Clanguage 部分聲明如下:
// 初始化語言,返回確定要使用的語言
static LANGID Initialize();
// 列出語言到菜單中
static UINT List(HMENU hMenu);
// 設置語言
static VOID Set(HMENU hMenu, UINT uLang);
// 獲取語言描述,比如語言是誰翻譯制作什么的
static PTSTR GetDescription(HMENU hMenu, UINT uLang);
// 獲取常量文本
static PTSTR TranslateText(PCTSTR ptzName, PCTSTR ptzDefault = NULL);
// 獲取常量文本,并替換特殊字符 (替換換行和零字符,有時候要用到,比如多行文本,又比如 GetOpenFileName 使用到的 Filter 字符串列表,需要替換零字符)
static PTSTR TranslateTextEx(PCTSTR ptzName, PCTSTR ptzDefault = NULL);
// 獲取資源字符串
static PTSTR TranslateString(UINT uResID);
// 更新菜單
static VOID TranslateMenu(HMENU hMenu, PCTSTR ptzResName);
// 更新對話框
static VOID TranslateDialog(HWND hWnd, PCTSTR ptzResName);
常量文本因為需要指定關聯 ID,所以應該放在一起 (即使不是多語言,也應該養成習慣,把所有的字符串放在一起,用宏來引用這些字符串) 。在 Language.h 中,我定義了如下四個宏,下面是其中兩個:
#define _Lang(n, a) CLanguage::TranslateText(TEXT(#n), TEXT(a))
#define _LangEx(n, a) CLanguage::TranslateTextEx(TEXT(#n), TEXT(a)) // 多行文本或字符串組
比如用到以下常量字符串,都放在 Define.h 中吧:
#define LNG_Ready _Lang(0, "Ready.")
#define LNG_BmpFilter _LangEx(1, "Bitmap Files(*.bmp)\0*.bmp\0")
... (等等,ID 不重復,為了美觀,連續遞增比較好看:)
具體使用看代碼吧,三言兩句講不清楚,還有不少注意事項的,挺晚的了,我要看看電視睡覺了……
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -