-
Notifications
You must be signed in to change notification settings - Fork 1
/
Translator.cpp
174 lines (158 loc) · 4.77 KB
/
Translator.cpp
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
#include "stdafx.h"
#include "resource.h"
#include "Translator.h"
#include "Globals.h"
// TODO: Implement as a singleton.
Translator GlobalTranslator;
Translator::Translator()
{
m_TranslationMap.clear();
}
Translator::~Translator()
{
}
bool Translator::ParseLanguageData(const WCHAR* LngData, size_t LngDataSz)
{
// The parser is implemented as Finite State Machine.
// Skip the BOM signature if it is present.
size_t pos = ((LngData[0] == 0xfeff) ? 1 : 0);
// Available states of the FSM.
enum ParserFSM
{
PARSER_NEWLINE,
PARSER_COMMENT,
PARSER_NUMBER,
PARSER_QUOTE,
PARSER_STRING
};
ParserFSM mode = PARSER_NEWLINE; // The current FSM state.
DWORD code = 0;
size_t str_begin = 0;
while (pos < LngDataSz)
{
switch (mode)
{
case PARSER_NEWLINE:
// New line starts: expect a number, if not found skip the line as comment.
if ((LngData[pos] >= L'0') && (LngData[pos] <= L'9'))
{
mode = PARSER_NUMBER;
code = LngData[pos] - L'0';
}
else
mode = PARSER_COMMENT;
break;
case PARSER_COMMENT:
// Comment lasts till the end of line.
if (LngData[pos] == L'\x0a')
mode = PARSER_NEWLINE;
break;
case PARSER_NUMBER:
// Number either continues with digits or meets the equal sign after which the translation string begins.
if ((LngData[pos] >= L'0') && (LngData[pos] <= L'9'))
code = code * 10 + (LngData[pos] - L'0');
else if (LngData[pos] == L'=')
mode = PARSER_QUOTE;
else
return false;
break;
case PARSER_QUOTE:
// Translation string must start with double-quote or can be empty.
if (LngData[pos] == L'"')
{
str_begin = pos + 1;
mode = PARSER_STRING;
}
else if (LngData[pos] == L'\x0a')
mode = PARSER_NEWLINE;
break;
case PARSER_STRING:
if (LngData[pos] == L'"')
{
// Found end of translation string (closing double-quote), everything else up to the end of line is a comment.
// Finalize the string: replace "\n" with real end-of-lines.
// TODO: Support for embedded double-quote characters inside the string by escaping them.
wstring str;
str.assign(LngData + str_begin, pos - str_begin);
for (size_t i = 0; i < str.size(); ++i)
{
if ((str[i] == L'\\') && (str[i + 1] == L'n'))
{
str[i] = L'\x0d';
str[i + 1] = L'\x0a';
}
}
m_TranslationMap[code] = str;
mode = PARSER_COMMENT;
}
break;
default:
return false;
}
++pos;
}
return true;
}
bool Translator::ReadLanguageFile(const WCHAR* FileName)
{
WCHAR* LngData = NULL;
// First, parse the embedded language file from resources to fill all the IDs with default language.
HRSRC hres = FindResource(PluginInstance, MAKEINTRESOURCE(IDR_DEFAULT_LANGUAGE), RT_RCDATA);
if (hres == NULL)
return false;
HGLOBAL hg = LoadResource(PluginInstance, hres);
if (hg == NULL)
return false;
LngData = (WCHAR*)LockResource(hg);
if (LngData == NULL)
return false;
if (!ParseLanguageData(LngData, SizeofResource(PluginInstance, hres) / sizeof(WCHAR)))
return false;
// If external LNG file is not specified, internal English translation will remain.
if ((FileName == NULL) || (FileName[0] == L'\0'))
return true;
// Otherwise read and parse the language file replacing the English lines with translated ones.
HANDLE LngFile = CreateFile((wstring(PluginDirectory) + L"\\Language\\" + FileName).c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if (LngFile == INVALID_HANDLE_VALUE)
return false;
// Read file contents into memory.
LARGE_INTEGER FileSz;
if (!GetFileSizeEx(LngFile, &FileSz))
{
CloseHandle(LngFile);
return false;
}
size_t LngFileDataSz = (size_t)(FileSz.QuadPart / sizeof(WCHAR));
LngData = new WCHAR[LngFileDataSz];
DWORD dr;
bool res = (ReadFile(LngFile, LngData, (DWORD)FileSz.QuadPart, &dr, NULL) && (dr == FileSz.QuadPart));
CloseHandle(LngFile);
if (!res)
return false;
// Parse the file contents.
if (!ParseLanguageData(LngData, LngFileDataSz))
return false;
return true;
}
const wstring& Translator::GetLine(DWORD Id) const
{
// Search for the line with ID requested and return it or (if not found) special fixed string.
static const wstring Untranslated = L"<UNTRANSLATED>";
MapIdToString::const_iterator LineIter = m_TranslationMap.find(Id);
return ((LineIter == m_TranslationMap.end()) ? Untranslated : LineIter->second);
}
void Translator::TranslateDialog(HWND hwndDlg, const DWORD* LangIDs)
{
// The LangIDs array contains pairs (DialogControlID, TranslationLineID).
// DialogControlID == -1 means the dialog title.
// DialogControlID == 0 means the end of the array.
size_t i = 0;
while (LangIDs[i] != 0)
{
if (LangIDs[i] == (DWORD)-1)
SetWindowText(hwndDlg, GetLine(LangIDs[i + 1]).c_str());
else
SetDlgItemText(hwndDlg, LangIDs[i], GetLine(LangIDs[i + 1]).c_str());
i += 2;
}
}