Merge pull request #24470 from fuzzard/release_20.3
[xbmc.git] / xbmc / LangInfo.cpp
blobdb85aa555e6edceb82c42d9829cfec29ee8d1a4f
1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "LangInfo.h"
11 #include "ServiceBroker.h"
12 #include "XBDateTime.h"
13 #include "addons/AddonInstaller.h"
14 #include "addons/AddonManager.h"
15 #include "addons/LanguageResource.h"
16 #include "addons/RepositoryUpdater.h"
17 #include "addons/addoninfo/AddonType.h"
18 #include "guilib/LocalizeStrings.h"
19 #include "messaging/ApplicationMessenger.h"
20 #include "pvr/PVRManager.h"
21 #include "settings/AdvancedSettings.h"
22 #include "settings/Settings.h"
23 #include "settings/SettingsComponent.h"
24 #include "settings/lib/Setting.h"
25 #include "settings/lib/SettingDefinitions.h"
26 #include "utils/CharsetConverter.h"
27 #include "utils/LangCodeExpander.h"
28 #include "utils/StringUtils.h"
29 #include "utils/URIUtils.h"
30 #include "utils/XBMCTinyXML.h"
31 #include "utils/XMLUtils.h"
32 #include "utils/log.h"
33 #include "weather/WeatherManager.h"
35 #include <algorithm>
36 #include <stdexcept>
38 using namespace PVR;
40 static std::string shortDateFormats[] = {
41 // short date formats using "/"
42 "DD/MM/YYYY",
43 "MM/DD/YYYY",
44 "YYYY/MM/DD",
45 "D/M/YYYY",
46 // short date formats using "-"
47 "DD-MM-YYYY",
48 "MM-DD-YYYY",
49 "YYYY-MM-DD",
50 "YYYY-M-D",
51 // short date formats using "."
52 "DD.MM.YYYY",
53 "DD.M.YYYY",
54 "D.M.YYYY",
55 "D. M. YYYY",
56 "YYYY.MM.DD"
59 static std::string longDateFormats[] = {
60 "DDDD, D MMMM YYYY",
61 "DDDD, DD MMMM YYYY",
62 "DDDD, D. MMMM YYYY",
63 "DDDD, DD. MMMM YYYY",
64 "DDDD, MMMM D, YYYY",
65 "DDDD, MMMM DD, YYYY",
66 "DDDD D MMMM YYYY",
67 "DDDD DD MMMM YYYY",
68 "DDDD D. MMMM YYYY",
69 "DDDD DD. MMMM YYYY",
70 "D. MMMM YYYY",
71 "DD. MMMM YYYY",
72 "D. MMMM. YYYY",
73 "DD. MMMM. YYYY",
74 "YYYY. MMMM. D"
77 #define TIME_FORMAT_MM_SS ":mm:ss"
78 #define TIME_FORMAT_SINGLE_12 "h" TIME_FORMAT_MM_SS
79 #define TIME_FORMAT_DOUBLE_12 "hh" TIME_FORMAT_MM_SS
80 #define TIME_FORMAT_SINGLE_24 "H" TIME_FORMAT_MM_SS
81 #define TIME_FORMAT_DOUBLE_24 "HH" TIME_FORMAT_MM_SS
83 #define TIME_FORMAT_12HOURS "12hours"
84 #define TIME_FORMAT_24HOURS "24hours"
86 typedef struct TemperatureInfo {
87 CTemperature::Unit unit;
88 std::string name;
89 } TemperatureInfo;
91 static TemperatureInfo temperatureInfo[] = {
92 { CTemperature::UnitFahrenheit, "f" },
93 { CTemperature::UnitKelvin, "k" },
94 { CTemperature::UnitCelsius, "c" },
95 { CTemperature::UnitReaumur, "re" },
96 { CTemperature::UnitRankine, "ra" },
97 { CTemperature::UnitRomer, "ro" },
98 { CTemperature::UnitDelisle, "de" },
99 { CTemperature::UnitNewton, "n" }
102 #define TEMP_UNIT_STRINGS 20027
104 typedef struct SpeedInfo {
105 CSpeed::Unit unit;
106 std::string name;
107 } SpeedInfo;
109 static SpeedInfo speedInfo[] = {
110 { CSpeed::UnitKilometresPerHour, "kmh" },
111 { CSpeed::UnitMetresPerMinute, "mpmin" },
112 { CSpeed::UnitMetresPerSecond, "mps" },
113 { CSpeed::UnitFeetPerHour, "fth" },
114 { CSpeed::UnitFeetPerMinute, "ftm" },
115 { CSpeed::UnitFeetPerSecond, "fts" },
116 { CSpeed::UnitMilesPerHour, "mph" },
117 { CSpeed::UnitKnots, "kts" },
118 { CSpeed::UnitBeaufort, "beaufort" },
119 { CSpeed::UnitInchPerSecond, "inchs" },
120 { CSpeed::UnitYardPerSecond, "yards" },
121 { CSpeed::UnitFurlongPerFortnight, "fpf" }
124 #define SPEED_UNIT_STRINGS 20200
126 #define SETTING_REGIONAL_DEFAULT "regional"
128 static std::string ToTimeFormat(bool use24HourClock, bool singleHour, bool meridiem)
130 if (use24HourClock)
131 return singleHour ? TIME_FORMAT_SINGLE_24 : TIME_FORMAT_DOUBLE_24;
133 if (!meridiem)
134 return singleHour ? TIME_FORMAT_SINGLE_12 : TIME_FORMAT_DOUBLE_12;
136 return StringUtils::Format(g_localizeStrings.Get(12382), ToTimeFormat(false, singleHour, false));
139 static std::string ToSettingTimeFormat(const CDateTime& time, const std::string& timeFormat)
141 return StringUtils::Format(g_localizeStrings.Get(20036),
142 time.GetAsLocalizedTime(timeFormat, true), timeFormat);
145 static CTemperature::Unit StringToTemperatureUnit(const std::string& temperatureUnit)
147 std::string unit(temperatureUnit);
148 StringUtils::ToLower(unit);
150 for (const TemperatureInfo& info : temperatureInfo)
152 if (info.name == unit)
153 return info.unit;
156 return CTemperature::UnitCelsius;
159 static CSpeed::Unit StringToSpeedUnit(const std::string& speedUnit)
161 std::string unit(speedUnit);
162 StringUtils::ToLower(unit);
164 for (const SpeedInfo& info : speedInfo)
166 if (info.name == unit)
167 return info.unit;
170 return CSpeed::UnitKilometresPerHour;
173 struct SortLanguage
175 bool operator()(const StringSettingOption &left, const StringSettingOption &right) const
177 std::string strLeft = left.label;
178 std::string strRight = right.label;
179 StringUtils::ToLower(strLeft);
180 StringUtils::ToLower(strRight);
182 return strLeft.compare(strRight) < 0;
186 CLangInfo::CRegion::CRegion()
188 SetDefaults();
191 void CLangInfo::CRegion::SetDefaults()
193 m_strName="N/A";
194 m_strLangLocaleName = "English";
195 m_strLangLocaleCodeTwoChar = "en";
197 m_strDateFormatShort="DD/MM/YYYY";
198 m_strDateFormatLong="DDDD, D MMMM YYYY";
199 m_strTimeFormat="HH:mm:ss";
200 m_tempUnit = CTemperature::UnitCelsius;
201 m_speedUnit = CSpeed::UnitKilometresPerHour;
202 m_strTimeZone.clear();
205 void CLangInfo::CRegion::SetTemperatureUnit(const std::string& strUnit)
207 m_tempUnit = StringToTemperatureUnit(strUnit);
210 void CLangInfo::CRegion::SetSpeedUnit(const std::string& strUnit)
212 m_speedUnit = StringToSpeedUnit(strUnit);
215 void CLangInfo::CRegion::SetTimeZone(const std::string& strTimeZone)
217 m_strTimeZone = strTimeZone;
220 void CLangInfo::CRegion::SetGlobalLocale()
222 std::string strLocale;
223 if (m_strRegionLocaleName.length() > 0)
225 #ifdef TARGET_WINDOWS
226 std::string strLang, strRegion;
227 g_LangCodeExpander.ConvertToISO6391(m_strLangLocaleName, strLang);
228 g_LangCodeExpander.ConvertToISO6391(m_strRegionLocaleName, strRegion);
229 strLocale = strLang + "-" + strRegion;
230 #else
231 strLocale = m_strLangLocaleName + "_" + m_strRegionLocaleName;
232 #endif
233 #ifdef TARGET_POSIX
234 strLocale += ".UTF-8";
235 #endif
237 g_langInfo.m_originalLocale = std::locale(std::locale::classic(), new custom_numpunct(m_cDecimalSep, m_cThousandsSep, m_strGrouping));
239 CLog::Log(LOGDEBUG, "trying to set locale to {}", strLocale);
241 // We need to set the locale to only change the collate. Otherwise,
242 // decimal separator is changed depending of the current language
243 // (ie. "," in French or Dutch instead of "."). This breaks atof() and
244 // others similar functions.
245 #if !(defined(TARGET_FREEBSD) || defined(TARGET_DARWIN_OSX) || defined(__UCLIBC__))
246 // on FreeBSD, darwin and uClibc-based systems libstdc++ is compiled with
247 // "generic" locale support
248 std::locale current_locale = std::locale::classic(); // C-Locale
251 std::locale lcl = std::locale(strLocale.c_str());
252 strLocale = lcl.name();
253 current_locale = current_locale.combine< std::collate<wchar_t> >(lcl);
254 current_locale = current_locale.combine< std::ctype<wchar_t> >(lcl);
255 current_locale = current_locale.combine< std::time_get<wchar_t> >(lcl);
256 current_locale = current_locale.combine< std::time_put<wchar_t> >(lcl);
258 assert(std::use_facet< std::numpunct<char> >(current_locale).decimal_point() == '.');
260 } catch(...) {
261 current_locale = std::locale::classic();
262 strLocale = "C";
265 g_langInfo.m_systemLocale = current_locale; //! @todo move to CLangInfo class
266 g_langInfo.m_collationtype = 0;
267 std::locale::global(current_locale);
268 #endif
270 #ifndef TARGET_WINDOWS
271 if (setlocale(LC_COLLATE, strLocale.c_str()) == NULL ||
272 setlocale(LC_CTYPE, strLocale.c_str()) == NULL ||
273 setlocale(LC_TIME, strLocale.c_str()) == NULL)
275 strLocale = "C";
276 setlocale(LC_COLLATE, strLocale.c_str());
277 setlocale(LC_CTYPE, strLocale.c_str());
278 setlocale(LC_TIME, strLocale.c_str());
280 #else
281 std::wstring strLocaleW;
282 g_charsetConverter.utf8ToW(strLocale, strLocaleW);
283 if (_wsetlocale(LC_COLLATE, strLocaleW.c_str()) == NULL ||
284 _wsetlocale(LC_CTYPE, strLocaleW.c_str()) == NULL ||
285 _wsetlocale(LC_TIME, strLocaleW.c_str()) == NULL)
287 strLocale = "C";
288 strLocaleW = L"C";
289 _wsetlocale(LC_COLLATE, strLocaleW.c_str());
290 _wsetlocale(LC_CTYPE, strLocaleW.c_str());
291 _wsetlocale(LC_TIME, strLocaleW.c_str());
293 #endif
295 g_charsetConverter.resetSystemCharset();
296 CLog::Log(LOGINFO, "global locale set to {}", strLocale);
298 #ifdef TARGET_ANDROID
299 // Force UTF8 for, e.g., vsnprintf
300 setlocale(LC_ALL, "C.UTF-8");
301 #endif
304 CLangInfo::CLangInfo()
306 SetDefaults();
307 m_shortDateFormat = m_defaultRegion.m_strDateFormatShort;
308 m_longDateFormat = m_defaultRegion.m_strDateFormatLong;
309 m_timeFormat = m_defaultRegion.m_strTimeFormat;
310 m_use24HourClock = DetermineUse24HourClockFromTimeFormat(m_defaultRegion.m_strTimeFormat);
311 m_temperatureUnit = m_defaultRegion.m_tempUnit;
312 m_speedUnit = m_defaultRegion.m_speedUnit;
313 m_collationtype = 0;
316 CLangInfo::~CLangInfo() = default;
318 void CLangInfo::OnSettingChanged(const std::shared_ptr<const CSetting>& setting)
320 if (setting == NULL)
321 return;
323 auto settingsComponent = CServiceBroker::GetSettingsComponent();
324 if (!settingsComponent)
325 return;
327 auto settings = settingsComponent->GetSettings();
328 if (!settings)
329 return;
331 const std::string &settingId = setting->GetId();
332 if (settingId == CSettings::SETTING_LOCALE_AUDIOLANGUAGE)
333 SetAudioLanguage(std::static_pointer_cast<const CSettingString>(setting)->GetValue());
334 else if (settingId == CSettings::SETTING_LOCALE_SUBTITLELANGUAGE)
335 SetSubtitleLanguage(std::static_pointer_cast<const CSettingString>(setting)->GetValue());
336 else if (settingId == CSettings::SETTING_LOCALE_LANGUAGE)
338 if (!SetLanguage(std::static_pointer_cast<const CSettingString>(setting)->GetValue()))
340 auto langsetting = settings->GetSetting(CSettings::SETTING_LOCALE_LANGUAGE);
341 if (!langsetting)
343 CLog::Log(LOGERROR, "Failed to load setting for: {}", CSettings::SETTING_LOCALE_LANGUAGE);
344 return;
347 std::static_pointer_cast<CSettingString>(langsetting)->Reset();
350 else if (settingId == CSettings::SETTING_LOCALE_COUNTRY)
351 SetCurrentRegion(std::static_pointer_cast<const CSettingString>(setting)->GetValue());
352 else if (settingId == CSettings::SETTING_LOCALE_SHORTDATEFORMAT)
353 SetShortDateFormat(std::static_pointer_cast<const CSettingString>(setting)->GetValue());
354 else if (settingId == CSettings::SETTING_LOCALE_LONGDATEFORMAT)
355 SetLongDateFormat(std::static_pointer_cast<const CSettingString>(setting)->GetValue());
356 else if (settingId == CSettings::SETTING_LOCALE_TIMEFORMAT)
357 SetTimeFormat(std::static_pointer_cast<const CSettingString>(setting)->GetValue());
358 else if (settingId == CSettings::SETTING_LOCALE_USE24HOURCLOCK)
360 Set24HourClock(std::static_pointer_cast<const CSettingString>(setting)->GetValue());
362 // update the time format
363 settings->SetString(CSettings::SETTING_LOCALE_TIMEFORMAT,
364 PrepareTimeFormat(GetTimeFormat(), m_use24HourClock));
366 else if (settingId == CSettings::SETTING_LOCALE_TEMPERATUREUNIT)
367 SetTemperatureUnit(std::static_pointer_cast<const CSettingString>(setting)->GetValue());
368 else if (settingId == CSettings::SETTING_LOCALE_SPEEDUNIT)
369 SetSpeedUnit(std::static_pointer_cast<const CSettingString>(setting)->GetValue());
372 void CLangInfo::OnSettingsLoaded()
374 // set the temperature and speed units based on the settings
375 const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
376 SetShortDateFormat(settings->GetString(CSettings::SETTING_LOCALE_SHORTDATEFORMAT));
377 SetLongDateFormat(settings->GetString(CSettings::SETTING_LOCALE_LONGDATEFORMAT));
378 Set24HourClock(settings->GetString(CSettings::SETTING_LOCALE_USE24HOURCLOCK));
379 SetTimeFormat(settings->GetString(CSettings::SETTING_LOCALE_TIMEFORMAT));
380 SetTemperatureUnit(settings->GetString(CSettings::SETTING_LOCALE_TEMPERATUREUNIT));
381 SetSpeedUnit(settings->GetString(CSettings::SETTING_LOCALE_SPEEDUNIT));
384 bool CLangInfo::Load(const std::string& strLanguage)
386 SetDefaults();
388 std::string strFileName = GetLanguageInfoPath(strLanguage);
390 CXBMCTinyXML xmlDoc;
391 if (!xmlDoc.LoadFile(strFileName))
393 CLog::Log(LOGERROR, "unable to load {}: {} at line {}", strFileName, xmlDoc.ErrorDesc(),
394 xmlDoc.ErrorRow());
395 return false;
398 // get the matching language addon
399 m_languageAddon = GetLanguageAddon(strLanguage);
400 if (m_languageAddon == NULL)
402 CLog::Log(LOGERROR, "Unknown language {}", strLanguage);
403 return false;
406 // get some language-specific information from the language addon
407 m_strGuiCharSet = m_languageAddon->GetGuiCharset();
408 m_forceUnicodeFont = m_languageAddon->ForceUnicodeFont();
409 m_strSubtitleCharSet = m_languageAddon->GetSubtitleCharset();
410 m_strDVDMenuLanguage = m_languageAddon->GetDvdMenuLanguage();
411 m_strDVDAudioLanguage = m_languageAddon->GetDvdAudioLanguage();
412 m_strDVDSubtitleLanguage = m_languageAddon->GetDvdSubtitleLanguage();
413 m_sortTokens = m_languageAddon->GetSortTokens();
415 TiXmlElement* pRootElement = xmlDoc.RootElement();
416 if (pRootElement->ValueStr() != "language")
418 CLog::Log(LOGERROR, "{} Doesn't contain <language>", strFileName);
419 return false;
422 if (pRootElement->Attribute("locale"))
423 m_defaultRegion.m_strLangLocaleName = pRootElement->Attribute("locale");
425 #ifdef TARGET_WINDOWS
426 // Windows need 3 chars isolang code
427 if (m_defaultRegion.m_strLangLocaleName.length() == 2)
429 if (!g_LangCodeExpander.ConvertISO6391ToISO6392B(m_defaultRegion.m_strLangLocaleName, m_defaultRegion.m_strLangLocaleName, true))
430 m_defaultRegion.m_strLangLocaleName = "";
433 if (!g_LangCodeExpander.ConvertWindowsLanguageCodeToISO6392B(m_defaultRegion.m_strLangLocaleName, m_languageCodeGeneral))
434 m_languageCodeGeneral = "";
435 #else
436 if (m_defaultRegion.m_strLangLocaleName.length() != 3)
438 if (!g_LangCodeExpander.ConvertToISO6392B(m_defaultRegion.m_strLangLocaleName, m_languageCodeGeneral))
439 m_languageCodeGeneral = "";
441 else
442 m_languageCodeGeneral = m_defaultRegion.m_strLangLocaleName;
443 #endif
445 std::string tmp;
446 if (g_LangCodeExpander.ConvertToISO6391(m_defaultRegion.m_strLangLocaleName, tmp))
447 m_defaultRegion.m_strLangLocaleCodeTwoChar = tmp;
449 const TiXmlNode *pRegions = pRootElement->FirstChild("regions");
450 if (pRegions && !pRegions->NoChildren())
452 const TiXmlElement *pRegion=pRegions->FirstChildElement("region");
453 while (pRegion)
455 CRegion region(m_defaultRegion);
456 region.m_strName = XMLUtils::GetAttribute(pRegion, "name");
457 if (region.m_strName.empty())
458 region.m_strName=g_localizeStrings.Get(10005); // Not available
460 if (pRegion->Attribute("locale"))
461 region.m_strRegionLocaleName = pRegion->Attribute("locale");
463 #ifdef TARGET_WINDOWS
464 // Windows need 3 chars regions code
465 if (region.m_strRegionLocaleName.length() == 2)
467 if (!g_LangCodeExpander.ConvertISO31661Alpha2ToISO31661Alpha3(region.m_strRegionLocaleName, region.m_strRegionLocaleName))
468 region.m_strRegionLocaleName = "";
470 #endif
472 const TiXmlNode *pDateLong=pRegion->FirstChild("datelong");
473 if (pDateLong && !pDateLong->NoChildren())
474 region.m_strDateFormatLong=pDateLong->FirstChild()->ValueStr();
476 const TiXmlNode *pDateShort=pRegion->FirstChild("dateshort");
477 if (pDateShort && !pDateShort->NoChildren())
478 region.m_strDateFormatShort=pDateShort->FirstChild()->ValueStr();
480 const TiXmlElement *pTime=pRegion->FirstChildElement("time");
481 if (pTime && !pTime->NoChildren())
483 region.m_strTimeFormat=pTime->FirstChild()->Value();
484 region.m_strMeridiemSymbols[MeridiemSymbolAM] = XMLUtils::GetAttribute(pTime, "symbolAM");
485 region.m_strMeridiemSymbols[MeridiemSymbolPM] = XMLUtils::GetAttribute(pTime, "symbolPM");
488 const TiXmlNode *pTempUnit=pRegion->FirstChild("tempunit");
489 if (pTempUnit && !pTempUnit->NoChildren())
490 region.SetTemperatureUnit(pTempUnit->FirstChild()->ValueStr());
492 const TiXmlNode *pSpeedUnit=pRegion->FirstChild("speedunit");
493 if (pSpeedUnit && !pSpeedUnit->NoChildren())
494 region.SetSpeedUnit(pSpeedUnit->FirstChild()->ValueStr());
496 const TiXmlNode *pTimeZone=pRegion->FirstChild("timezone");
497 if (pTimeZone && !pTimeZone->NoChildren())
498 region.SetTimeZone(pTimeZone->FirstChild()->ValueStr());
500 const TiXmlElement *pThousandsSep = pRegion->FirstChildElement("thousandsseparator");
501 if (pThousandsSep)
503 if (!pThousandsSep->NoChildren())
505 region.m_cThousandsSep = pThousandsSep->FirstChild()->Value()[0];
506 if (pThousandsSep->Attribute("groupingformat"))
507 region.m_strGrouping = StringUtils::BinaryStringToString(pThousandsSep->Attribute("groupingformat"));
508 else
509 region.m_strGrouping = "\3";
512 else
514 region.m_cThousandsSep = ',';
515 region.m_strGrouping = "\3";
518 const TiXmlElement *pDecimalSep = pRegion->FirstChildElement("decimalseparator");
519 if (pDecimalSep)
521 if (!pDecimalSep->NoChildren())
522 region.m_cDecimalSep = pDecimalSep->FirstChild()->Value()[0];
524 else
525 region.m_cDecimalSep = '.';
527 m_regions.insert(PAIR_REGIONS(region.m_strName, region));
529 pRegion=pRegion->NextSiblingElement("region");
532 const std::string& strName = CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_LOCALE_COUNTRY);
533 SetCurrentRegion(strName);
535 g_charsetConverter.reinitCharsetsFromSettings();
537 return true;
540 std::string CLangInfo::GetLanguagePath(const std::string &language)
542 if (language.empty())
543 return "";
545 std::string addonId = ADDON::CLanguageResource::GetAddonId(language);
547 std::string path = URIUtils::AddFileToFolder(GetLanguagePath(), addonId);
548 URIUtils::AddSlashAtEnd(path);
550 return path;
553 std::string CLangInfo::GetLanguageInfoPath(const std::string &language)
555 if (language.empty())
556 return "";
558 return URIUtils::AddFileToFolder(GetLanguagePath(language), "langinfo.xml");
561 bool CLangInfo::UseLocaleCollation()
563 if (m_collationtype == 0)
565 // Determine collation to use. When using MySQL/MariaDB or a platform that does not support
566 // locale language collation then use accent folding internal equivalent of utf8_general_ci
567 m_collationtype = 1;
568 if (!StringUtils::EqualsNoCase(
569 CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_databaseMusic.type,
570 "mysql") &&
571 !StringUtils::EqualsNoCase(
572 CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_databaseVideo.type,
573 "mysql") &&
574 CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_useLocaleCollation)
576 // Check that locale collation facet is implemented on the platform
577 const std::collate<wchar_t>& coll = std::use_facet<std::collate<wchar_t>>(m_systemLocale);
578 wchar_t lc = L'z';
579 wchar_t rc = 0x00E2; // Latin small letter a with circumflex
580 int comp_result = coll.compare(&lc, &lc + 1, &rc, &rc + 1);
581 if (comp_result > 0)
582 // Latin small letter a with circumflex put before z - collation works
583 m_collationtype = 2;
586 return m_collationtype == 2;
589 void CLangInfo::LoadTokens(const TiXmlNode* pTokens, std::set<std::string>& vecTokens)
591 if (pTokens && !pTokens->NoChildren())
593 const TiXmlElement *pToken = pTokens->FirstChildElement("token");
594 while (pToken)
596 std::string strSep= " ._";
597 if (pToken->Attribute("separators"))
598 strSep = pToken->Attribute("separators");
599 if (pToken->FirstChild() && pToken->FirstChild()->Value())
601 if (strSep.empty())
602 vecTokens.insert(pToken->FirstChild()->ValueStr());
603 else
604 for (unsigned int i=0;i<strSep.size();++i)
605 vecTokens.insert(pToken->FirstChild()->ValueStr() + strSep[i]);
607 pToken = pToken->NextSiblingElement();
612 void CLangInfo::SetDefaults()
614 m_regions.clear();
616 //Reset default region
617 m_defaultRegion.SetDefaults();
619 // Set the default region, we may be unable to load langinfo.xml
620 m_currentRegion = &m_defaultRegion;
622 m_systemLocale = std::locale::classic();
624 m_forceUnicodeFont = false;
625 m_strGuiCharSet = "CP1252";
626 m_strSubtitleCharSet = "CP1252";
627 m_strDVDMenuLanguage = "en";
628 m_strDVDAudioLanguage = "en";
629 m_strDVDSubtitleLanguage = "en";
630 m_sortTokens.clear();
632 m_languageCodeGeneral = "eng";
635 std::string CLangInfo::GetGuiCharSet() const
637 std::shared_ptr<CSettingString> charsetSetting = std::static_pointer_cast<CSettingString>(CServiceBroker::GetSettingsComponent()->GetSettings()->GetSetting(CSettings::SETTING_LOCALE_CHARSET));
638 if (charsetSetting == NULL || charsetSetting->IsDefault())
639 return m_strGuiCharSet;
641 return charsetSetting->GetValue();
644 std::string CLangInfo::GetSubtitleCharSet() const
646 std::shared_ptr<CSettingString> charsetSetting = std::static_pointer_cast<CSettingString>(CServiceBroker::GetSettingsComponent()->GetSettings()->GetSetting(CSettings::SETTING_SUBTITLES_CHARSET));
647 if (charsetSetting->IsDefault())
648 return m_strSubtitleCharSet;
650 return charsetSetting->GetValue();
653 void CLangInfo::GetAddonsLanguageCodes(std::map<std::string, std::string>& languages)
655 ADDON::VECADDONS addons;
656 CServiceBroker::GetAddonMgr().GetAddons(addons, ADDON::AddonType::RESOURCE_LANGUAGE);
657 for (const auto& addon : addons)
659 const LanguageResourcePtr langAddon =
660 std::dynamic_pointer_cast<ADDON::CLanguageResource>(addon);
661 std::string langCode{langAddon->GetLocale().ToShortStringLC()};
662 StringUtils::Replace(langCode, '_', '-');
663 languages.emplace(langCode, addon->Name());
667 LanguageResourcePtr CLangInfo::GetLanguageAddon(const std::string& locale /* = "" */) const
669 if (locale.empty() ||
670 (m_languageAddon != NULL && (locale.compare(m_languageAddon->ID()) == 0 || m_languageAddon->GetLocale().Equals(locale))))
671 return m_languageAddon;
673 std::string addonId = ADDON::CLanguageResource::GetAddonId(locale);
674 if (addonId.empty())
675 addonId = CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_LOCALE_LANGUAGE);
677 ADDON::AddonPtr addon;
678 if (CServiceBroker::GetAddonMgr().GetAddon(addonId, addon, ADDON::AddonType::RESOURCE_LANGUAGE,
679 ADDON::OnlyEnabled::CHOICE_YES) &&
680 addon != NULL)
681 return std::dynamic_pointer_cast<ADDON::CLanguageResource>(addon);
683 return NULL;
686 std::string CLangInfo::ConvertEnglishNameToAddonLocale(const std::string& langName)
688 ADDON::VECADDONS addons;
689 CServiceBroker::GetAddonMgr().GetAddons(addons, ADDON::AddonType::RESOURCE_LANGUAGE);
690 for (const auto& addon : addons)
692 if (StringUtils::CompareNoCase(addon->Name(), langName) == 0)
694 const LanguageResourcePtr langAddon =
695 std::dynamic_pointer_cast<ADDON::CLanguageResource>(addon);
696 std::string locale = langAddon->GetLocale().ToShortStringLC();
697 StringUtils::Replace(locale, '_', '-');
698 return locale;
701 return "";
704 std::string CLangInfo::GetEnglishLanguageName(const std::string& locale /* = "" */) const
706 LanguageResourcePtr addon = GetLanguageAddon(locale);
707 if (addon == NULL)
708 return "";
710 return addon->Name();
713 bool CLangInfo::SetLanguage(std::string language /* = "" */, bool reloadServices /* = true */)
715 if (language.empty())
716 language = CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_LOCALE_LANGUAGE);
718 auto& addonMgr = CServiceBroker::GetAddonMgr();
719 ADDON::AddonPtr addon;
721 // Find the chosen language add-on if it's enabled
722 if (!addonMgr.GetAddon(language, addon, ADDON::AddonType::RESOURCE_LANGUAGE,
723 ADDON::OnlyEnabled::CHOICE_YES))
725 if (!addonMgr.IsAddonInstalled(language) ||
726 (addonMgr.IsAddonDisabled(language) && !addonMgr.EnableAddon(language)))
728 CLog::Log(LOGWARNING,
729 "CLangInfo::{}: could not find or enable language add-on '{}', loading default...",
730 __func__, language);
731 language = std::static_pointer_cast<const CSettingString>(
732 CServiceBroker::GetSettingsComponent()->GetSettings()->GetSetting(
733 CSettings::SETTING_LOCALE_LANGUAGE))
734 ->GetDefault();
736 if (!addonMgr.GetAddon(language, addon, ADDON::AddonType::RESOURCE_LANGUAGE,
737 ADDON::OnlyEnabled::CHOICE_NO))
739 CLog::Log(LOGFATAL, "CLangInfo::{}: could not find default language add-on '{}'", __func__,
740 language);
741 return false;
746 CLog::Log(LOGINFO, "CLangInfo: loading {} language information...", language);
747 if (!Load(language))
749 CLog::LogF(LOGFATAL, "CLangInfo: failed to load {} language information", language);
750 return false;
753 CLog::Log(LOGINFO, "CLangInfo: loading {} language strings...", language);
754 if (!g_localizeStrings.Load(GetLanguagePath(), language))
756 CLog::LogF(LOGFATAL, "CLangInfo: failed to load {} language strings", language);
757 return false;
760 ADDON::VECADDONS addons;
761 if (CServiceBroker::GetAddonMgr().GetInstalledAddons(addons))
763 const std::string locale = CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_LOCALE_LANGUAGE);
764 for (const auto& addon : addons)
766 const std::string path = URIUtils::AddFileToFolder(addon->Path(), "resources", "language/");
767 g_localizeStrings.LoadAddonStrings(path, locale, addon->ID());
771 if (reloadServices)
773 // also tell our weather and skin to reload as these are localized
774 CServiceBroker::GetWeatherManager().Refresh();
775 CServiceBroker::GetPVRManager().LocalizationChanged();
776 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_EXECUTE_BUILT_IN, -1, -1, nullptr,
777 "ReloadSkin");
780 return true;
783 // three char language code (not win32 specific)
784 const std::string& CLangInfo::GetAudioLanguage() const
786 if (!m_audioLanguage.empty())
787 return m_audioLanguage;
789 return m_languageCodeGeneral;
792 void CLangInfo::SetAudioLanguage(const std::string& language)
794 if (language.empty()
795 || StringUtils::EqualsNoCase(language, "default")
796 || StringUtils::EqualsNoCase(language, "original")
797 || StringUtils::EqualsNoCase(language, "mediadefault")
798 || !g_LangCodeExpander.ConvertToISO6392B(language, m_audioLanguage))
799 m_audioLanguage.clear();
802 // three char language code (not win32 specific)
803 const std::string& CLangInfo::GetSubtitleLanguage() const
805 if (!m_subtitleLanguage.empty())
806 return m_subtitleLanguage;
808 return m_languageCodeGeneral;
811 void CLangInfo::SetSubtitleLanguage(const std::string& language)
813 if (language.empty()
814 || StringUtils::EqualsNoCase(language, "default")
815 || StringUtils::EqualsNoCase(language, "original")
816 || !g_LangCodeExpander.ConvertToISO6392B(language, m_subtitleLanguage))
817 m_subtitleLanguage.clear();
820 // two character codes as defined in ISO639
821 const std::string CLangInfo::GetDVDMenuLanguage() const
823 std::string code;
824 if (!g_LangCodeExpander.ConvertToISO6391(m_currentRegion->m_strLangLocaleName, code))
825 code = m_strDVDMenuLanguage;
827 return code;
830 // two character codes as defined in ISO639
831 const std::string CLangInfo::GetDVDAudioLanguage() const
833 std::string code;
834 if (!g_LangCodeExpander.ConvertToISO6391(m_audioLanguage, code))
835 code = m_strDVDAudioLanguage;
837 return code;
840 // two character codes as defined in ISO639
841 const std::string CLangInfo::GetDVDSubtitleLanguage() const
843 std::string code;
844 if (!g_LangCodeExpander.ConvertToISO6391(m_subtitleLanguage, code))
845 code = m_strDVDSubtitleLanguage;
847 return code;
850 const CLocale& CLangInfo::GetLocale() const
852 LanguageResourcePtr language = GetLanguageAddon();
853 if (language != NULL)
854 return language->GetLocale();
856 return CLocale::Empty;
859 const std::string& CLangInfo::GetRegionLocale() const
861 return m_currentRegion->m_strRegionLocaleName;
864 const std::locale& CLangInfo::GetOriginalLocale() const
866 return m_originalLocale;
869 // Returns the format string for the date of the current language
870 const std::string& CLangInfo::GetDateFormat(bool bLongDate /* = false */) const
872 if (bLongDate)
873 return GetLongDateFormat();
875 return GetShortDateFormat();
878 void CLangInfo::SetDateFormat(const std::string& dateFormat, bool bLongDate /* = false */)
880 if (bLongDate)
881 SetLongDateFormat(dateFormat);
882 else
883 SetShortDateFormat(dateFormat);
886 const std::string& CLangInfo::GetShortDateFormat() const
888 return m_shortDateFormat;
891 void CLangInfo::SetShortDateFormat(const std::string& shortDateFormat)
893 std::string newShortDateFormat = shortDateFormat;
894 if (shortDateFormat == SETTING_REGIONAL_DEFAULT)
895 newShortDateFormat = m_currentRegion->m_strDateFormatShort;
897 m_shortDateFormat = newShortDateFormat;
900 const std::string& CLangInfo::GetLongDateFormat() const
902 return m_longDateFormat;
905 void CLangInfo::SetLongDateFormat(const std::string& longDateFormat)
907 std::string newLongDateFormat = longDateFormat;
908 if (longDateFormat == SETTING_REGIONAL_DEFAULT)
909 newLongDateFormat = m_currentRegion->m_strDateFormatShort;
911 m_longDateFormat = newLongDateFormat;
914 // Returns the format string for the time of the current language
915 const std::string& CLangInfo::GetTimeFormat() const
917 return m_timeFormat;
920 void CLangInfo::SetTimeFormat(const std::string& timeFormat)
922 std::string newTimeFormat = timeFormat;
923 if (timeFormat == SETTING_REGIONAL_DEFAULT)
924 newTimeFormat = m_currentRegion->m_strTimeFormat;
926 m_timeFormat = PrepareTimeFormat(newTimeFormat, m_use24HourClock);
929 bool CLangInfo::Use24HourClock() const
931 return m_use24HourClock;
934 void CLangInfo::Set24HourClock(bool use24HourClock)
936 m_use24HourClock = use24HourClock;
939 void CLangInfo::Set24HourClock(const std::string& str24HourClock)
941 bool use24HourClock = false;
942 if (str24HourClock == TIME_FORMAT_12HOURS)
943 use24HourClock = false;
944 else if (str24HourClock == TIME_FORMAT_24HOURS)
945 use24HourClock = true;
946 else if (str24HourClock == SETTING_REGIONAL_DEFAULT)
948 Set24HourClock(m_currentRegion->m_strTimeFormat);
949 return;
951 else
952 use24HourClock = DetermineUse24HourClockFromTimeFormat(str24HourClock);
954 if (m_use24HourClock == use24HourClock)
955 return;
957 m_use24HourClock = use24HourClock;
960 const std::string& CLangInfo::GetTimeZone() const
962 return m_currentRegion->m_strTimeZone;
965 // Returns the AM/PM symbol of the current language
966 const std::string& CLangInfo::GetMeridiemSymbol(MeridiemSymbol symbol) const
968 // nothing to return if we use 24-hour clock
969 if (m_use24HourClock)
970 return StringUtils::Empty;
972 return MeridiemSymbolToString(symbol);
975 const std::string& CLangInfo::MeridiemSymbolToString(MeridiemSymbol symbol)
977 switch (symbol)
979 case MeridiemSymbolAM:
980 return g_localizeStrings.Get(378);
982 case MeridiemSymbolPM:
983 return g_localizeStrings.Get(379);
985 default:
986 break;
989 return StringUtils::Empty;
992 // Fills the array with the region names available for this language
993 void CLangInfo::GetRegionNames(std::vector<std::string>& array)
995 for (const auto &region : m_regions)
997 std::string strName=region.first;
998 if (strName=="N/A")
999 strName=g_localizeStrings.Get(10005); // Not available
1000 array.emplace_back(std::move(strName));
1004 // Set the current region by its name, names from GetRegionNames() are valid.
1005 // If the region is not found the first available region is set.
1006 void CLangInfo::SetCurrentRegion(const std::string& strName)
1008 ITMAPREGIONS it=m_regions.find(strName);
1009 if (it!=m_regions.end())
1010 m_currentRegion=&it->second;
1011 else if (!m_regions.empty())
1012 m_currentRegion=&m_regions.begin()->second;
1013 else
1014 m_currentRegion=&m_defaultRegion;
1016 m_currentRegion->SetGlobalLocale();
1018 const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
1019 if (settings->GetString(CSettings::SETTING_LOCALE_SHORTDATEFORMAT) == SETTING_REGIONAL_DEFAULT)
1020 SetShortDateFormat(m_currentRegion->m_strDateFormatShort);
1021 if (settings->GetString(CSettings::SETTING_LOCALE_LONGDATEFORMAT) == SETTING_REGIONAL_DEFAULT)
1022 SetLongDateFormat(m_currentRegion->m_strDateFormatLong);
1023 if (settings->GetString(CSettings::SETTING_LOCALE_USE24HOURCLOCK) == SETTING_REGIONAL_DEFAULT)
1025 Set24HourClock(m_currentRegion->m_strTimeFormat);
1027 // update the time format
1028 SetTimeFormat(settings->GetString(CSettings::SETTING_LOCALE_TIMEFORMAT));
1030 if (settings->GetString(CSettings::SETTING_LOCALE_TIMEFORMAT) == SETTING_REGIONAL_DEFAULT)
1031 SetTimeFormat(m_currentRegion->m_strTimeFormat);
1032 if (settings->GetString(CSettings::SETTING_LOCALE_TEMPERATUREUNIT) == SETTING_REGIONAL_DEFAULT)
1033 SetTemperatureUnit(m_currentRegion->m_tempUnit);
1034 if (settings->GetString(CSettings::SETTING_LOCALE_SPEEDUNIT) == SETTING_REGIONAL_DEFAULT)
1035 SetSpeedUnit(m_currentRegion->m_speedUnit);
1038 // Returns the current region set for this language
1039 const std::string& CLangInfo::GetCurrentRegion() const
1041 return m_currentRegion->m_strName;
1044 CTemperature::Unit CLangInfo::GetTemperatureUnit() const
1046 return m_temperatureUnit;
1049 void CLangInfo::SetTemperatureUnit(CTemperature::Unit temperatureUnit)
1051 if (m_temperatureUnit == temperatureUnit)
1052 return;
1054 m_temperatureUnit = temperatureUnit;
1056 // refresh weather manager as temperatures need re-translating
1057 // NOTE: this could be called before our service manager is up
1058 if (CServiceBroker::IsServiceManagerUp())
1059 CServiceBroker::GetWeatherManager().Refresh();
1062 void CLangInfo::SetTemperatureUnit(const std::string& temperatureUnit)
1064 CTemperature::Unit unit = CTemperature::UnitCelsius;
1065 if (temperatureUnit == SETTING_REGIONAL_DEFAULT)
1066 unit = m_currentRegion->m_tempUnit;
1067 else
1068 unit = StringToTemperatureUnit(temperatureUnit);
1070 SetTemperatureUnit(unit);
1073 std::string CLangInfo::GetTemperatureAsString(const CTemperature& temperature) const
1075 if (!temperature.IsValid())
1076 return g_localizeStrings.Get(13205); // "Unknown"
1078 CTemperature::Unit temperatureUnit = GetTemperatureUnit();
1079 return StringUtils::Format("{}{}", temperature.ToString(temperatureUnit),
1080 GetTemperatureUnitString());
1083 // Returns the temperature unit string for the current language
1084 const std::string& CLangInfo::GetTemperatureUnitString() const
1086 return GetTemperatureUnitString(m_temperatureUnit);
1089 const std::string& CLangInfo::GetTemperatureUnitString(CTemperature::Unit temperatureUnit)
1091 return g_localizeStrings.Get(TEMP_UNIT_STRINGS + temperatureUnit);
1094 void CLangInfo::SetSpeedUnit(CSpeed::Unit speedUnit)
1096 if (m_speedUnit == speedUnit)
1097 return;
1099 m_speedUnit = speedUnit;
1101 // refresh weather manager as speeds need re-translating
1102 // NOTE: this could be called before our service manager is up
1103 if (CServiceBroker::IsServiceManagerUp())
1104 CServiceBroker::GetWeatherManager().Refresh();
1107 void CLangInfo::SetSpeedUnit(const std::string& speedUnit)
1109 CSpeed::Unit unit = CSpeed::UnitKilometresPerHour;
1110 if (speedUnit == SETTING_REGIONAL_DEFAULT)
1111 unit = m_currentRegion->m_speedUnit;
1112 else
1113 unit = StringToSpeedUnit(speedUnit);
1115 SetSpeedUnit(unit);
1118 CSpeed::Unit CLangInfo::GetSpeedUnit() const
1120 return m_speedUnit;
1123 std::string CLangInfo::GetSpeedAsString(const CSpeed& speed) const
1125 if (!speed.IsValid())
1126 return g_localizeStrings.Get(13205); // "Unknown"
1128 return StringUtils::Format("{}{}", speed.ToString(GetSpeedUnit()), GetSpeedUnitString());
1131 // Returns the speed unit string for the current language
1132 const std::string& CLangInfo::GetSpeedUnitString() const
1134 return GetSpeedUnitString(m_speedUnit);
1137 const std::string& CLangInfo::GetSpeedUnitString(CSpeed::Unit speedUnit)
1139 return g_localizeStrings.Get(SPEED_UNIT_STRINGS + speedUnit);
1142 std::set<std::string> CLangInfo::GetSortTokens() const
1144 std::set<std::string> sortTokens = m_sortTokens;
1145 for (const auto& t : CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_vecTokens)
1146 sortTokens.insert(t);
1148 return sortTokens;
1151 bool CLangInfo::DetermineUse24HourClockFromTimeFormat(const std::string& timeFormat)
1153 // if the time format contains a "h" it's 12-hour and otherwise 24-hour clock format
1154 return timeFormat.find('h') == std::string::npos;
1157 bool CLangInfo::DetermineUseMeridiemFromTimeFormat(const std::string& timeFormat)
1159 // if the time format contains "xx" it's using meridiem
1160 return timeFormat.find("xx") != std::string::npos;
1163 std::string CLangInfo::PrepareTimeFormat(const std::string& timeFormat, bool use24HourClock)
1165 std::string preparedTimeFormat = timeFormat;
1166 if (use24HourClock)
1168 // replace all "h" with "H"
1169 StringUtils::Replace(preparedTimeFormat, 'h', 'H');
1171 // remove any "xx" for meridiem
1172 StringUtils::Replace(preparedTimeFormat, "x", "");
1174 else
1175 // replace all "H" with "h"
1176 StringUtils::Replace(preparedTimeFormat, 'H', 'h');
1178 StringUtils::Trim(preparedTimeFormat);
1180 return preparedTimeFormat;
1183 void CLangInfo::SettingOptionsLanguageNamesFiller(const SettingConstPtr& setting,
1184 std::vector<StringSettingOption>& list,
1185 std::string& current,
1186 void* data)
1188 // find languages...
1189 ADDON::VECADDONS addons;
1190 if (!CServiceBroker::GetAddonMgr().GetAddons(addons, ADDON::AddonType::RESOURCE_LANGUAGE))
1191 return;
1193 for (const auto &addon : addons)
1194 list.emplace_back(addon->Name(), addon->Name());
1196 sort(list.begin(), list.end(), SortLanguage());
1199 void CLangInfo::SettingOptionsISO6391LanguagesFiller(const SettingConstPtr& setting,
1200 std::vector<StringSettingOption>& list,
1201 std::string& current,
1202 void* data)
1204 std::vector<std::string> languages = g_LangCodeExpander.GetLanguageNames(
1205 CLangCodeExpander::ISO_639_1, CLangCodeExpander::LANG_LIST::INCLUDE_USERDEFINED);
1207 for (const auto &language : languages)
1208 list.emplace_back(language, language);
1211 void CLangInfo::SettingOptionsAudioStreamLanguagesFiller(const SettingConstPtr& setting,
1212 std::vector<StringSettingOption>& list,
1213 std::string& current,
1214 void* data)
1216 list.emplace_back(g_localizeStrings.Get(307), "mediadefault");
1217 list.emplace_back(g_localizeStrings.Get(308), "original");
1218 list.emplace_back(g_localizeStrings.Get(309), "default");
1220 AddLanguages(list);
1223 void CLangInfo::SettingOptionsSubtitleStreamLanguagesFiller(const SettingConstPtr& setting,
1224 std::vector<StringSettingOption>& list,
1225 std::string& current,
1226 void* data)
1228 list.emplace_back(g_localizeStrings.Get(231), "none");
1229 list.emplace_back(g_localizeStrings.Get(13207), "forced_only");
1230 list.emplace_back(g_localizeStrings.Get(308), "original");
1231 list.emplace_back(g_localizeStrings.Get(309), "default");
1233 AddLanguages(list);
1236 void CLangInfo::SettingOptionsSubtitleDownloadlanguagesFiller(
1237 const SettingConstPtr& setting,
1238 std::vector<StringSettingOption>& list,
1239 std::string& current,
1240 void* data)
1242 list.emplace_back(g_localizeStrings.Get(308), "original");
1243 list.emplace_back(g_localizeStrings.Get(309), "default");
1245 AddLanguages(list);
1248 void CLangInfo::SettingOptionsRegionsFiller(const SettingConstPtr& setting,
1249 std::vector<StringSettingOption>& list,
1250 std::string& current,
1251 void* data)
1253 std::vector<std::string> regions;
1254 g_langInfo.GetRegionNames(regions);
1255 std::sort(regions.begin(), regions.end(), sortstringbyname());
1257 bool match = false;
1258 for (unsigned int i = 0; i < regions.size(); ++i)
1260 std::string region = regions[i];
1261 list.emplace_back(region, region);
1263 if (!match && region == std::static_pointer_cast<const CSettingString>(setting)->GetValue())
1265 match = true;
1266 current = region;
1270 if (!match && !regions.empty())
1271 current = regions[0];
1274 void CLangInfo::SettingOptionsShortDateFormatsFiller(const SettingConstPtr& setting,
1275 std::vector<StringSettingOption>& list,
1276 std::string& current,
1277 void* data)
1279 bool match = false;
1280 const std::string& shortDateFormatSetting = std::static_pointer_cast<const CSettingString>(setting)->GetValue();
1282 CDateTime now = CDateTime::GetCurrentDateTime();
1284 list.emplace_back(
1285 StringUtils::Format(g_localizeStrings.Get(20035),
1286 now.GetAsLocalizedDate(g_langInfo.m_currentRegion->m_strDateFormatShort)),
1287 SETTING_REGIONAL_DEFAULT);
1288 if (shortDateFormatSetting == SETTING_REGIONAL_DEFAULT)
1290 match = true;
1291 current = SETTING_REGIONAL_DEFAULT;
1294 for (const std::string& shortDateFormat : shortDateFormats)
1296 list.emplace_back(now.GetAsLocalizedDate(shortDateFormat), shortDateFormat);
1298 if (!match && shortDateFormatSetting == shortDateFormat)
1300 match = true;
1301 current = shortDateFormat;
1305 if (!match && !list.empty())
1306 current = list[0].value;
1309 void CLangInfo::SettingOptionsLongDateFormatsFiller(const SettingConstPtr& setting,
1310 std::vector<StringSettingOption>& list,
1311 std::string& current,
1312 void* data)
1314 bool match = false;
1315 const std::string& longDateFormatSetting = std::static_pointer_cast<const CSettingString>(setting)->GetValue();
1317 CDateTime now = CDateTime::GetCurrentDateTime();
1319 list.emplace_back(
1320 StringUtils::Format(g_localizeStrings.Get(20035),
1321 now.GetAsLocalizedDate(g_langInfo.m_currentRegion->m_strDateFormatLong)),
1322 SETTING_REGIONAL_DEFAULT);
1323 if (longDateFormatSetting == SETTING_REGIONAL_DEFAULT)
1325 match = true;
1326 current = SETTING_REGIONAL_DEFAULT;
1329 for (const std::string& longDateFormat : longDateFormats)
1331 list.emplace_back(now.GetAsLocalizedDate(longDateFormat), longDateFormat);
1333 if (!match && longDateFormatSetting == longDateFormat)
1335 match = true;
1336 current = longDateFormat;
1340 if (!match && !list.empty())
1341 current = list[0].value;
1344 void CLangInfo::SettingOptionsTimeFormatsFiller(const SettingConstPtr& setting,
1345 std::vector<StringSettingOption>& list,
1346 std::string& current,
1347 void* data)
1349 bool match = false;
1350 const std::string& timeFormatSetting = std::static_pointer_cast<const CSettingString>(setting)->GetValue();
1352 CDateTime now = CDateTime::GetCurrentDateTime();
1353 bool use24hourFormat = g_langInfo.Use24HourClock();
1355 list.emplace_back(
1356 StringUtils::Format(g_localizeStrings.Get(20035),
1357 ToSettingTimeFormat(now, g_langInfo.m_currentRegion->m_strTimeFormat)),
1358 SETTING_REGIONAL_DEFAULT);
1359 if (timeFormatSetting == SETTING_REGIONAL_DEFAULT)
1361 match = true;
1362 current = SETTING_REGIONAL_DEFAULT;
1365 if (use24hourFormat)
1367 list.emplace_back(ToSettingTimeFormat(now, TIME_FORMAT_SINGLE_24), TIME_FORMAT_SINGLE_24);
1368 if (timeFormatSetting == TIME_FORMAT_SINGLE_24)
1370 current = TIME_FORMAT_SINGLE_24;
1371 match = true;
1374 list.emplace_back(ToSettingTimeFormat(now, TIME_FORMAT_DOUBLE_24), TIME_FORMAT_DOUBLE_24);
1375 if (timeFormatSetting == TIME_FORMAT_DOUBLE_24)
1377 current = TIME_FORMAT_DOUBLE_24;
1378 match = true;
1381 else
1383 list.emplace_back(ToSettingTimeFormat(now, TIME_FORMAT_SINGLE_12), TIME_FORMAT_SINGLE_12);
1384 if (timeFormatSetting == TIME_FORMAT_SINGLE_12)
1386 current = TIME_FORMAT_SINGLE_12;
1387 match = true;
1390 list.emplace_back(ToSettingTimeFormat(now, TIME_FORMAT_DOUBLE_12), TIME_FORMAT_DOUBLE_12);
1391 if (timeFormatSetting == TIME_FORMAT_DOUBLE_12)
1393 current = TIME_FORMAT_DOUBLE_12;
1394 match = true;
1397 std::string timeFormatSingle12Meridiem = ToTimeFormat(false, true, true);
1398 list.emplace_back(ToSettingTimeFormat(now, timeFormatSingle12Meridiem), timeFormatSingle12Meridiem);
1399 if (timeFormatSetting == timeFormatSingle12Meridiem)
1401 current = timeFormatSingle12Meridiem;
1402 match = true;
1405 std::string timeFormatDouble12Meridiem = ToTimeFormat(false, false, true);
1406 list.emplace_back(ToSettingTimeFormat(now, timeFormatDouble12Meridiem), timeFormatDouble12Meridiem);
1407 if (timeFormatSetting == timeFormatDouble12Meridiem)
1409 current = timeFormatDouble12Meridiem;
1410 match = true;
1414 if (!match && !list.empty())
1415 current = list[0].value;
1418 void CLangInfo::SettingOptions24HourClockFormatsFiller(const SettingConstPtr& setting,
1419 std::vector<StringSettingOption>& list,
1420 std::string& current,
1421 void* data)
1423 bool match = false;
1424 const std::string& clock24HourFormatSetting = std::static_pointer_cast<const CSettingString>(setting)->GetValue();
1426 // determine the 24-hour clock format of the regional setting
1427 int regionalClock24HourFormatLabel = DetermineUse24HourClockFromTimeFormat(g_langInfo.m_currentRegion->m_strTimeFormat) ? 12384 : 12383;
1428 list.emplace_back(StringUtils::Format(g_localizeStrings.Get(20035),
1429 g_localizeStrings.Get(regionalClock24HourFormatLabel)),
1430 SETTING_REGIONAL_DEFAULT);
1431 if (clock24HourFormatSetting == SETTING_REGIONAL_DEFAULT)
1433 match = true;
1434 current = SETTING_REGIONAL_DEFAULT;
1437 list.emplace_back(g_localizeStrings.Get(12383), TIME_FORMAT_12HOURS);
1438 if (clock24HourFormatSetting == TIME_FORMAT_12HOURS)
1440 current = TIME_FORMAT_12HOURS;
1441 match = true;
1444 list.emplace_back(g_localizeStrings.Get(12384), TIME_FORMAT_24HOURS);
1445 if (clock24HourFormatSetting == TIME_FORMAT_24HOURS)
1447 current = TIME_FORMAT_24HOURS;
1448 match = true;
1451 if (!match && !list.empty())
1452 current = list[0].value;
1455 void CLangInfo::SettingOptionsTemperatureUnitsFiller(const SettingConstPtr& setting,
1456 std::vector<StringSettingOption>& list,
1457 std::string& current,
1458 void* data)
1460 bool match = false;
1461 const std::string& temperatureUnitSetting = std::static_pointer_cast<const CSettingString>(setting)->GetValue();
1463 list.emplace_back(
1464 StringUtils::Format(g_localizeStrings.Get(20035),
1465 GetTemperatureUnitString(g_langInfo.m_currentRegion->m_tempUnit)),
1466 SETTING_REGIONAL_DEFAULT);
1467 if (temperatureUnitSetting == SETTING_REGIONAL_DEFAULT)
1469 match = true;
1470 current = SETTING_REGIONAL_DEFAULT;
1473 for (const TemperatureInfo& info : temperatureInfo)
1475 list.emplace_back(GetTemperatureUnitString(info.unit), info.name);
1477 if (!match && temperatureUnitSetting == info.name)
1479 match = true;
1480 current = info.name;
1484 if (!match && !list.empty())
1485 current = list[0].value;
1488 void CLangInfo::SettingOptionsSpeedUnitsFiller(const SettingConstPtr& setting,
1489 std::vector<StringSettingOption>& list,
1490 std::string& current,
1491 void* data)
1493 bool match = false;
1494 const std::string& speedUnitSetting = std::static_pointer_cast<const CSettingString>(setting)->GetValue();
1496 list.emplace_back(
1497 StringUtils::Format(g_localizeStrings.Get(20035),
1498 GetSpeedUnitString(g_langInfo.m_currentRegion->m_speedUnit)),
1499 SETTING_REGIONAL_DEFAULT);
1500 if (speedUnitSetting == SETTING_REGIONAL_DEFAULT)
1502 match = true;
1503 current = SETTING_REGIONAL_DEFAULT;
1506 for (const SpeedInfo& info : speedInfo)
1508 list.emplace_back(GetSpeedUnitString(info.unit), info.name);
1510 if (!match && speedUnitSetting == info.name)
1512 match = true;
1513 current = info.name;
1517 if (!match && !list.empty())
1518 current = list[0].value;
1521 void CLangInfo::AddLanguages(std::vector<StringSettingOption> &list)
1523 std::vector<std::string> languages = g_LangCodeExpander.GetLanguageNames(
1524 CLangCodeExpander::ISO_639_1, CLangCodeExpander::LANG_LIST::INCLUDE_ADDONS_USERDEFINED);
1526 for (const auto& language : languages)
1527 list.emplace_back(language, language);