29#include <boost/test/unit_test.hpp>
32#include <wx/filename.h>
33#include <wx/textfile.h>
49 std::string m_msgIdPlural;
50 std::vector<std::string> m_msgStrs;
57std::string unquote(
const std::string& aLine )
62 for(
size_t i = 0; i < aLine.size(); ++i )
74 if( ch ==
'\\' && i + 1 < aLine.size() )
90std::vector<PO_ENTRY> parsePo(
const wxString& aPath )
92 std::vector<PO_ENTRY> entries;
95 if( !file.Open( aPath ) )
100 bool pendingFuzzy =
false;
104 auto starts = [](
const std::string& aStr,
const char* aPrefix )
106 return aStr.rfind( aPrefix, 0 ) == 0;
111 for( wxString wxLine = file.GetFirstLine(); !file.Eof(); wxLine = file.GetNextLine() )
114 std::string line = wxLine.Trim(
false ).Trim(
true ).utf8_string();
116 if( starts( line,
"#," ) )
119 if( line.find(
"fuzzy" ) != std::string::npos )
122 else if( starts( line,
"msgid_plural" ) )
125 cur.m_msgIdPlural = unquote( line );
129 else if( starts( line,
"msgid " ) )
132 entries.push_back( cur );
135 cur.m_msgId = unquote( line );
137 cur.m_fuzzy = pendingFuzzy;
138 pendingFuzzy =
false;
142 else if( starts( line,
"msgstr[" ) )
146 int idx = std::atoi( line.c_str() +
sizeof(
"msgstr[" ) - 1 );
149 if( idx >= 0 && idx < 64 )
151 strIdx =
static_cast<size_t>( idx );
153 if( cur.m_msgStrs.size() <= strIdx )
154 cur.m_msgStrs.resize( strIdx + 1 );
156 cur.m_msgStrs[strIdx] = unquote( line );
162 else if( starts( line,
"msgstr " ) )
166 cur.m_msgStrs.assign( 1, unquote( line ) );
172 else if( !line.empty() && line[0] ==
'"' )
174 if( haveCur && state ==
ID )
175 cur.m_msgId += unquote( line );
176 else if( haveCur && state == IDP )
177 cur.m_msgIdPlural += unquote( line );
178 else if( haveCur && state == STR && strIdx < cur.m_msgStrs.size() )
179 cur.m_msgStrs[strIdx] += unquote( line );
181 else if( line.empty() )
192 entries.push_back( cur );
203std::map<char, int> consumingSpecs(
const std::string& aStr )
205 static const std::string flags =
"-+#0";
206 static const std::string convs =
"diouxXeEfFgGaAcsSCp";
208 std::map<char, int> counts;
209 std::map<char, std::set<int>> posArgs;
211 for(
size_t i = 0; i < aStr.size(); ++i )
218 if( j < aStr.size() && aStr[j] ==
'%' )
228 while( k < aStr.size() && std::isdigit(
static_cast<unsigned char>( aStr[k] ) ) )
231 if( k < aStr.size() && aStr[k] ==
'$' && k > j )
233 position = std::atoi( aStr.c_str() + j );
237 while( j < aStr.size() && flags.find( aStr[j] ) != std::string::npos )
241 if( j < aStr.size() && aStr[j] ==
'*' )
248 while( j < aStr.size() && std::isdigit(
static_cast<unsigned char>( aStr[j] ) ) )
252 if( j < aStr.size() && aStr[j] ==
'.' )
257 if( j < aStr.size() && aStr[j] ==
'*' )
264 while( j < aStr.size() && std::isdigit(
static_cast<unsigned char>( aStr[j] ) ) )
269 static const std::string lengths =
"lhzjtL";
271 while( j < aStr.size() && lengths.find( aStr[j] ) != std::string::npos )
274 if( j < aStr.size() && convs.find( aStr[j] ) != std::string::npos )
277 posArgs[aStr[j]].insert( position );
285 for(
const auto& [conv, positions] : posArgs )
286 counts[conv] +=
static_cast<int>( positions.size() );
294 wxFileName dir = wxFileName::DirName( wxString::FromUTF8( QA_SRC_ROOT ) );
295 dir.AppendDir( wxS(
"translation" ) );
296 dir.AppendDir( wxS(
"pofiles" ) );
311 wxString poPath = poDir().GetFullPath();
313 BOOST_REQUIRE_MESSAGE( wxDir::Exists( poPath ),
314 "Translation pofiles directory not found: " << poPath.utf8_string() );
317 wxDir::GetAllFiles( poPath, &files, wxS(
"*.po" ), wxDIR_FILES );
319 BOOST_REQUIRE_MESSAGE( !files.empty(),
"No .po files found in " << poPath.utf8_string() );
323 for(
const wxString& po : files )
325 wxFileName poFile( po );
327 for(
const PO_ENTRY& entry : parsePo( po ) )
333 std::map<char, int> src = consumingSpecs( entry.m_msgId );
335 for(
const auto& [conv, count] : consumingSpecs( entry.m_msgIdPlural ) )
336 src[conv] = std::max( src[conv], count );
343 for(
const std::string& msgStr : entry.m_msgStrs )
348 std::map<char, int> dst = consumingSpecs( msgStr );
350 for(
const auto& [conv, count] : dst )
352 if( count > src[conv] )
355 BOOST_ERROR( poFile.GetFullName().utf8_string()
356 <<
":" << entry.m_line <<
" translation adds extra '%" << conv
357 <<
"' specifier (msgid=\"" << entry.m_msgId <<
"\" msgstr=\""
358 << msgStr <<
"\")" );
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_AUTO_TEST_SUITE_END()
BOOST_CHECK_EQUAL(result, "25.4")