1
// TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2012-2013 - TortoiseGit
4 // Copyright (C) 2003-2008, 2011-2016 - TortoiseSVN
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include "ResModule.h"
32 #define MYERROR {CUtils::Error(); return FALSE;}
36 , m_bAdjustEOLs(false)
40 CPOFile::~CPOFile(void)
44 static bool StartsWith(const wchar_t* heystacl
, const wchar_t* needle
)
46 return wcsncmp(heystacl
, needle
, wcslen(needle
)) == 0;
49 BOOL
CPOFile::ParseFile(LPCTSTR szPath
, BOOL bUpdateExisting
, bool bAdjustEOLs
)
51 if (!PathFileExists(szPath
))
54 m_bAdjustEOLs
= bAdjustEOLs
;
57 _ftprintf(stdout
, L
"parsing file %s...\n", szPath
);
62 //since stream classes still expect the filepath in char and not wchar_t
63 //we need to convert the filepath to multibyte
64 char filepath
[MAX_PATH
+ 1] = { 0 };
65 WideCharToMultiByte(CP_ACP
, 0, szPath
, -1, filepath
, _countof(filepath
) - 1, nullptr, nullptr);
68 File
.imbue(std::locale(std::locale(), new utf8_conversion()));
72 _ftprintf(stderr
, L
"can't open input file %s\n", szPath
);
75 auto line
= std::make_unique
<TCHAR
[]>(2 * MAX_STRING_LENGTH
);
76 std::vector
<std::wstring
> entry
;
79 File
.getline(line
.get(), 2*MAX_STRING_LENGTH
);
82 //empty line means end of entry!
83 RESOURCEENTRY resEntry
= {0};
85 std::wstring regexsearch
, regexreplace
;
87 for (auto I
= entry
.cbegin(); I
!= entry
.cend(); ++I
)
89 if (StartsWith(I
->c_str(), L
"# "))
92 if (StartsWith(I
->c_str(), L
"# regexsearch="))
93 regexsearch
= I
->substr(14);
94 else if (StartsWith(I
->c_str(), L
"# regexreplace="))
95 regexreplace
= I
->substr(15);
97 resEntry
.translatorcomments
.push_back(I
->c_str());
98 if (!regexsearch
.empty() && !regexreplace
.empty())
100 m_regexes
.push_back(std::make_tuple(regexsearch
, regexreplace
));
102 regexreplace
.clear();
106 if (StartsWith(I
->c_str(), L
"#."))
109 resEntry
.automaticcomments
.push_back(I
->c_str());
112 if (StartsWith(I
->c_str(), L
"#,"))
115 resEntry
.flag
= I
->c_str();
118 if (StartsWith(I
->c_str(), L
"msgid"))
122 msgid
= std::wstring(msgid
.substr(7, msgid
.size() - 8));
124 std::wstring s
= msgid
;
125 s
.erase(s
.cbegin(), std::find_if(s
.cbegin(), s
.cend(), [](const auto& c
) { return !iswspace(c
); }));
130 if (StartsWith(I
->c_str(), L
"msgstr"))
133 resEntry
.msgstr
= I
->c_str();
134 resEntry
.msgstr
= resEntry
.msgstr
.substr(8, resEntry
.msgstr
.length() - 9);
135 if (!resEntry
.msgstr
.empty())
139 if (StartsWith(I
->c_str(), L
"\""))
143 std::wstring temp
= I
->c_str();
144 temp
= temp
.substr(1, temp
.length()-2);
149 if (resEntry
.msgstr
.empty())
151 std::wstring temp
= I
->c_str();
152 temp
= temp
.substr(1, temp
.length()-2);
153 resEntry
.msgstr
+= temp
;
158 if ((bUpdateExisting
)&&(this->count(msgid
) == 0))
162 if ((m_bAdjustEOLs
)&&(msgid
.find(L
"\\r\\n") != std::string::npos
))
164 AdjustEOLs(resEntry
.msgstr
);
166 // always use the new data for generated comments/flags
167 auto newEntry
= (*this)[msgid
];
168 resEntry
.automaticcomments
= newEntry
.automaticcomments
;
169 resEntry
.flag
= newEntry
.flag
;
170 resEntry
.resourceIDs
= newEntry
.resourceIDs
;
172 (*this)[msgid
] = resEntry
;
178 entry
.push_back(line
.get());
180 } while (File
.gcount() > 0);
181 printf("%s", File
.getloc().name().c_str());
183 RESOURCEENTRY emptyentry
= {0};
184 (*this)[std::wstring(L
"")] = emptyentry
;
186 _ftprintf(stdout
, L
"%d Entries found, %d were already translated and %d got deleted\n", nEntries
, nTranslated
, nDeleted
);
190 BOOL
CPOFile::SaveFile(LPCTSTR szPath
, LPCTSTR lpszHeaderFile
)
192 //since stream classes still expect the filepath in char and not wchar_t
193 //we need to convert the filepath to multibyte
194 char filepath
[MAX_PATH
+ 1] = { 0 };
196 WideCharToMultiByte(CP_ACP
, 0, szPath
, -1, filepath
, _countof(filepath
) - 1, nullptr, nullptr);
199 File
.imbue(std::locale(std::locale(), new utf8_conversion()));
200 File
.open(filepath
, std::ios_base::binary
);
202 if ((lpszHeaderFile
)&&(lpszHeaderFile
[0])&&(PathFileExists(lpszHeaderFile
)))
204 // read the header file and save it to the top of the pot file
205 std::wifstream inFile
;
206 inFile
.imbue(std::locale(std::locale(), new utf8_conversion()));
207 inFile
.open(lpszHeaderFile
, std::ios_base::binary
);
210 while(inFile
&& inFile
.get(ch
))
216 File
<< L
"# SOME DESCRIPTIVE TITLE.\n";
217 File
<< L
"# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n";
218 File
<< L
"# This file is distributed under the same license as the PACKAGE package.\n";
219 File
<< L
"# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.\n";
221 File
<< L
"#, fuzzy\n";
222 File
<< L
"msgid \"\"\n";
223 File
<< L
"msgstr \"\"\n";
224 File
<< L
"\"Project-Id-Version: PACKAGE VERSION\\n\"\n";
225 File
<< L
"\"Report-Msgid-Bugs-To: \\n\"\n";
226 File
<< L
"\"POT-Creation-Date: 1900-01-01 00:00+0000\\n\"\n";
227 File
<< L
"\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n";
228 File
<< L
"\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n";
229 File
<< L
"\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n";
230 File
<< L
"\"MIME-Version: 1.0\\n\"\n";
231 File
<< L
"\"Content-Type: text/plain; charset=UTF-8\\n\"\n";
232 File
<< L
"\"Content-Transfer-Encoding: 8bit\\n\"\n\n";
235 File
<< L
"# msgid/msgstr fields for Accelerator keys\n";
236 File
<< L
"# Format is: \"ID:xxxxxx:VACS+X\" where:\n";
237 File
<< L
"# ID:xxxxx = the menu ID corresponding to the accelerator\n";
238 File
<< L
"# V = Virtual key (or blank if not used) - nearly always set!\n";
239 File
<< L
"# A = Alt key (or blank if not used)\n";
240 File
<< L
"# C = Ctrl key (or blank if not used)\n";
241 File
<< L
"# S = Shift key (or blank if not used)\n";
242 File
<< L
"# X = upper case character\n";
243 File
<< L
"# e.g. \"V CS+Q\" == Ctrl + Shift + 'Q'\n";
245 File
<< L
"# ONLY Accelerator Keys with corresponding alphanumeric characters can be\n";
246 File
<< L
"# updated i.e. function keys (F2), special keys (Delete, HoMe) etc. will not.\n";
248 File
<< L
"# ONLY change the msgstr field. Do NOT change any other.\n";
249 File
<< L
"# If you do not want to change an Accelerator Key, copy msgid to msgstr\n";
252 for (auto I
= this->cbegin(); I
!= this->cend(); ++I
)
254 std::wstring s
= I
->first
;
255 s
.erase(s
.cbegin(), std::find_if(s
.cbegin(), s
.cend(), [](const auto& c
) { return !iswspace(c
); }));
259 RESOURCEENTRY entry
= I
->second
;
260 for (auto II
= entry
.automaticcomments
.cbegin(); II
!= entry
.automaticcomments
.cend(); ++II
)
262 File
<< II
->c_str() << L
"\n";
264 for (auto II
= entry
.translatorcomments
.cbegin(); II
!= entry
.translatorcomments
.cend(); ++II
)
266 File
<< II
->c_str() << L
"\n";
268 if (!I
->second
.resourceIDs
.empty())
270 File
<< L
"#. Resource IDs: (";
272 auto II
= I
->second
.resourceIDs
.begin();
275 while (II
!= I
->second
.resourceIDs
.end())
283 if (I
->second
.flag
.length() > 0)
284 File
<< (I
->second
.flag
.c_str()) << L
"\n";
285 File
<< (L
"msgid \"") << (I
->first
.c_str()) << L
"\"\n";
286 File
<< (L
"msgstr \"") << (I
->second
.msgstr
.c_str()) << L
"\"\n\n";
291 _ftprintf(stdout
, L
"File %s saved, containing %d entries\n", szPath
, nEntries
);
295 void CPOFile::AdjustEOLs(std::wstring
& str
)
298 std::wstring::size_type pos
= 0;
299 for ( ; ; ) // while (true)
301 std::wstring::size_type next
= str
.find(L
"\\r\\n", pos
);
302 result
.append(str
, pos
, next
-pos
);
303 if( next
!= std::string::npos
)
305 result
.append(L
"\\n");
306 pos
= next
+ 4; // 4 = sizeof("\\r\\n")
317 for ( ; ; ) // while (true)
319 std::wstring::size_type next
= str
.find(L
"\\n", pos
);
320 result
.append(str
, pos
, next
-pos
);
321 if( next
!= std::string::npos
)
323 result
.append(L
"\\r\\n");
324 pos
= next
+ 2; // 2 = sizeof("\\n")