[Login Dialog]
1001 = 用戶帳號
1002 = 用戶密碼
1017 = 登 錄
1018 = 退 出
[Main Window]
1001 = 檔案(&F)
1002 = 編輯(&E)
1003 = 視圖(&V)
...
GetDlgCtrlID())class CLanguageManager {
struct ItemContext {
UINT uCtrlID;
CString strText;
};
vector m_vItems; // 所有語言項
CIniFile m_ini; // 簡易 INI 讀取類
public:
BOOL Load(LPCTSTR iniPath, LPCTSTR section) {
m_ini.SetFile(iniPath);
CStringArray keys, values;
if (!m_ini.GetSectionValues(section, keys, values)) return FALSE;
m_vItems.clear();
for (int i = 0; i < keys.GetSize(); ++i) {
ItemContext ctx;
ctx.uCtrlID = _ttoi(keys[i]);
ctx.strText = values[i];
m_vItems.push_back(ctx);
}
return TRUE;
}
BOOL ApplyToWindow(CWnd* pWnd) {
if (!pWnd) return FALSE;
// 1. 視窗標題
UINT id = pWnd->GetDlgCtrlID();
SetControlText(pWnd, id);
// 2. 遞迴所有子控制項
CWnd* pChild = pWnd->GetWindow(GW_CHILD);
while (pChild) {
id = pChild->GetDlgCtrlID();
SetControlText(pChild, id);
pChild = pChild->GetNextWindow(GW_HWNDNEXT);
}
return TRUE;
}
private:
void SetControlText(CWnd* pCtrl, UINT ctrlID) {
for (const auto& item : m_vItems) {
if (item.uCtrlID == ctrlID) {
pCtrl->SetWindowText(item.strText);
break;
}
}
}
};
class CIniFile {
CStringArray m_lines;
CString m_path;
public:
void SetFile(LPCTSTR path) { m_path = path; Load(); }
BOOL GetSectionValues(LPCTSTR section, CStringArray& keys, CStringArray& values) {
keys.RemoveAll(); values.RemoveAll();
BOOL bInSection = FALSE;
for (int i = 0; i < m_lines.GetSize(); ++i) {
CString line = m_lines[i];
line.Trim();
if (line.IsEmpty() || line.Left(2) == _T("//")) continue;
if (line[0] == _T('[')) {
int end = line.Find(_T(']'));
if (end > 0) {
CString sec = line.Mid(1, end-1); sec.Trim();
bInSection = (sec.CompareNoCase(section) == 0);
}
continue;
}
if (!bInSection) continue;
int eq = line.Find(_T('='));
if (eq > 0) {
CString key = line.Left(eq); key.Trim();
CString val = line.Mid(eq+1); val.Trim();
keys.Add(key); values.Add(val);
}
}
return keys.GetSize() > 0;
}
private:
void Load() {
m_lines.RemoveAll();
CStdioFile file;
if (!file.Open(m_path, CFile::modeRead | CFile::typeText)) return;
CString line;
while (file.ReadString(line)) m_lines.Add(line);
file.Close();
}
};
// 應用程式啟動時
CLanguageManager g_langMgr;
BOOL CMyApp::InitInstance() {
// 載入預設語言 (可從註冊表讀取上次選擇)
g_langMgr.Load(_T("lang_chs.ini"), _T("Login Dialog"));
g_langMgr.Load(_T("lang_chs.ini"), _T("Main Window")); // 累加多 Section
// ...
}
// 視窗建立後套用
void CLoginDlg::OnInitDialog() {
CDialog::OnInitDialog();
g_langMgr.ApplyToWindow(this);
}
// 切換語言
void CMainFrame::OnLanguageChinese() {
g_langMgr.Load(_T("lang_chs.ini"), _T("Login Dialog"));
g_langMgr.Load(_T("lang_chs.ini"), _T("Main Window"));
g_langMgr.ApplyToWindow(this); // 主視窗
g_langMgr.ApplyToWindow(m_pLoginDlg); // 模態對話方塊需單獨套用
}
| 優點 | 缺點 |
|---|---|
| 極簡部署:文字檔即語言包 | 不支援複雜格式 (複數、性別、語序調整) |
| 無需重新編譯新增語言 | 控制項 ID 衝突風險 (同 ID 不同視窗需不同 Section 隔離) |
| 執行時動態切換 | 不支援右-to-左 (RTL) 語言佈局反轉 |
| 零第三方庫依賴 | 大型專案項目多時 INI 檔維護成本高 |
[ClassName::DialogID] 如 [CLoginDlg::IDD_LOGIN]lang_*.ini 自動建構語言選單此方案以 INI + 控制項 ID 映射 + 自動遍歷 三大核心,以最小代價實現多語言支援,極適合中小型 VC++ 專案快速落地國際化。