@@ -33,18 +33,8 @@ namespace Lang {
3333 return true ;
3434 }
3535
36- bool Resource::Load ( )
36+ static void load_messages_from_json ( const Json &data, const std::string &filename, Resource::StringMap &strings )
3737 {
38- if (m_loaded)
39- return true ;
40-
41- std::string filename = " lang/" + m_name + " /" + m_langCode + " .json" ;
42- Json data = JsonUtils::LoadJsonDataFile (filename);
43- if (data.is_null ()) {
44- Log::Warning (" couldn't read language file '{}'\n " , filename.c_str ());
45- return false ;
46- }
47-
4838 for (Json::iterator i = data.begin (); i != data.end (); ++i) {
4939 const std::string token = i.key ();
5040 if (token.empty ()) {
@@ -97,7 +87,28 @@ namespace Lang {
9787 text = adjustedText;
9888 }
9989
100- m_strings[token] = text;
90+ strings[token] = text;
91+ }
92+ }
93+
94+ static bool load_messages_from_file (const std::string &filename, Resource::StringMap &strings)
95+ {
96+ Json data = JsonUtils::LoadJsonDataFile (filename);
97+ if (data.is_null ())
98+ return false ;
99+ load_messages_from_json (data, filename, strings);
100+ return true ;
101+ }
102+
103+ bool Resource::Load ()
104+ {
105+ if (m_loaded)
106+ return true ;
107+
108+ std::string filename = " lang/" + m_name + " /" + m_langCode + " .json" ;
109+ if (!load_messages_from_file (filename, m_strings)) {
110+ Log::Warning (" couldn't read language file '{}'\n " , filename.c_str ());
111+ return false ;
101112 }
102113
103114 m_loaded = true ;
@@ -232,4 +243,73 @@ namespace Lang {
232243 return m_cachedResources.at (key);
233244 }
234245
246+ static std::map<std::string, int > s_languageCompletion;
247+ static bool s_languageCompletionScanned = false ;
248+
249+ void ScanLanguageCompletion ()
250+ {
251+ if (s_languageCompletionScanned)
252+ return ;
253+ s_languageCompletionScanned = true ;
254+
255+ struct Stats {
256+ int translated = 0 ;
257+ int total = 0 ;
258+ };
259+ std::map<std::string, Stats> stats;
260+
261+ const int dirFlags = FileSystem::FileEnumerator::IncludeDirs | FileSystem::FileEnumerator::ExcludeFiles;
262+ for (FileSystem::FileEnumerator resources (FileSystem::gameDataFiles, " lang" , dirFlags); !resources.Finished (); resources.Next ()) {
263+ const std::string resourcePath = resources.Current ().GetPath ();
264+
265+ Resource::StringMap english;
266+ if (!load_messages_from_file (FileSystem::JoinPath (resourcePath, " en.json" ), english) || english.empty ())
267+ continue ;
268+
269+ for (FileSystem::FileEnumerator files (FileSystem::gameDataFiles, resourcePath); !files.Finished (); files.Next ()) {
270+ if (!files.Current ().IsFile ())
271+ continue ;
272+
273+ const std::string &name = files.Current ().GetName ();
274+ if (!ends_with_ci (name, " .json" ))
275+ continue ;
276+
277+ const std::string langCode = name.substr (0 , name.size () - 5 );
278+ if (langCode == " en" )
279+ continue ;
280+
281+ Resource::StringMap translated;
282+ load_messages_from_file (files.Current ().GetPath (), translated);
283+
284+ for (const auto &entry : english) {
285+ const std::string &token = entry.first ;
286+ const std::string &enText = entry.second ;
287+ stats[langCode].total ++;
288+ const auto it = translated.find (token);
289+ if (it != translated.end () && it->second != enText)
290+ stats[langCode].translated ++;
291+ }
292+ }
293+ }
294+
295+ for (const auto &entry : stats) {
296+ const Stats &s = entry.second ;
297+ s_languageCompletion[entry.first ] = s.total > 0 ? (s.translated * 100 ) / s.total : 0 ;
298+ }
299+ }
300+
301+ int GetLanguageCompletionPercent (std::string_view langCode)
302+ {
303+ if (langCode == " en" )
304+ return 100 ;
305+
306+ if (!s_languageCompletionScanned)
307+ ScanLanguageCompletion ();
308+
309+ const auto it = s_languageCompletion.find (std::string (langCode));
310+ if (it == s_languageCompletion.end ())
311+ return 0 ;
312+ return it->second ;
313+ }
314+
235315} // namespace Lang
0 commit comments