1- // Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org)
1+ // Copyright (C) 2005 - 2026 Settlers Freaks (sf-team at siedler25.org)
22//
33// SPDX-License-Identifier: GPL-2.0-or-later
44
55#define BOOST_TEST_MODULE RTTR_LanguageFiles
66
7+ #include " helpers/OptionalIO.h"
78#include " languageFiles.h"
89#include " mygettext/readCatalog.h"
910#include " s25util/utf8.h"
1011#include < boost/filesystem.hpp>
1112#include < boost/format.hpp>
1213#include < boost/test/unit_test.hpp>
1314#include < map>
15+ #include < optional>
1416
1517#if RTTR_HAS_VLD
1618# include < vld.h>
1719#endif
1820
21+ namespace {
1922struct FormatProperties
2023{
21- int numParameters;
24+ std::optional< int > numParameters;
2225};
2326
24- static std::map<std::string, FormatProperties> getGoldMapping ()
27+ FormatProperties getFormatProperties (const std::string& str)
28+ {
29+ try
30+ {
31+ const boost::format fmt (str);
32+ return FormatProperties{fmt.expected_args ()};
33+ } catch (const std::exception&)
34+ {
35+ return FormatProperties{std::nullopt };
36+ }
37+ }
38+
39+ std::map<std::string, FormatProperties> getGoldMapping ()
2540{
2641 const std::map<std::string, std::string> translations =
2742 mygettext::readCatalog (std::string (RTTR_TRANSLATION_DIR) + " /rttr-en_GB.mo" , " UTF-8" );
@@ -30,19 +45,12 @@ static std::map<std::string, FormatProperties> getGoldMapping()
3045 {
3146 if (entry.first .empty ())
3247 continue ;
33- try
34- {
35- const boost::format fmt (entry.first );
36- result.emplace (std::make_pair (entry.first , FormatProperties{fmt.expected_args ()}));
37- } catch (const std::exception&)
38- {
39- result.emplace (std::make_pair (entry.first , FormatProperties{0 }));
40- }
48+ result.emplace (entry.first , getFormatProperties (entry.first ));
4149 }
4250 return result;
4351}
4452
45- static std::string replaceLF (std::string s)
53+ std::string replaceLF (std::string s)
4654{
4755 size_t index = 0 ;
4856 while ((index = s.find (' \n ' )) != std::string::npos)
@@ -51,41 +59,38 @@ static std::string replaceLF(std::string s)
5159 }
5260 return s;
5361}
62+ } // namespace
5463
5564BOOST_AUTO_TEST_CASE (AllFilesHaveValidFormat)
5665{
5766 const auto goldMapping = getGoldMapping ();
58- for (const auto & it : boost::filesystem::directory_iterator (RTTR_TRANSLATION_DIR))
67+ for (const auto & itFile : boost::filesystem::directory_iterator (RTTR_TRANSLATION_DIR))
5968 {
60- if (!is_regular_file (it .status ()) || it .path ().extension () != " .mo" )
69+ if (!is_regular_file (itFile .status ()) || itFile .path ().extension () != " .mo" )
6170 continue ; // LCOV_EXCL_LINE
62- const auto translatedStrings = mygettext::readCatalog (it .path ().string (), " UTF-8" );
71+ const auto translatedStrings = mygettext::readCatalog (itFile .path ().string (), " UTF-8" );
6372
64- BOOST_TEST_CONTEXT (" Locale: " << it .path ().stem ())
65- for (const auto & entry : goldMapping )
73+ BOOST_TEST_CONTEXT (" Locale: " << itFile .path ().stem ())
74+ for (const auto & entry : translatedStrings )
6675 {
67- const auto it = translatedStrings.find (entry.first );
68- if (it == translatedStrings.end ())
69- continue ; // Not translated
70- BOOST_TEST_CONTEXT (" Entry '" << replaceLF (it->first ) << " ' => '" << replaceLF (it->second ) << " '" )
76+ const auto itGoldEntry = goldMapping.find (entry.first );
77+ const auto & [origTxt, translation] = entry;
78+ BOOST_TEST_CONTEXT (" Entry '" << replaceLF (origTxt) << " ' => '" << replaceLF (translation) << " '" )
7179 {
72- BOOST_TEST (s25util::isValidUTF8 (it-> second ));
80+ BOOST_TEST (s25util::isValidUTF8 (translation ));
7381 // Note: "Check 50%" is invalid (ends in %) but "50% checked" is not and translations might move the %
7482 // around Hence rely on the number of format args which should be consistent
75- try
76- {
77- const boost::format fmt (it->second );
78- BOOST_TEST (fmt.expected_args () == entry.second .numParameters );
79- } catch (const std::exception&)
83+ FormatProperties origProps = getFormatProperties (origTxt);
84+ FormatProperties transProps = getFormatProperties (translation);
85+ // Orig text replacements must match with gold version
86+ // Might not exist if orig text is not "translated" (already in English)
87+ if (itGoldEntry != goldMapping.end ())
88+ BOOST_TEST (origProps.numParameters == itGoldEntry->second .numParameters );
89+ if (origProps.numParameters .has_value () == transProps.numParameters .has_value ())
8090 {
81- // Should have been a format string
82- if (entry.second .numParameters > 0 )
83- {
84- // LCOV_EXCL_START
85- BOOST_TEST_ERROR (" Invalid format string" );
86- // LCOV_EXCL_STOP
87- }
88- }
91+ BOOST_TEST (origProps.numParameters == transProps.numParameters );
92+ } else if (origProps.numParameters > 0 )
93+ BOOST_TEST_ERROR (" Invalid format string in translation" ); // LCOV_EXCL_LINE
8994 }
9095 }
9196 }
0 commit comments