From 131accb5ac966e6a0da7c2927f61d1b9b1d97b61 Mon Sep 17 00:00:00 2001 From: mniip Date: Fri, 20 Mar 2020 07:02:16 +0300 Subject: [PATCH] Add i18n debug stuff --- SConscript | 7 ++- src/PowderToySDL.cpp | 10 +++++ src/common/Internationalization.cpp | 11 +++++ src/common/Internationalization.h | 70 ++++++++++++++++++++++++++++- 4 files changed, 95 insertions(+), 3 deletions(-) diff --git a/SConscript b/SConscript index 4409c18d3..43766fded 100644 --- a/SConscript +++ b/SConscript @@ -70,6 +70,7 @@ AddSconsOption('opengl', False, False, "Build with OpenGL interface support.") AddSconsOption('opengl-renderer', False, False, "Build with OpenGL renderer support (turns on --opengl).") #Note: this has nothing to do with --renderer, only tells the game to render particles with opengl AddSconsOption('renderer', False, False, "Build the save renderer.") AddSconsOption('font', False, False, "Build the font editor.") +AddSconsOption('i18n-debug', False, False, "Instrument the binary for debugging internationalization") AddSconsOption('wall', False, False, "Error on all warnings.") AddSconsOption('no-warnings', False, False, "Disable all compiler warnings.") @@ -404,7 +405,8 @@ elif not GetOption('help'): env = conf.Finish() if not msvc: - env.Append(CXXFLAGS=['-std=c++11', '-U__STRICT_ANSI__']) + env.Append(CXXFLAGS=['-std=c++11' if not GetOption('i18n-debug') else '-std=c++14']) + env.Append(CXXFLAGS=['-U__STRICT_ANSI__']) env.Append(CXXFLAGS=['-Wno-invalid-offsetof']) if platform == "Linux": env.Append(CXXFLAGS=['-Wno-unused-result']) @@ -519,6 +521,9 @@ if GetOption('renderer'): if GetOption('font'): env.Append(CPPDEFINES=['FONTEDITOR']) +if GetOption('i18n-debug'): + env.Append(CPPDEFINES=['I18N_DEBUG']) + if GetOption("wall"): if msvc: env.Append(CCFLAGS=['/WX']) diff --git a/src/PowderToySDL.cpp b/src/PowderToySDL.cpp index 4e7926bb2..e7ae7064c 100644 --- a/src/PowderToySDL.cpp +++ b/src/PowderToySDL.cpp @@ -673,6 +673,16 @@ int main(int argc, char * argv[]) else ChdirToDataDirectory(); +#ifdef I18N_DEBUG + for(auto const &key : i18n::activeKeys()) + { + std::cout << "{"; + for(auto const &s : key) + std::cout << "\"" << s << "\","; + std::cout << "}" << std::endl; + } +#endif // I18N_DEBUG + String localeName = Client::Ref().GetPrefString("Locale", ""); for(Locale *locale : locales) if(locale->GetName() == localeName) diff --git a/src/common/Internationalization.cpp b/src/common/Internationalization.cpp index ed7860174..70538c7e9 100644 --- a/src/common/Internationalization.cpp +++ b/src/common/Internationalization.cpp @@ -28,4 +28,15 @@ namespace i18n else return it->second; } + +#ifdef I18N_DEBUG + + std::set > &activeKeys() + { + static std::set > activeKeys; + return activeKeys; + } + +#endif // I18N_DEBUG + } diff --git a/src/common/Internationalization.h b/src/common/Internationalization.h index 77e364f7b..9f92745f4 100644 --- a/src/common/Internationalization.h +++ b/src/common/Internationalization.h @@ -2,6 +2,8 @@ #include #include +#include +#include #include "String.h" @@ -31,7 +33,7 @@ Sometimes several pieces of strings are assembled into a larger string, possibly with data inbetween, e.g.: String::Build("Page ", page, " of ", total) - In the C world we could've gotten away with using a key with placeholders + In the C world we could've gotten away with using a key with placeholders such as "Page %d of %d" and then the translation would include the format specifiers as well and it would all work out. Note that it is a bad idea to introduce "Page" and "of" as separate keys as they don't make much sense on @@ -62,6 +64,29 @@ namespace i18n CanonicalPtr Canonicalize(LiteralPtr str); +#ifdef I18N_DEBUG + std::set > &activeKeys(); + + template struct Chars + { + static char const chars[]; + }; + template char const Chars::chars[] = {cs..., 0}; + + template struct KeyUsage + { + KeyUsage() { activeKeys().insert({ByteString({cs..., 0})}); } + }; + + template struct MultiKeyUsage + { + MultiKeyUsage() { activeKeys().insert({Ts::chars...}); } + }; + + template KeyUsage keyUsage; + template MultiKeyUsage multiKeyUsage; +#endif + template struct TranslationMap { // This is not just a static field so that it is possible to use it @@ -77,7 +102,7 @@ namespace i18n { return TranslationMap<1>::Map()[{Canonicalize(str)}][0]; } - + template inline std::array &multiTranslation(std::array &cans, size_t) { return TranslationMap::Map()[cans]; @@ -121,6 +146,8 @@ namespace i18n } } +#ifndef I18N_DEBUG + inline String operator""_i18n(char const *str, size_t) { return i18n::getTranslation<1>({str})[0]; @@ -131,3 +158,42 @@ template std::array i18nMulti(Ts&&... arg std::array strs; return i18n::getMultiTranslation(strs, 0, std::forward(args)...); } + +#else // I18N_DEBUG + +template inline i18n::Chars operator""_chars() +{ + return {}; +} + +template inline String operator""_i18n() +{ + (void)i18n::keyUsage; + return i18n::getTranslation<1>({i18n::Chars::chars})[0]; +} + +// Macros are hard +// Expand i18nMulti("a", "b") into i18nMultiImpl() +#define NARG_SELECT(_1, _2, _3, _4, _5, _6, _7, _8, _9, N, ...) N +#define NARG(...) NARG_SELECT(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define i18nMultiNargH(N, ...) i18nMultiNarg(N, __VA_ARGS__) +#define i18nMultiNarg(N, ...) i18nMulti_##N(__VA_ARGS__) +#define i18nMulti(...) i18nMultiNargH(NARG(__VA_ARGS__), __VA_ARGS__) +#define i18nMulti_1(s1) i18nMultiImpl() +#define i18nMulti_2(s1, s2) i18nMultiImpl() +#define i18nMulti_3(s1, s2, s3) i18nMultiImpl() +#define i18nMulti_4(s1, s2, s3, s4) i18nMultiImpl() +#define i18nMulti_5(s1, s2, s3, s4, s5) i18nMultiImpl() +#define i18nMulti_6(s1, s2, s3, s4, s5, s6) i18nMultiImpl() +#define i18nMulti_7(s1, s2, s3, s4, s5, s6, s7) i18nMultiImpl() +#define i18nMulti_8(s1, s2, s3, s4, s5, s6, s7, s8) i18nMultiImpl() +#define i18nMulti_9(s1, s2, s3, s4, s5, s6, s7, s8, s9) i18nMultiImpl() + +template std::array i18nMultiImpl() +{ + (void)i18n::multiKeyUsage; + std::array strs; + return i18n::getMultiTranslation(strs, 0, Ts::chars...); +} + +#endif // I18N_DEBUG