Feature: Embed language resources, disk files act as optional patches
This commit is contained in:
@@ -50,47 +50,71 @@ public:
|
|||||||
char line[4096];
|
char line[4096];
|
||||||
|
|
||||||
while (fgets(line, sizeof(line), f)) {
|
while (fgets(line, sizeof(line), f)) {
|
||||||
// 去除行尾换行符
|
ParseLine(line, currentSection);
|
||||||
size_t len = strlen(line);
|
|
||||||
while (len > 0 && (line[len - 1] == '\n' || line[len - 1] == '\r'))
|
|
||||||
line[--len] = '\0';
|
|
||||||
|
|
||||||
if (len == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// 跳过注释
|
|
||||||
if (line[0] == ';' || line[0] == '#')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// 检测 section 头: [SectionName]
|
|
||||||
// 真正的 section 头:']' 后面没有 '='(否则是 key=value)
|
|
||||||
if (line[0] == '[') {
|
|
||||||
char* end = strchr(line, ']');
|
|
||||||
if (end) {
|
|
||||||
char* eqAfter = strchr(end + 1, '=');
|
|
||||||
if (!eqAfter) {
|
|
||||||
// 纯 section 头,如 [Strings]
|
|
||||||
*end = '\0';
|
|
||||||
currentSection = line + 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// ']' 后有 '=',如 [使用FRP]=[Using FRP],当作 key=value 处理
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 不在任何 section 内则跳过
|
fclose(f);
|
||||||
if (currentSection.empty())
|
return true;
|
||||||
continue;
|
|
||||||
|
|
||||||
// 解析 key=value(只按第一个 '=' 分割,不 trim)
|
|
||||||
// key 和 value 均做反转义(\n \r \t \\ \")
|
|
||||||
char* eq = strchr(line, '=');
|
|
||||||
if (eq && eq != line) {
|
|
||||||
*eq = '\0';
|
|
||||||
std::string key = Unescape(std::string(line));
|
|
||||||
std::string value = Unescape(std::string(eq + 1));
|
|
||||||
m_sections[currentSection][key] = value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 从内存加载 INI 数据,返回是否成功
|
||||||
|
// 用于加载嵌入的资源数据
|
||||||
|
bool LoadFromMemory(const char* data, size_t size)
|
||||||
|
{
|
||||||
|
Clear();
|
||||||
|
|
||||||
|
if (!data || size == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::string currentSection;
|
||||||
|
const char* p = data;
|
||||||
|
const char* end = data + size;
|
||||||
|
|
||||||
|
while (p < end) {
|
||||||
|
// 找到行尾
|
||||||
|
const char* lineEnd = p;
|
||||||
|
while (lineEnd < end && *lineEnd != '\n' && *lineEnd != '\r')
|
||||||
|
lineEnd++;
|
||||||
|
|
||||||
|
// 复制行内容
|
||||||
|
size_t lineLen = lineEnd - p;
|
||||||
|
if (lineLen > 0 && lineLen < 4096) {
|
||||||
|
char line[4096];
|
||||||
|
memcpy(line, p, lineLen);
|
||||||
|
line[lineLen] = '\0';
|
||||||
|
ParseLine(line, currentSection);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 跳过换行符
|
||||||
|
p = lineEnd;
|
||||||
|
while (p < end && (*p == '\n' || *p == '\r'))
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 追加加载(不清除现有数据,用于覆盖)
|
||||||
|
bool LoadFileAppend(const char* filePath)
|
||||||
|
{
|
||||||
|
if (!filePath || !filePath[0])
|
||||||
|
return false;
|
||||||
|
|
||||||
|
FILE* f = nullptr;
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
if (fopen_s(&f, filePath, "r") != 0 || !f)
|
||||||
|
return false;
|
||||||
|
#else
|
||||||
|
f = fopen(filePath, "r");
|
||||||
|
if (!f)
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::string currentSection;
|
||||||
|
char line[4096];
|
||||||
|
|
||||||
|
while (fgets(line, sizeof(line), f)) {
|
||||||
|
ParseLine(line, currentSection);
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(f);
|
fclose(f);
|
||||||
@@ -138,6 +162,52 @@ public:
|
|||||||
private:
|
private:
|
||||||
TSections m_sections;
|
TSections m_sections;
|
||||||
|
|
||||||
|
// 解析单行 INI 内容
|
||||||
|
void ParseLine(char* line, std::string& currentSection)
|
||||||
|
{
|
||||||
|
// 去除行尾换行符
|
||||||
|
size_t len = strlen(line);
|
||||||
|
while (len > 0 && (line[len - 1] == '\n' || line[len - 1] == '\r'))
|
||||||
|
line[--len] = '\0';
|
||||||
|
|
||||||
|
if (len == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// 跳过注释
|
||||||
|
if (line[0] == ';' || line[0] == '#')
|
||||||
|
return;
|
||||||
|
|
||||||
|
// 检测 section 头: [SectionName]
|
||||||
|
// 真正的 section 头:']' 后面没有 '='(否则是 key=value)
|
||||||
|
if (line[0] == '[') {
|
||||||
|
char* end = strchr(line, ']');
|
||||||
|
if (end) {
|
||||||
|
char* eqAfter = strchr(end + 1, '=');
|
||||||
|
if (!eqAfter) {
|
||||||
|
// 纯 section 头,如 [Strings]
|
||||||
|
*end = '\0';
|
||||||
|
currentSection = line + 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// ']' 后有 '=',如 [使用FRP]=[Using FRP],当作 key=value 处理
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 不在任何 section 内则跳过
|
||||||
|
if (currentSection.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// 解析 key=value(只按第一个 '=' 分割,不 trim)
|
||||||
|
// key 和 value 均做反转义(\n \r \t \\ \")
|
||||||
|
char* eq = strchr(line, '=');
|
||||||
|
if (eq && eq != line) {
|
||||||
|
*eq = '\0';
|
||||||
|
std::string key = Unescape(std::string(line));
|
||||||
|
std::string value = Unescape(std::string(eq + 1));
|
||||||
|
m_sections[currentSection][key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 反转义:将字面量 \n \r \t \\ \" 转为对应的控制字符
|
// 反转义:将字面量 \n \r \t \\ \" 转为对应的控制字符
|
||||||
static std::string Unescape(const std::string& s)
|
static std::string Unescape(const std::string& s)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -526,14 +526,13 @@ BOOL CMy2015RemoteApp::InitInstance()
|
|||||||
SetChineseThreadLocale();
|
SetChineseThreadLocale();
|
||||||
|
|
||||||
// 加载语言包(必须在显示任何文本之前)
|
// 加载语言包(必须在显示任何文本之前)
|
||||||
|
// 内嵌资源支持 en_US 和 zh_TW,无需外部文件
|
||||||
auto lang = THIS_CFG.GetStr("settings", "Language", "en_US");
|
auto lang = THIS_CFG.GetStr("settings", "Language", "en_US");
|
||||||
auto langDir = THIS_CFG.GetStr("settings", "LangDir", "./lang");
|
auto langDir = THIS_CFG.GetStr("settings", "LangDir", "./lang");
|
||||||
langDir = langDir.empty() ? "./lang" : langDir;
|
langDir = langDir.empty() ? "./lang" : langDir;
|
||||||
if (PathFileExists(langDir.c_str())) {
|
g_Lang.Init(langDir.c_str()); // 初始化目录(用于磁盘补丁文件)
|
||||||
g_Lang.Init(langDir.c_str());
|
g_Lang.Load(lang.c_str()); // 加载语言(优先内嵌资源,再覆盖磁盘文件)
|
||||||
g_Lang.Load(lang.c_str());
|
Mprintf("语言: %s, 目录: %s\n", lang.c_str(), langDir.c_str());
|
||||||
Mprintf("语言包目录已经指定[%s], 语言数量: %d\n", langDir.c_str(), g_Lang.GetLanguageCount());
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建并显示启动画面
|
// 创建并显示启动画面
|
||||||
CSplashDlg* pSplash = new CSplashDlg();
|
CSplashDlg* pSplash = new CSplashDlg();
|
||||||
|
|||||||
Binary file not shown.
@@ -1,11 +1,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <locale.h>
|
#include <locale.h>
|
||||||
#include <afxwin.h>
|
#include <afxwin.h>
|
||||||
#include "common/IniParser.h"
|
#include "common/IniParser.h"
|
||||||
|
#include "resource.h" // 用于内嵌语言资源 ID
|
||||||
|
|
||||||
// 设置线程区域为简体中文
|
// 设置线程区域为简体中文
|
||||||
// 这样 MBCS 程序在非中文系统上创建对话框时,也能正确解码 RC 资源中的 GBK 中文
|
// 这样 MBCS 程序在非中文系统上创建对话框时,也能正确解码 RC 资源中的 GBK 中文
|
||||||
@@ -60,12 +62,18 @@ public:
|
|||||||
CreateDirectory(m_langDir, NULL);
|
CreateDirectory(m_langDir, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取可用的语言列表
|
// 获取可用的语言列表(包括内嵌语言)
|
||||||
std::vector<CString> GetAvailableLanguages()
|
std::vector<CString> GetAvailableLanguages()
|
||||||
{
|
{
|
||||||
std::vector<CString> langs;
|
std::vector<CString> langs;
|
||||||
CString searchPath = m_langDir + _T("\\*.ini");
|
std::set<CString> langSet; // 用于去重
|
||||||
|
|
||||||
|
// 1. 添加内嵌语言(始终可用)
|
||||||
|
langSet.insert(_T("en_US"));
|
||||||
|
langSet.insert(_T("zh_TW"));
|
||||||
|
|
||||||
|
// 2. 扫描磁盘上的语言文件
|
||||||
|
CString searchPath = m_langDir + _T("\\*.ini");
|
||||||
WIN32_FIND_DATA fd;
|
WIN32_FIND_DATA fd;
|
||||||
HANDLE hFind = FindFirstFile(searchPath, &fd);
|
HANDLE hFind = FindFirstFile(searchPath, &fd);
|
||||||
if (hFind != INVALID_HANDLE_VALUE) {
|
if (hFind != INVALID_HANDLE_VALUE) {
|
||||||
@@ -73,30 +81,43 @@ public:
|
|||||||
CString filename(fd.cFileName);
|
CString filename(fd.cFileName);
|
||||||
int dotPos = filename.ReverseFind(_T('.'));
|
int dotPos = filename.ReverseFind(_T('.'));
|
||||||
if (dotPos > 0) {
|
if (dotPos > 0) {
|
||||||
langs.push_back(filename.Left(dotPos));
|
langSet.insert(filename.Left(dotPos));
|
||||||
}
|
}
|
||||||
} while (FindNextFile(hFind, &fd));
|
} while (FindNextFile(hFind, &fd));
|
||||||
FindClose(hFind);
|
FindClose(hFind);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 转为 vector 返回
|
||||||
|
for (const auto& lang : langSet) {
|
||||||
|
langs.push_back(lang);
|
||||||
|
}
|
||||||
return langs;
|
return langs;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查语言文件编码是否为 ANSI
|
// 检查语言文件编码是否为 ANSI
|
||||||
// 返回 false 表示文件不存在或编码不是 ANSI(检测 BOM 和 UTF-8 无 BOM)
|
// 返回 false 表示编码不是 ANSI(检测 BOM 和 UTF-8 无 BOM)
|
||||||
|
// 内嵌语言(en_US, zh_TW)直接返回 true
|
||||||
bool CheckEncoding(const CString& langCode)
|
bool CheckEncoding(const CString& langCode)
|
||||||
{
|
{
|
||||||
|
// 中文模式无需检查
|
||||||
if (langCode == _T("zh_CN") || langCode.IsEmpty()) {
|
if (langCode == _T("zh_CN") || langCode.IsEmpty()) {
|
||||||
TRACE("[LangEnc] zh_CN or empty, skip check\n");
|
TRACE("[LangEnc] zh_CN or empty, skip check\n");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 内嵌语言无需检查(已确保编码正确)
|
||||||
|
if (langCode == _T("en_US") || langCode == _T("zh_TW")) {
|
||||||
|
TRACE("[LangEnc] builtin language, skip check\n");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
CString langFile = m_langDir + _T("\\") + langCode + _T(".ini");
|
CString langFile = m_langDir + _T("\\") + langCode + _T(".ini");
|
||||||
TRACE("[LangEnc] Checking: %s\n", (LPCSTR)langFile);
|
TRACE("[LangEnc] Checking: %s\n", (LPCSTR)langFile);
|
||||||
|
|
||||||
FILE* f = nullptr;
|
FILE* f = nullptr;
|
||||||
if (fopen_s(&f, (LPCSTR)langFile, "rb") != 0 || !f) {
|
if (fopen_s(&f, (LPCSTR)langFile, "rb") != 0 || !f) {
|
||||||
TRACE("[LangEnc] fopen failed\n");
|
TRACE("[LangEnc] fopen failed\n");
|
||||||
return false;
|
return false; // 非内嵌语言必须有磁盘文件
|
||||||
}
|
}
|
||||||
|
|
||||||
// 读取文件内容(最多检测前 4KB 即可判断)
|
// 读取文件内容(最多检测前 4KB 即可判断)
|
||||||
@@ -164,26 +185,103 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 加载语言文件
|
// 加载语言文件
|
||||||
|
// 优先从内嵌资源加载,然后用磁盘文件覆盖(如果存在)
|
||||||
bool Load(const CString& langCode)
|
bool Load(const CString& langCode)
|
||||||
{
|
{
|
||||||
m_strings.clear();
|
m_strings.clear();
|
||||||
m_currentLang = langCode;
|
m_currentLang = langCode;
|
||||||
|
|
||||||
// 如果是中文,不需要加载翻译
|
// 中文模式:检查是否有补丁文件
|
||||||
if (langCode == _T("zh_CN") || langCode.IsEmpty()) {
|
if (langCode == _T("zh_CN") || langCode.IsEmpty()) {
|
||||||
|
// 尝试加载中文补丁文件(可选)
|
||||||
|
CString patchFile = m_langDir + _T("\\zh_CN.ini");
|
||||||
|
if (GetFileAttributes(patchFile) != INVALID_FILE_ATTRIBUTES) {
|
||||||
|
CIniParser ini;
|
||||||
|
if (ini.LoadFile((LPCSTR)patchFile)) {
|
||||||
|
const CIniParser::TKeyVal* pSection = ini.GetSection("Strings");
|
||||||
|
if (pSection) {
|
||||||
|
for (const auto& kv : *pSection) {
|
||||||
|
m_strings[CString(kv.first.c_str())] = CString(kv.second.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TRACE("[Lang] Loaded zh_CN patch: %d strings\n", (int)m_strings.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
CString langFile = m_langDir + _T("\\") + langCode + _T(".ini");
|
// 1. 先从内嵌资源加载(英语和繁体中文)
|
||||||
|
bool hasBuiltin = LoadFromResource(langCode);
|
||||||
|
|
||||||
// 检查文件是否存在
|
// 2. 再从磁盘文件加载(覆盖内嵌翻译)
|
||||||
if (GetFileAttributes(langFile) == INVALID_FILE_ATTRIBUTES) {
|
CString langFile = m_langDir + _T("\\") + langCode + _T(".ini");
|
||||||
|
if (GetFileAttributes(langFile) != INVALID_FILE_ATTRIBUTES) {
|
||||||
|
CIniParser ini;
|
||||||
|
// 如果有内嵌资源,使用追加模式覆盖;否则使用普通加载
|
||||||
|
if (hasBuiltin) {
|
||||||
|
// 追加模式:磁盘文件中的翻译会覆盖内嵌翻译
|
||||||
|
if (ini.LoadFile((LPCSTR)langFile)) {
|
||||||
|
const CIniParser::TKeyVal* pSection = ini.GetSection("Strings");
|
||||||
|
if (pSection) {
|
||||||
|
for (const auto& kv : *pSection) {
|
||||||
|
m_strings[CString(kv.first.c_str())] = CString(kv.second.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TRACE("[Lang] Loaded disk file (override): %s\n", (LPCSTR)langFile);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 无内嵌资源,直接从磁盘加载
|
||||||
|
if (ini.LoadFile((LPCSTR)langFile)) {
|
||||||
|
const CIniParser::TKeyVal* pSection = ini.GetSection("Strings");
|
||||||
|
if (pSection) {
|
||||||
|
for (const auto& kv : *pSection) {
|
||||||
|
m_strings[CString(kv.first.c_str())] = CString(kv.second.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TRACE("[Lang] Loaded disk file: %s\n", (LPCSTR)langFile);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hasBuiltin || !m_strings.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从内嵌资源加载语言数据
|
||||||
|
bool LoadFromResource(const CString& langCode)
|
||||||
|
{
|
||||||
|
UINT resID = 0;
|
||||||
|
if (langCode == _T("en_US")) {
|
||||||
|
resID = IDR_LANG_EN_US;
|
||||||
|
} else if (langCode == _T("zh_TW")) {
|
||||||
|
resID = IDR_LANG_ZH_TW;
|
||||||
|
} else {
|
||||||
|
return false; // 无内嵌资源
|
||||||
|
}
|
||||||
|
|
||||||
|
HRSRC hRes = FindResource(NULL, MAKEINTRESOURCE(resID), RT_RCDATA);
|
||||||
|
if (!hRes) {
|
||||||
|
TRACE("[Lang] Resource not found: %d\n", resID);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用 CIniParser 解析,无文件大小限制,且不 trim key
|
HGLOBAL hData = LoadResource(NULL, hRes);
|
||||||
|
if (!hData) {
|
||||||
|
TRACE("[Lang] Failed to load resource: %d\n", resID);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* data = (const char*)LockResource(hData);
|
||||||
|
DWORD size = SizeofResource(NULL, hRes);
|
||||||
|
if (!data || size == 0) {
|
||||||
|
TRACE("[Lang] Empty resource: %d\n", resID);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用 CIniParser 从内存解析
|
||||||
CIniParser ini;
|
CIniParser ini;
|
||||||
if (!ini.LoadFile((LPCSTR)langFile)) {
|
if (!ini.LoadFromMemory(data, size)) {
|
||||||
|
TRACE("[Lang] Failed to parse resource: %d\n", resID);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,6 +292,8 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TRACE("[Lang] Loaded builtin resource: %s (%d strings)\n",
|
||||||
|
(LPCSTR)langCode, (int)m_strings.size());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -625,24 +725,24 @@ protected:
|
|||||||
AppendData(&dlgTemplate, sizeof(DLGTEMPLATE));
|
AppendData(&dlgTemplate, sizeof(DLGTEMPLATE));
|
||||||
AppendWord(0); // 菜单
|
AppendWord(0); // 菜单
|
||||||
AppendWord(0); // 窗口类
|
AppendWord(0); // 窗口类
|
||||||
AppendString(_T("选择语言 / Select Language"));
|
AppendString(_T("Select Language"));
|
||||||
AlignToDword();
|
AlignToDword();
|
||||||
|
|
||||||
// 静态文本
|
// 静态文本
|
||||||
AddControl(0x0082, 15, 15, 40, 12, (WORD)-1,
|
AddControl(0x0082, 15, 15, 50, 12, (WORD)-1,
|
||||||
SS_LEFT | WS_CHILD | WS_VISIBLE, _T("语言:"));
|
SS_LEFT | WS_CHILD | WS_VISIBLE, _T("Language:"));
|
||||||
|
|
||||||
// ComboBox
|
// ComboBox
|
||||||
AddControl(0x0085, 55, 13, 130, 150, 1001,
|
AddControl(0x0085, 65, 13, 120, 150, 1001,
|
||||||
CBS_DROPDOWNLIST | WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL, _T(""));
|
CBS_DROPDOWNLIST | WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL, _T(""));
|
||||||
|
|
||||||
// 确定按钮
|
// OK 按钮
|
||||||
AddControl(0x0080, 45, 50, 50, 14, IDOK,
|
AddControl(0x0080, 45, 50, 50, 14, IDOK,
|
||||||
BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, _T("确定"));
|
BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, _T("OK"));
|
||||||
|
|
||||||
// 取消按钮
|
// Cancel 按钮
|
||||||
AddControl(0x0080, 105, 50, 50, 14, IDCANCEL,
|
AddControl(0x0080, 105, 50, 50, 14, IDCANCEL,
|
||||||
BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, _T("取消"));
|
BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, _T("Cancel"));
|
||||||
|
|
||||||
return (LPCDLGTEMPLATE)m_templateBuffer.data();
|
return (LPCDLGTEMPLATE)m_templateBuffer.data();
|
||||||
}
|
}
|
||||||
@@ -703,8 +803,8 @@ protected:
|
|||||||
|
|
||||||
m_comboLang.SubclassDlgItem(1001, this);
|
m_comboLang.SubclassDlgItem(1001, this);
|
||||||
|
|
||||||
// 添加简体中文
|
// 添加简体中文(显示为英语避免乱码)
|
||||||
int idx = m_comboLang.AddString(_T("简体中文"));
|
int idx = m_comboLang.AddString(GetLanguageDisplayName(_T("zh_CN")));
|
||||||
m_langCodes.push_back(_T("zh_CN"));
|
m_langCodes.push_back(_T("zh_CN"));
|
||||||
m_comboLang.SetItemData(idx, 0);
|
m_comboLang.SetItemData(idx, 0);
|
||||||
|
|
||||||
|
|||||||
@@ -968,6 +968,11 @@
|
|||||||
#define IDC_STATIC_TRIGGER_TYPE 2544
|
#define IDC_STATIC_TRIGGER_TYPE 2544
|
||||||
#define IDC_STATIC_TRIGGER_ACTION 2545
|
#define IDC_STATIC_TRIGGER_ACTION 2545
|
||||||
|
|
||||||
|
// 内嵌语言资源 (RCDATA)
|
||||||
|
// 注意:避免与 IDB_BITMAP_TRIGGER(372) 和 IDB_BITMAP_WEBDESKTOP(373) 冲突
|
||||||
|
#define IDR_LANG_EN_US 380
|
||||||
|
#define IDR_LANG_ZH_TW 381
|
||||||
|
|
||||||
// Next default values for new objects
|
// Next default values for new objects
|
||||||
//
|
//
|
||||||
#ifdef APSTUDIO_INVOKED
|
#ifdef APSTUDIO_INVOKED
|
||||||
|
|||||||
Reference in New Issue
Block a user