diff options
Diffstat (limited to 'xbmc/LangInfo.cpp')
-rw-r--r-- | xbmc/LangInfo.cpp | 1528 |
1 files changed, 1528 insertions, 0 deletions
diff --git a/xbmc/LangInfo.cpp b/xbmc/LangInfo.cpp new file mode 100644 index 0000000..db85aa5 --- /dev/null +++ b/xbmc/LangInfo.cpp @@ -0,0 +1,1528 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "LangInfo.h" + +#include "ServiceBroker.h" +#include "XBDateTime.h" +#include "addons/AddonInstaller.h" +#include "addons/AddonManager.h" +#include "addons/LanguageResource.h" +#include "addons/RepositoryUpdater.h" +#include "addons/addoninfo/AddonType.h" +#include "guilib/LocalizeStrings.h" +#include "messaging/ApplicationMessenger.h" +#include "pvr/PVRManager.h" +#include "settings/AdvancedSettings.h" +#include "settings/Settings.h" +#include "settings/SettingsComponent.h" +#include "settings/lib/Setting.h" +#include "settings/lib/SettingDefinitions.h" +#include "utils/CharsetConverter.h" +#include "utils/LangCodeExpander.h" +#include "utils/StringUtils.h" +#include "utils/URIUtils.h" +#include "utils/XBMCTinyXML.h" +#include "utils/XMLUtils.h" +#include "utils/log.h" +#include "weather/WeatherManager.h" + +#include <algorithm> +#include <stdexcept> + +using namespace PVR; + +static std::string shortDateFormats[] = { + // short date formats using "/" + "DD/MM/YYYY", + "MM/DD/YYYY", + "YYYY/MM/DD", + "D/M/YYYY", + // short date formats using "-" + "DD-MM-YYYY", + "MM-DD-YYYY", + "YYYY-MM-DD", + "YYYY-M-D", + // short date formats using "." + "DD.MM.YYYY", + "DD.M.YYYY", + "D.M.YYYY", + "D. M. YYYY", + "YYYY.MM.DD" +}; + +static std::string longDateFormats[] = { + "DDDD, D MMMM YYYY", + "DDDD, DD MMMM YYYY", + "DDDD, D. MMMM YYYY", + "DDDD, DD. MMMM YYYY", + "DDDD, MMMM D, YYYY", + "DDDD, MMMM DD, YYYY", + "DDDD D MMMM YYYY", + "DDDD DD MMMM YYYY", + "DDDD D. MMMM YYYY", + "DDDD DD. MMMM YYYY", + "D. MMMM YYYY", + "DD. MMMM YYYY", + "D. MMMM. YYYY", + "DD. MMMM. YYYY", + "YYYY. MMMM. D" +}; + +#define TIME_FORMAT_MM_SS ":mm:ss" +#define TIME_FORMAT_SINGLE_12 "h" TIME_FORMAT_MM_SS +#define TIME_FORMAT_DOUBLE_12 "hh" TIME_FORMAT_MM_SS +#define TIME_FORMAT_SINGLE_24 "H" TIME_FORMAT_MM_SS +#define TIME_FORMAT_DOUBLE_24 "HH" TIME_FORMAT_MM_SS + +#define TIME_FORMAT_12HOURS "12hours" +#define TIME_FORMAT_24HOURS "24hours" + +typedef struct TemperatureInfo { + CTemperature::Unit unit; + std::string name; +} TemperatureInfo; + +static TemperatureInfo temperatureInfo[] = { + { CTemperature::UnitFahrenheit, "f" }, + { CTemperature::UnitKelvin, "k" }, + { CTemperature::UnitCelsius, "c" }, + { CTemperature::UnitReaumur, "re" }, + { CTemperature::UnitRankine, "ra" }, + { CTemperature::UnitRomer, "ro" }, + { CTemperature::UnitDelisle, "de" }, + { CTemperature::UnitNewton, "n" } +}; + +#define TEMP_UNIT_STRINGS 20027 + +typedef struct SpeedInfo { + CSpeed::Unit unit; + std::string name; +} SpeedInfo; + +static SpeedInfo speedInfo[] = { + { CSpeed::UnitKilometresPerHour, "kmh" }, + { CSpeed::UnitMetresPerMinute, "mpmin" }, + { CSpeed::UnitMetresPerSecond, "mps" }, + { CSpeed::UnitFeetPerHour, "fth" }, + { CSpeed::UnitFeetPerMinute, "ftm" }, + { CSpeed::UnitFeetPerSecond, "fts" }, + { CSpeed::UnitMilesPerHour, "mph" }, + { CSpeed::UnitKnots, "kts" }, + { CSpeed::UnitBeaufort, "beaufort" }, + { CSpeed::UnitInchPerSecond, "inchs" }, + { CSpeed::UnitYardPerSecond, "yards" }, + { CSpeed::UnitFurlongPerFortnight, "fpf" } +}; + +#define SPEED_UNIT_STRINGS 20200 + +#define SETTING_REGIONAL_DEFAULT "regional" + +static std::string ToTimeFormat(bool use24HourClock, bool singleHour, bool meridiem) +{ + if (use24HourClock) + return singleHour ? TIME_FORMAT_SINGLE_24 : TIME_FORMAT_DOUBLE_24; + + if (!meridiem) + return singleHour ? TIME_FORMAT_SINGLE_12 : TIME_FORMAT_DOUBLE_12; + + return StringUtils::Format(g_localizeStrings.Get(12382), ToTimeFormat(false, singleHour, false)); +} + +static std::string ToSettingTimeFormat(const CDateTime& time, const std::string& timeFormat) +{ + return StringUtils::Format(g_localizeStrings.Get(20036), + time.GetAsLocalizedTime(timeFormat, true), timeFormat); +} + +static CTemperature::Unit StringToTemperatureUnit(const std::string& temperatureUnit) +{ + std::string unit(temperatureUnit); + StringUtils::ToLower(unit); + + for (const TemperatureInfo& info : temperatureInfo) + { + if (info.name == unit) + return info.unit; + } + + return CTemperature::UnitCelsius; +} + +static CSpeed::Unit StringToSpeedUnit(const std::string& speedUnit) +{ + std::string unit(speedUnit); + StringUtils::ToLower(unit); + + for (const SpeedInfo& info : speedInfo) + { + if (info.name == unit) + return info.unit; + } + + return CSpeed::UnitKilometresPerHour; +} + +struct SortLanguage +{ + bool operator()(const StringSettingOption &left, const StringSettingOption &right) const + { + std::string strLeft = left.label; + std::string strRight = right.label; + StringUtils::ToLower(strLeft); + StringUtils::ToLower(strRight); + + return strLeft.compare(strRight) < 0; + } +}; + +CLangInfo::CRegion::CRegion() +{ + SetDefaults(); +} + +void CLangInfo::CRegion::SetDefaults() +{ + m_strName="N/A"; + m_strLangLocaleName = "English"; + m_strLangLocaleCodeTwoChar = "en"; + + m_strDateFormatShort="DD/MM/YYYY"; + m_strDateFormatLong="DDDD, D MMMM YYYY"; + m_strTimeFormat="HH:mm:ss"; + m_tempUnit = CTemperature::UnitCelsius; + m_speedUnit = CSpeed::UnitKilometresPerHour; + m_strTimeZone.clear(); +} + +void CLangInfo::CRegion::SetTemperatureUnit(const std::string& strUnit) +{ + m_tempUnit = StringToTemperatureUnit(strUnit); +} + +void CLangInfo::CRegion::SetSpeedUnit(const std::string& strUnit) +{ + m_speedUnit = StringToSpeedUnit(strUnit); +} + +void CLangInfo::CRegion::SetTimeZone(const std::string& strTimeZone) +{ + m_strTimeZone = strTimeZone; +} + +void CLangInfo::CRegion::SetGlobalLocale() +{ + std::string strLocale; + if (m_strRegionLocaleName.length() > 0) + { +#ifdef TARGET_WINDOWS + std::string strLang, strRegion; + g_LangCodeExpander.ConvertToISO6391(m_strLangLocaleName, strLang); + g_LangCodeExpander.ConvertToISO6391(m_strRegionLocaleName, strRegion); + strLocale = strLang + "-" + strRegion; +#else + strLocale = m_strLangLocaleName + "_" + m_strRegionLocaleName; +#endif +#ifdef TARGET_POSIX + strLocale += ".UTF-8"; +#endif + } + g_langInfo.m_originalLocale = std::locale(std::locale::classic(), new custom_numpunct(m_cDecimalSep, m_cThousandsSep, m_strGrouping)); + + CLog::Log(LOGDEBUG, "trying to set locale to {}", strLocale); + + // We need to set the locale to only change the collate. Otherwise, + // decimal separator is changed depending of the current language + // (ie. "," in French or Dutch instead of "."). This breaks atof() and + // others similar functions. +#if !(defined(TARGET_FREEBSD) || defined(TARGET_DARWIN_OSX) || defined(__UCLIBC__)) + // on FreeBSD, darwin and uClibc-based systems libstdc++ is compiled with + // "generic" locale support + std::locale current_locale = std::locale::classic(); // C-Locale + try + { + std::locale lcl = std::locale(strLocale.c_str()); + strLocale = lcl.name(); + current_locale = current_locale.combine< std::collate<wchar_t> >(lcl); + current_locale = current_locale.combine< std::ctype<wchar_t> >(lcl); + current_locale = current_locale.combine< std::time_get<wchar_t> >(lcl); + current_locale = current_locale.combine< std::time_put<wchar_t> >(lcl); + + assert(std::use_facet< std::numpunct<char> >(current_locale).decimal_point() == '.'); + + } catch(...) { + current_locale = std::locale::classic(); + strLocale = "C"; + } + + g_langInfo.m_systemLocale = current_locale; //! @todo move to CLangInfo class + g_langInfo.m_collationtype = 0; + std::locale::global(current_locale); +#endif + +#ifndef TARGET_WINDOWS + if (setlocale(LC_COLLATE, strLocale.c_str()) == NULL || + setlocale(LC_CTYPE, strLocale.c_str()) == NULL || + setlocale(LC_TIME, strLocale.c_str()) == NULL) + { + strLocale = "C"; + setlocale(LC_COLLATE, strLocale.c_str()); + setlocale(LC_CTYPE, strLocale.c_str()); + setlocale(LC_TIME, strLocale.c_str()); + } +#else + std::wstring strLocaleW; + g_charsetConverter.utf8ToW(strLocale, strLocaleW); + if (_wsetlocale(LC_COLLATE, strLocaleW.c_str()) == NULL || + _wsetlocale(LC_CTYPE, strLocaleW.c_str()) == NULL || + _wsetlocale(LC_TIME, strLocaleW.c_str()) == NULL) + { + strLocale = "C"; + strLocaleW = L"C"; + _wsetlocale(LC_COLLATE, strLocaleW.c_str()); + _wsetlocale(LC_CTYPE, strLocaleW.c_str()); + _wsetlocale(LC_TIME, strLocaleW.c_str()); + } +#endif + + g_charsetConverter.resetSystemCharset(); + CLog::Log(LOGINFO, "global locale set to {}", strLocale); + +#ifdef TARGET_ANDROID + // Force UTF8 for, e.g., vsnprintf + setlocale(LC_ALL, "C.UTF-8"); +#endif +} + +CLangInfo::CLangInfo() +{ + SetDefaults(); + m_shortDateFormat = m_defaultRegion.m_strDateFormatShort; + m_longDateFormat = m_defaultRegion.m_strDateFormatLong; + m_timeFormat = m_defaultRegion.m_strTimeFormat; + m_use24HourClock = DetermineUse24HourClockFromTimeFormat(m_defaultRegion.m_strTimeFormat); + m_temperatureUnit = m_defaultRegion.m_tempUnit; + m_speedUnit = m_defaultRegion.m_speedUnit; + m_collationtype = 0; +} + +CLangInfo::~CLangInfo() = default; + +void CLangInfo::OnSettingChanged(const std::shared_ptr<const CSetting>& setting) +{ + if (setting == NULL) + return; + + auto settingsComponent = CServiceBroker::GetSettingsComponent(); + if (!settingsComponent) + return; + + auto settings = settingsComponent->GetSettings(); + if (!settings) + return; + + const std::string &settingId = setting->GetId(); + if (settingId == CSettings::SETTING_LOCALE_AUDIOLANGUAGE) + SetAudioLanguage(std::static_pointer_cast<const CSettingString>(setting)->GetValue()); + else if (settingId == CSettings::SETTING_LOCALE_SUBTITLELANGUAGE) + SetSubtitleLanguage(std::static_pointer_cast<const CSettingString>(setting)->GetValue()); + else if (settingId == CSettings::SETTING_LOCALE_LANGUAGE) + { + if (!SetLanguage(std::static_pointer_cast<const CSettingString>(setting)->GetValue())) + { + auto langsetting = settings->GetSetting(CSettings::SETTING_LOCALE_LANGUAGE); + if (!langsetting) + { + CLog::Log(LOGERROR, "Failed to load setting for: {}", CSettings::SETTING_LOCALE_LANGUAGE); + return; + } + + std::static_pointer_cast<CSettingString>(langsetting)->Reset(); + } + } + else if (settingId == CSettings::SETTING_LOCALE_COUNTRY) + SetCurrentRegion(std::static_pointer_cast<const CSettingString>(setting)->GetValue()); + else if (settingId == CSettings::SETTING_LOCALE_SHORTDATEFORMAT) + SetShortDateFormat(std::static_pointer_cast<const CSettingString>(setting)->GetValue()); + else if (settingId == CSettings::SETTING_LOCALE_LONGDATEFORMAT) + SetLongDateFormat(std::static_pointer_cast<const CSettingString>(setting)->GetValue()); + else if (settingId == CSettings::SETTING_LOCALE_TIMEFORMAT) + SetTimeFormat(std::static_pointer_cast<const CSettingString>(setting)->GetValue()); + else if (settingId == CSettings::SETTING_LOCALE_USE24HOURCLOCK) + { + Set24HourClock(std::static_pointer_cast<const CSettingString>(setting)->GetValue()); + + // update the time format + settings->SetString(CSettings::SETTING_LOCALE_TIMEFORMAT, + PrepareTimeFormat(GetTimeFormat(), m_use24HourClock)); + } + else if (settingId == CSettings::SETTING_LOCALE_TEMPERATUREUNIT) + SetTemperatureUnit(std::static_pointer_cast<const CSettingString>(setting)->GetValue()); + else if (settingId == CSettings::SETTING_LOCALE_SPEEDUNIT) + SetSpeedUnit(std::static_pointer_cast<const CSettingString>(setting)->GetValue()); +} + +void CLangInfo::OnSettingsLoaded() +{ + // set the temperature and speed units based on the settings + const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings(); + SetShortDateFormat(settings->GetString(CSettings::SETTING_LOCALE_SHORTDATEFORMAT)); + SetLongDateFormat(settings->GetString(CSettings::SETTING_LOCALE_LONGDATEFORMAT)); + Set24HourClock(settings->GetString(CSettings::SETTING_LOCALE_USE24HOURCLOCK)); + SetTimeFormat(settings->GetString(CSettings::SETTING_LOCALE_TIMEFORMAT)); + SetTemperatureUnit(settings->GetString(CSettings::SETTING_LOCALE_TEMPERATUREUNIT)); + SetSpeedUnit(settings->GetString(CSettings::SETTING_LOCALE_SPEEDUNIT)); +} + +bool CLangInfo::Load(const std::string& strLanguage) +{ + SetDefaults(); + + std::string strFileName = GetLanguageInfoPath(strLanguage); + + CXBMCTinyXML xmlDoc; + if (!xmlDoc.LoadFile(strFileName)) + { + CLog::Log(LOGERROR, "unable to load {}: {} at line {}", strFileName, xmlDoc.ErrorDesc(), + xmlDoc.ErrorRow()); + return false; + } + + // get the matching language addon + m_languageAddon = GetLanguageAddon(strLanguage); + if (m_languageAddon == NULL) + { + CLog::Log(LOGERROR, "Unknown language {}", strLanguage); + return false; + } + + // get some language-specific information from the language addon + m_strGuiCharSet = m_languageAddon->GetGuiCharset(); + m_forceUnicodeFont = m_languageAddon->ForceUnicodeFont(); + m_strSubtitleCharSet = m_languageAddon->GetSubtitleCharset(); + m_strDVDMenuLanguage = m_languageAddon->GetDvdMenuLanguage(); + m_strDVDAudioLanguage = m_languageAddon->GetDvdAudioLanguage(); + m_strDVDSubtitleLanguage = m_languageAddon->GetDvdSubtitleLanguage(); + m_sortTokens = m_languageAddon->GetSortTokens(); + + TiXmlElement* pRootElement = xmlDoc.RootElement(); + if (pRootElement->ValueStr() != "language") + { + CLog::Log(LOGERROR, "{} Doesn't contain <language>", strFileName); + return false; + } + + if (pRootElement->Attribute("locale")) + m_defaultRegion.m_strLangLocaleName = pRootElement->Attribute("locale"); + +#ifdef TARGET_WINDOWS + // Windows need 3 chars isolang code + if (m_defaultRegion.m_strLangLocaleName.length() == 2) + { + if (!g_LangCodeExpander.ConvertISO6391ToISO6392B(m_defaultRegion.m_strLangLocaleName, m_defaultRegion.m_strLangLocaleName, true)) + m_defaultRegion.m_strLangLocaleName = ""; + } + + if (!g_LangCodeExpander.ConvertWindowsLanguageCodeToISO6392B(m_defaultRegion.m_strLangLocaleName, m_languageCodeGeneral)) + m_languageCodeGeneral = ""; +#else + if (m_defaultRegion.m_strLangLocaleName.length() != 3) + { + if (!g_LangCodeExpander.ConvertToISO6392B(m_defaultRegion.m_strLangLocaleName, m_languageCodeGeneral)) + m_languageCodeGeneral = ""; + } + else + m_languageCodeGeneral = m_defaultRegion.m_strLangLocaleName; +#endif + + std::string tmp; + if (g_LangCodeExpander.ConvertToISO6391(m_defaultRegion.m_strLangLocaleName, tmp)) + m_defaultRegion.m_strLangLocaleCodeTwoChar = tmp; + + const TiXmlNode *pRegions = pRootElement->FirstChild("regions"); + if (pRegions && !pRegions->NoChildren()) + { + const TiXmlElement *pRegion=pRegions->FirstChildElement("region"); + while (pRegion) + { + CRegion region(m_defaultRegion); + region.m_strName = XMLUtils::GetAttribute(pRegion, "name"); + if (region.m_strName.empty()) + region.m_strName=g_localizeStrings.Get(10005); // Not available + + if (pRegion->Attribute("locale")) + region.m_strRegionLocaleName = pRegion->Attribute("locale"); + +#ifdef TARGET_WINDOWS + // Windows need 3 chars regions code + if (region.m_strRegionLocaleName.length() == 2) + { + if (!g_LangCodeExpander.ConvertISO31661Alpha2ToISO31661Alpha3(region.m_strRegionLocaleName, region.m_strRegionLocaleName)) + region.m_strRegionLocaleName = ""; + } +#endif + + const TiXmlNode *pDateLong=pRegion->FirstChild("datelong"); + if (pDateLong && !pDateLong->NoChildren()) + region.m_strDateFormatLong=pDateLong->FirstChild()->ValueStr(); + + const TiXmlNode *pDateShort=pRegion->FirstChild("dateshort"); + if (pDateShort && !pDateShort->NoChildren()) + region.m_strDateFormatShort=pDateShort->FirstChild()->ValueStr(); + + const TiXmlElement *pTime=pRegion->FirstChildElement("time"); + if (pTime && !pTime->NoChildren()) + { + region.m_strTimeFormat=pTime->FirstChild()->Value(); + region.m_strMeridiemSymbols[MeridiemSymbolAM] = XMLUtils::GetAttribute(pTime, "symbolAM"); + region.m_strMeridiemSymbols[MeridiemSymbolPM] = XMLUtils::GetAttribute(pTime, "symbolPM"); + } + + const TiXmlNode *pTempUnit=pRegion->FirstChild("tempunit"); + if (pTempUnit && !pTempUnit->NoChildren()) + region.SetTemperatureUnit(pTempUnit->FirstChild()->ValueStr()); + + const TiXmlNode *pSpeedUnit=pRegion->FirstChild("speedunit"); + if (pSpeedUnit && !pSpeedUnit->NoChildren()) + region.SetSpeedUnit(pSpeedUnit->FirstChild()->ValueStr()); + + const TiXmlNode *pTimeZone=pRegion->FirstChild("timezone"); + if (pTimeZone && !pTimeZone->NoChildren()) + region.SetTimeZone(pTimeZone->FirstChild()->ValueStr()); + + const TiXmlElement *pThousandsSep = pRegion->FirstChildElement("thousandsseparator"); + if (pThousandsSep) + { + if (!pThousandsSep->NoChildren()) + { + region.m_cThousandsSep = pThousandsSep->FirstChild()->Value()[0]; + if (pThousandsSep->Attribute("groupingformat")) + region.m_strGrouping = StringUtils::BinaryStringToString(pThousandsSep->Attribute("groupingformat")); + else + region.m_strGrouping = "\3"; + } + } + else + { + region.m_cThousandsSep = ','; + region.m_strGrouping = "\3"; + } + + const TiXmlElement *pDecimalSep = pRegion->FirstChildElement("decimalseparator"); + if (pDecimalSep) + { + if (!pDecimalSep->NoChildren()) + region.m_cDecimalSep = pDecimalSep->FirstChild()->Value()[0]; + } + else + region.m_cDecimalSep = '.'; + + m_regions.insert(PAIR_REGIONS(region.m_strName, region)); + + pRegion=pRegion->NextSiblingElement("region"); + } + + const std::string& strName = CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_LOCALE_COUNTRY); + SetCurrentRegion(strName); + } + g_charsetConverter.reinitCharsetsFromSettings(); + + return true; +} + +std::string CLangInfo::GetLanguagePath(const std::string &language) +{ + if (language.empty()) + return ""; + + std::string addonId = ADDON::CLanguageResource::GetAddonId(language); + + std::string path = URIUtils::AddFileToFolder(GetLanguagePath(), addonId); + URIUtils::AddSlashAtEnd(path); + + return path; +} + +std::string CLangInfo::GetLanguageInfoPath(const std::string &language) +{ + if (language.empty()) + return ""; + + return URIUtils::AddFileToFolder(GetLanguagePath(language), "langinfo.xml"); +} + +bool CLangInfo::UseLocaleCollation() +{ + if (m_collationtype == 0) + { + // Determine collation to use. When using MySQL/MariaDB or a platform that does not support + // locale language collation then use accent folding internal equivalent of utf8_general_ci + m_collationtype = 1; + if (!StringUtils::EqualsNoCase( + CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_databaseMusic.type, + "mysql") && + !StringUtils::EqualsNoCase( + CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_databaseVideo.type, + "mysql") && + CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_useLocaleCollation) + { + // Check that locale collation facet is implemented on the platform + const std::collate<wchar_t>& coll = std::use_facet<std::collate<wchar_t>>(m_systemLocale); + wchar_t lc = L'z'; + wchar_t rc = 0x00E2; // Latin small letter a with circumflex + int comp_result = coll.compare(&lc, &lc + 1, &rc, &rc + 1); + if (comp_result > 0) + // Latin small letter a with circumflex put before z - collation works + m_collationtype = 2; + } + } + return m_collationtype == 2; +} + +void CLangInfo::LoadTokens(const TiXmlNode* pTokens, std::set<std::string>& vecTokens) +{ + if (pTokens && !pTokens->NoChildren()) + { + const TiXmlElement *pToken = pTokens->FirstChildElement("token"); + while (pToken) + { + std::string strSep= " ._"; + if (pToken->Attribute("separators")) + strSep = pToken->Attribute("separators"); + if (pToken->FirstChild() && pToken->FirstChild()->Value()) + { + if (strSep.empty()) + vecTokens.insert(pToken->FirstChild()->ValueStr()); + else + for (unsigned int i=0;i<strSep.size();++i) + vecTokens.insert(pToken->FirstChild()->ValueStr() + strSep[i]); + } + pToken = pToken->NextSiblingElement(); + } + } +} + +void CLangInfo::SetDefaults() +{ + m_regions.clear(); + + //Reset default region + m_defaultRegion.SetDefaults(); + + // Set the default region, we may be unable to load langinfo.xml + m_currentRegion = &m_defaultRegion; + + m_systemLocale = std::locale::classic(); + + m_forceUnicodeFont = false; + m_strGuiCharSet = "CP1252"; + m_strSubtitleCharSet = "CP1252"; + m_strDVDMenuLanguage = "en"; + m_strDVDAudioLanguage = "en"; + m_strDVDSubtitleLanguage = "en"; + m_sortTokens.clear(); + + m_languageCodeGeneral = "eng"; +} + +std::string CLangInfo::GetGuiCharSet() const +{ + std::shared_ptr<CSettingString> charsetSetting = std::static_pointer_cast<CSettingString>(CServiceBroker::GetSettingsComponent()->GetSettings()->GetSetting(CSettings::SETTING_LOCALE_CHARSET)); + if (charsetSetting == NULL || charsetSetting->IsDefault()) + return m_strGuiCharSet; + + return charsetSetting->GetValue(); +} + +std::string CLangInfo::GetSubtitleCharSet() const +{ + std::shared_ptr<CSettingString> charsetSetting = std::static_pointer_cast<CSettingString>(CServiceBroker::GetSettingsComponent()->GetSettings()->GetSetting(CSettings::SETTING_SUBTITLES_CHARSET)); + if (charsetSetting->IsDefault()) + return m_strSubtitleCharSet; + + return charsetSetting->GetValue(); +} + +void CLangInfo::GetAddonsLanguageCodes(std::map<std::string, std::string>& languages) +{ + ADDON::VECADDONS addons; + CServiceBroker::GetAddonMgr().GetAddons(addons, ADDON::AddonType::RESOURCE_LANGUAGE); + for (const auto& addon : addons) + { + const LanguageResourcePtr langAddon = + std::dynamic_pointer_cast<ADDON::CLanguageResource>(addon); + std::string langCode{langAddon->GetLocale().ToShortStringLC()}; + StringUtils::Replace(langCode, '_', '-'); + languages.emplace(langCode, addon->Name()); + } +} + +LanguageResourcePtr CLangInfo::GetLanguageAddon(const std::string& locale /* = "" */) const +{ + if (locale.empty() || + (m_languageAddon != NULL && (locale.compare(m_languageAddon->ID()) == 0 || m_languageAddon->GetLocale().Equals(locale)))) + return m_languageAddon; + + std::string addonId = ADDON::CLanguageResource::GetAddonId(locale); + if (addonId.empty()) + addonId = CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_LOCALE_LANGUAGE); + + ADDON::AddonPtr addon; + if (CServiceBroker::GetAddonMgr().GetAddon(addonId, addon, ADDON::AddonType::RESOURCE_LANGUAGE, + ADDON::OnlyEnabled::CHOICE_YES) && + addon != NULL) + return std::dynamic_pointer_cast<ADDON::CLanguageResource>(addon); + + return NULL; +} + +std::string CLangInfo::ConvertEnglishNameToAddonLocale(const std::string& langName) +{ + ADDON::VECADDONS addons; + CServiceBroker::GetAddonMgr().GetAddons(addons, ADDON::AddonType::RESOURCE_LANGUAGE); + for (const auto& addon : addons) + { + if (StringUtils::CompareNoCase(addon->Name(), langName) == 0) + { + const LanguageResourcePtr langAddon = + std::dynamic_pointer_cast<ADDON::CLanguageResource>(addon); + std::string locale = langAddon->GetLocale().ToShortStringLC(); + StringUtils::Replace(locale, '_', '-'); + return locale; + } + } + return ""; +} + +std::string CLangInfo::GetEnglishLanguageName(const std::string& locale /* = "" */) const +{ + LanguageResourcePtr addon = GetLanguageAddon(locale); + if (addon == NULL) + return ""; + + return addon->Name(); +} + +bool CLangInfo::SetLanguage(std::string language /* = "" */, bool reloadServices /* = true */) +{ + if (language.empty()) + language = CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_LOCALE_LANGUAGE); + + auto& addonMgr = CServiceBroker::GetAddonMgr(); + ADDON::AddonPtr addon; + + // Find the chosen language add-on if it's enabled + if (!addonMgr.GetAddon(language, addon, ADDON::AddonType::RESOURCE_LANGUAGE, + ADDON::OnlyEnabled::CHOICE_YES)) + { + if (!addonMgr.IsAddonInstalled(language) || + (addonMgr.IsAddonDisabled(language) && !addonMgr.EnableAddon(language))) + { + CLog::Log(LOGWARNING, + "CLangInfo::{}: could not find or enable language add-on '{}', loading default...", + __func__, language); + language = std::static_pointer_cast<const CSettingString>( + CServiceBroker::GetSettingsComponent()->GetSettings()->GetSetting( + CSettings::SETTING_LOCALE_LANGUAGE)) + ->GetDefault(); + + if (!addonMgr.GetAddon(language, addon, ADDON::AddonType::RESOURCE_LANGUAGE, + ADDON::OnlyEnabled::CHOICE_NO)) + { + CLog::Log(LOGFATAL, "CLangInfo::{}: could not find default language add-on '{}'", __func__, + language); + return false; + } + } + } + + CLog::Log(LOGINFO, "CLangInfo: loading {} language information...", language); + if (!Load(language)) + { + CLog::LogF(LOGFATAL, "CLangInfo: failed to load {} language information", language); + return false; + } + + CLog::Log(LOGINFO, "CLangInfo: loading {} language strings...", language); + if (!g_localizeStrings.Load(GetLanguagePath(), language)) + { + CLog::LogF(LOGFATAL, "CLangInfo: failed to load {} language strings", language); + return false; + } + + ADDON::VECADDONS addons; + if (CServiceBroker::GetAddonMgr().GetInstalledAddons(addons)) + { + const std::string locale = CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_LOCALE_LANGUAGE); + for (const auto& addon : addons) + { + const std::string path = URIUtils::AddFileToFolder(addon->Path(), "resources", "language/"); + g_localizeStrings.LoadAddonStrings(path, locale, addon->ID()); + } + } + + if (reloadServices) + { + // also tell our weather and skin to reload as these are localized + CServiceBroker::GetWeatherManager().Refresh(); + CServiceBroker::GetPVRManager().LocalizationChanged(); + CServiceBroker::GetAppMessenger()->PostMsg(TMSG_EXECUTE_BUILT_IN, -1, -1, nullptr, + "ReloadSkin"); + } + + return true; +} + +// three char language code (not win32 specific) +const std::string& CLangInfo::GetAudioLanguage() const +{ + if (!m_audioLanguage.empty()) + return m_audioLanguage; + + return m_languageCodeGeneral; +} + +void CLangInfo::SetAudioLanguage(const std::string& language) +{ + if (language.empty() + || StringUtils::EqualsNoCase(language, "default") + || StringUtils::EqualsNoCase(language, "original") + || StringUtils::EqualsNoCase(language, "mediadefault") + || !g_LangCodeExpander.ConvertToISO6392B(language, m_audioLanguage)) + m_audioLanguage.clear(); +} + +// three char language code (not win32 specific) +const std::string& CLangInfo::GetSubtitleLanguage() const +{ + if (!m_subtitleLanguage.empty()) + return m_subtitleLanguage; + + return m_languageCodeGeneral; +} + +void CLangInfo::SetSubtitleLanguage(const std::string& language) +{ + if (language.empty() + || StringUtils::EqualsNoCase(language, "default") + || StringUtils::EqualsNoCase(language, "original") + || !g_LangCodeExpander.ConvertToISO6392B(language, m_subtitleLanguage)) + m_subtitleLanguage.clear(); +} + +// two character codes as defined in ISO639 +const std::string CLangInfo::GetDVDMenuLanguage() const +{ + std::string code; + if (!g_LangCodeExpander.ConvertToISO6391(m_currentRegion->m_strLangLocaleName, code)) + code = m_strDVDMenuLanguage; + + return code; +} + +// two character codes as defined in ISO639 +const std::string CLangInfo::GetDVDAudioLanguage() const +{ + std::string code; + if (!g_LangCodeExpander.ConvertToISO6391(m_audioLanguage, code)) + code = m_strDVDAudioLanguage; + + return code; +} + +// two character codes as defined in ISO639 +const std::string CLangInfo::GetDVDSubtitleLanguage() const +{ + std::string code; + if (!g_LangCodeExpander.ConvertToISO6391(m_subtitleLanguage, code)) + code = m_strDVDSubtitleLanguage; + + return code; +} + +const CLocale& CLangInfo::GetLocale() const +{ + LanguageResourcePtr language = GetLanguageAddon(); + if (language != NULL) + return language->GetLocale(); + + return CLocale::Empty; +} + +const std::string& CLangInfo::GetRegionLocale() const +{ + return m_currentRegion->m_strRegionLocaleName; +} + +const std::locale& CLangInfo::GetOriginalLocale() const +{ + return m_originalLocale; +} + +// Returns the format string for the date of the current language +const std::string& CLangInfo::GetDateFormat(bool bLongDate /* = false */) const +{ + if (bLongDate) + return GetLongDateFormat(); + + return GetShortDateFormat(); +} + +void CLangInfo::SetDateFormat(const std::string& dateFormat, bool bLongDate /* = false */) +{ + if (bLongDate) + SetLongDateFormat(dateFormat); + else + SetShortDateFormat(dateFormat); +} + +const std::string& CLangInfo::GetShortDateFormat() const +{ + return m_shortDateFormat; +} + +void CLangInfo::SetShortDateFormat(const std::string& shortDateFormat) +{ + std::string newShortDateFormat = shortDateFormat; + if (shortDateFormat == SETTING_REGIONAL_DEFAULT) + newShortDateFormat = m_currentRegion->m_strDateFormatShort; + + m_shortDateFormat = newShortDateFormat; +} + +const std::string& CLangInfo::GetLongDateFormat() const +{ + return m_longDateFormat; +} + +void CLangInfo::SetLongDateFormat(const std::string& longDateFormat) +{ + std::string newLongDateFormat = longDateFormat; + if (longDateFormat == SETTING_REGIONAL_DEFAULT) + newLongDateFormat = m_currentRegion->m_strDateFormatShort; + + m_longDateFormat = newLongDateFormat; +} + +// Returns the format string for the time of the current language +const std::string& CLangInfo::GetTimeFormat() const +{ + return m_timeFormat; +} + +void CLangInfo::SetTimeFormat(const std::string& timeFormat) +{ + std::string newTimeFormat = timeFormat; + if (timeFormat == SETTING_REGIONAL_DEFAULT) + newTimeFormat = m_currentRegion->m_strTimeFormat; + + m_timeFormat = PrepareTimeFormat(newTimeFormat, m_use24HourClock); +} + +bool CLangInfo::Use24HourClock() const +{ + return m_use24HourClock; +} + +void CLangInfo::Set24HourClock(bool use24HourClock) +{ + m_use24HourClock = use24HourClock; +} + +void CLangInfo::Set24HourClock(const std::string& str24HourClock) +{ + bool use24HourClock = false; + if (str24HourClock == TIME_FORMAT_12HOURS) + use24HourClock = false; + else if (str24HourClock == TIME_FORMAT_24HOURS) + use24HourClock = true; + else if (str24HourClock == SETTING_REGIONAL_DEFAULT) + { + Set24HourClock(m_currentRegion->m_strTimeFormat); + return; + } + else + use24HourClock = DetermineUse24HourClockFromTimeFormat(str24HourClock); + + if (m_use24HourClock == use24HourClock) + return; + + m_use24HourClock = use24HourClock; +} + +const std::string& CLangInfo::GetTimeZone() const +{ + return m_currentRegion->m_strTimeZone; +} + +// Returns the AM/PM symbol of the current language +const std::string& CLangInfo::GetMeridiemSymbol(MeridiemSymbol symbol) const +{ + // nothing to return if we use 24-hour clock + if (m_use24HourClock) + return StringUtils::Empty; + + return MeridiemSymbolToString(symbol); +} + +const std::string& CLangInfo::MeridiemSymbolToString(MeridiemSymbol symbol) +{ + switch (symbol) + { + case MeridiemSymbolAM: + return g_localizeStrings.Get(378); + + case MeridiemSymbolPM: + return g_localizeStrings.Get(379); + + default: + break; + } + + return StringUtils::Empty; +} + +// Fills the array with the region names available for this language +void CLangInfo::GetRegionNames(std::vector<std::string>& array) +{ + for (const auto ®ion : m_regions) + { + std::string strName=region.first; + if (strName=="N/A") + strName=g_localizeStrings.Get(10005); // Not available + array.emplace_back(std::move(strName)); + } +} + +// Set the current region by its name, names from GetRegionNames() are valid. +// If the region is not found the first available region is set. +void CLangInfo::SetCurrentRegion(const std::string& strName) +{ + ITMAPREGIONS it=m_regions.find(strName); + if (it!=m_regions.end()) + m_currentRegion=&it->second; + else if (!m_regions.empty()) + m_currentRegion=&m_regions.begin()->second; + else + m_currentRegion=&m_defaultRegion; + + m_currentRegion->SetGlobalLocale(); + + const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings(); + if (settings->GetString(CSettings::SETTING_LOCALE_SHORTDATEFORMAT) == SETTING_REGIONAL_DEFAULT) + SetShortDateFormat(m_currentRegion->m_strDateFormatShort); + if (settings->GetString(CSettings::SETTING_LOCALE_LONGDATEFORMAT) == SETTING_REGIONAL_DEFAULT) + SetLongDateFormat(m_currentRegion->m_strDateFormatLong); + if (settings->GetString(CSettings::SETTING_LOCALE_USE24HOURCLOCK) == SETTING_REGIONAL_DEFAULT) + { + Set24HourClock(m_currentRegion->m_strTimeFormat); + + // update the time format + SetTimeFormat(settings->GetString(CSettings::SETTING_LOCALE_TIMEFORMAT)); + } + if (settings->GetString(CSettings::SETTING_LOCALE_TIMEFORMAT) == SETTING_REGIONAL_DEFAULT) + SetTimeFormat(m_currentRegion->m_strTimeFormat); + if (settings->GetString(CSettings::SETTING_LOCALE_TEMPERATUREUNIT) == SETTING_REGIONAL_DEFAULT) + SetTemperatureUnit(m_currentRegion->m_tempUnit); + if (settings->GetString(CSettings::SETTING_LOCALE_SPEEDUNIT) == SETTING_REGIONAL_DEFAULT) + SetSpeedUnit(m_currentRegion->m_speedUnit); +} + +// Returns the current region set for this language +const std::string& CLangInfo::GetCurrentRegion() const +{ + return m_currentRegion->m_strName; +} + +CTemperature::Unit CLangInfo::GetTemperatureUnit() const +{ + return m_temperatureUnit; +} + +void CLangInfo::SetTemperatureUnit(CTemperature::Unit temperatureUnit) +{ + if (m_temperatureUnit == temperatureUnit) + return; + + m_temperatureUnit = temperatureUnit; + + // refresh weather manager as temperatures need re-translating + // NOTE: this could be called before our service manager is up + if (CServiceBroker::IsServiceManagerUp()) + CServiceBroker::GetWeatherManager().Refresh(); +} + +void CLangInfo::SetTemperatureUnit(const std::string& temperatureUnit) +{ + CTemperature::Unit unit = CTemperature::UnitCelsius; + if (temperatureUnit == SETTING_REGIONAL_DEFAULT) + unit = m_currentRegion->m_tempUnit; + else + unit = StringToTemperatureUnit(temperatureUnit); + + SetTemperatureUnit(unit); +} + +std::string CLangInfo::GetTemperatureAsString(const CTemperature& temperature) const +{ + if (!temperature.IsValid()) + return g_localizeStrings.Get(13205); // "Unknown" + + CTemperature::Unit temperatureUnit = GetTemperatureUnit(); + return StringUtils::Format("{}{}", temperature.ToString(temperatureUnit), + GetTemperatureUnitString()); +} + +// Returns the temperature unit string for the current language +const std::string& CLangInfo::GetTemperatureUnitString() const +{ + return GetTemperatureUnitString(m_temperatureUnit); +} + +const std::string& CLangInfo::GetTemperatureUnitString(CTemperature::Unit temperatureUnit) +{ + return g_localizeStrings.Get(TEMP_UNIT_STRINGS + temperatureUnit); +} + +void CLangInfo::SetSpeedUnit(CSpeed::Unit speedUnit) +{ + if (m_speedUnit == speedUnit) + return; + + m_speedUnit = speedUnit; + + // refresh weather manager as speeds need re-translating + // NOTE: this could be called before our service manager is up + if (CServiceBroker::IsServiceManagerUp()) + CServiceBroker::GetWeatherManager().Refresh(); +} + +void CLangInfo::SetSpeedUnit(const std::string& speedUnit) +{ + CSpeed::Unit unit = CSpeed::UnitKilometresPerHour; + if (speedUnit == SETTING_REGIONAL_DEFAULT) + unit = m_currentRegion->m_speedUnit; + else + unit = StringToSpeedUnit(speedUnit); + + SetSpeedUnit(unit); +} + +CSpeed::Unit CLangInfo::GetSpeedUnit() const +{ + return m_speedUnit; +} + +std::string CLangInfo::GetSpeedAsString(const CSpeed& speed) const +{ + if (!speed.IsValid()) + return g_localizeStrings.Get(13205); // "Unknown" + + return StringUtils::Format("{}{}", speed.ToString(GetSpeedUnit()), GetSpeedUnitString()); +} + +// Returns the speed unit string for the current language +const std::string& CLangInfo::GetSpeedUnitString() const +{ + return GetSpeedUnitString(m_speedUnit); +} + +const std::string& CLangInfo::GetSpeedUnitString(CSpeed::Unit speedUnit) +{ + return g_localizeStrings.Get(SPEED_UNIT_STRINGS + speedUnit); +} + +std::set<std::string> CLangInfo::GetSortTokens() const +{ + std::set<std::string> sortTokens = m_sortTokens; + for (const auto& t : CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_vecTokens) + sortTokens.insert(t); + + return sortTokens; +} + +bool CLangInfo::DetermineUse24HourClockFromTimeFormat(const std::string& timeFormat) +{ + // if the time format contains a "h" it's 12-hour and otherwise 24-hour clock format + return timeFormat.find('h') == std::string::npos; +} + +bool CLangInfo::DetermineUseMeridiemFromTimeFormat(const std::string& timeFormat) +{ + // if the time format contains "xx" it's using meridiem + return timeFormat.find("xx") != std::string::npos; +} + +std::string CLangInfo::PrepareTimeFormat(const std::string& timeFormat, bool use24HourClock) +{ + std::string preparedTimeFormat = timeFormat; + if (use24HourClock) + { + // replace all "h" with "H" + StringUtils::Replace(preparedTimeFormat, 'h', 'H'); + + // remove any "xx" for meridiem + StringUtils::Replace(preparedTimeFormat, "x", ""); + } + else + // replace all "H" with "h" + StringUtils::Replace(preparedTimeFormat, 'H', 'h'); + + StringUtils::Trim(preparedTimeFormat); + + return preparedTimeFormat; +} + +void CLangInfo::SettingOptionsLanguageNamesFiller(const SettingConstPtr& setting, + std::vector<StringSettingOption>& list, + std::string& current, + void* data) +{ + // find languages... + ADDON::VECADDONS addons; + if (!CServiceBroker::GetAddonMgr().GetAddons(addons, ADDON::AddonType::RESOURCE_LANGUAGE)) + return; + + for (const auto &addon : addons) + list.emplace_back(addon->Name(), addon->Name()); + + sort(list.begin(), list.end(), SortLanguage()); +} + +void CLangInfo::SettingOptionsISO6391LanguagesFiller(const SettingConstPtr& setting, + std::vector<StringSettingOption>& list, + std::string& current, + void* data) +{ + std::vector<std::string> languages = g_LangCodeExpander.GetLanguageNames( + CLangCodeExpander::ISO_639_1, CLangCodeExpander::LANG_LIST::INCLUDE_USERDEFINED); + + for (const auto &language : languages) + list.emplace_back(language, language); +} + +void CLangInfo::SettingOptionsAudioStreamLanguagesFiller(const SettingConstPtr& setting, + std::vector<StringSettingOption>& list, + std::string& current, + void* data) +{ + list.emplace_back(g_localizeStrings.Get(307), "mediadefault"); + list.emplace_back(g_localizeStrings.Get(308), "original"); + list.emplace_back(g_localizeStrings.Get(309), "default"); + + AddLanguages(list); +} + +void CLangInfo::SettingOptionsSubtitleStreamLanguagesFiller(const SettingConstPtr& setting, + std::vector<StringSettingOption>& list, + std::string& current, + void* data) +{ + list.emplace_back(g_localizeStrings.Get(231), "none"); + list.emplace_back(g_localizeStrings.Get(13207), "forced_only"); + list.emplace_back(g_localizeStrings.Get(308), "original"); + list.emplace_back(g_localizeStrings.Get(309), "default"); + + AddLanguages(list); +} + +void CLangInfo::SettingOptionsSubtitleDownloadlanguagesFiller( + const SettingConstPtr& setting, + std::vector<StringSettingOption>& list, + std::string& current, + void* data) +{ + list.emplace_back(g_localizeStrings.Get(308), "original"); + list.emplace_back(g_localizeStrings.Get(309), "default"); + + AddLanguages(list); +} + +void CLangInfo::SettingOptionsRegionsFiller(const SettingConstPtr& setting, + std::vector<StringSettingOption>& list, + std::string& current, + void* data) +{ + std::vector<std::string> regions; + g_langInfo.GetRegionNames(regions); + std::sort(regions.begin(), regions.end(), sortstringbyname()); + + bool match = false; + for (unsigned int i = 0; i < regions.size(); ++i) + { + std::string region = regions[i]; + list.emplace_back(region, region); + + if (!match && region == std::static_pointer_cast<const CSettingString>(setting)->GetValue()) + { + match = true; + current = region; + } + } + + if (!match && !regions.empty()) + current = regions[0]; +} + +void CLangInfo::SettingOptionsShortDateFormatsFiller(const SettingConstPtr& setting, + std::vector<StringSettingOption>& list, + std::string& current, + void* data) +{ + bool match = false; + const std::string& shortDateFormatSetting = std::static_pointer_cast<const CSettingString>(setting)->GetValue(); + + CDateTime now = CDateTime::GetCurrentDateTime(); + + list.emplace_back( + StringUtils::Format(g_localizeStrings.Get(20035), + now.GetAsLocalizedDate(g_langInfo.m_currentRegion->m_strDateFormatShort)), + SETTING_REGIONAL_DEFAULT); + if (shortDateFormatSetting == SETTING_REGIONAL_DEFAULT) + { + match = true; + current = SETTING_REGIONAL_DEFAULT; + } + + for (const std::string& shortDateFormat : shortDateFormats) + { + list.emplace_back(now.GetAsLocalizedDate(shortDateFormat), shortDateFormat); + + if (!match && shortDateFormatSetting == shortDateFormat) + { + match = true; + current = shortDateFormat; + } + } + + if (!match && !list.empty()) + current = list[0].value; +} + +void CLangInfo::SettingOptionsLongDateFormatsFiller(const SettingConstPtr& setting, + std::vector<StringSettingOption>& list, + std::string& current, + void* data) +{ + bool match = false; + const std::string& longDateFormatSetting = std::static_pointer_cast<const CSettingString>(setting)->GetValue(); + + CDateTime now = CDateTime::GetCurrentDateTime(); + + list.emplace_back( + StringUtils::Format(g_localizeStrings.Get(20035), + now.GetAsLocalizedDate(g_langInfo.m_currentRegion->m_strDateFormatLong)), + SETTING_REGIONAL_DEFAULT); + if (longDateFormatSetting == SETTING_REGIONAL_DEFAULT) + { + match = true; + current = SETTING_REGIONAL_DEFAULT; + } + + for (const std::string& longDateFormat : longDateFormats) + { + list.emplace_back(now.GetAsLocalizedDate(longDateFormat), longDateFormat); + + if (!match && longDateFormatSetting == longDateFormat) + { + match = true; + current = longDateFormat; + } + } + + if (!match && !list.empty()) + current = list[0].value; +} + +void CLangInfo::SettingOptionsTimeFormatsFiller(const SettingConstPtr& setting, + std::vector<StringSettingOption>& list, + std::string& current, + void* data) +{ + bool match = false; + const std::string& timeFormatSetting = std::static_pointer_cast<const CSettingString>(setting)->GetValue(); + + CDateTime now = CDateTime::GetCurrentDateTime(); + bool use24hourFormat = g_langInfo.Use24HourClock(); + + list.emplace_back( + StringUtils::Format(g_localizeStrings.Get(20035), + ToSettingTimeFormat(now, g_langInfo.m_currentRegion->m_strTimeFormat)), + SETTING_REGIONAL_DEFAULT); + if (timeFormatSetting == SETTING_REGIONAL_DEFAULT) + { + match = true; + current = SETTING_REGIONAL_DEFAULT; + } + + if (use24hourFormat) + { + list.emplace_back(ToSettingTimeFormat(now, TIME_FORMAT_SINGLE_24), TIME_FORMAT_SINGLE_24); + if (timeFormatSetting == TIME_FORMAT_SINGLE_24) + { + current = TIME_FORMAT_SINGLE_24; + match = true; + } + + list.emplace_back(ToSettingTimeFormat(now, TIME_FORMAT_DOUBLE_24), TIME_FORMAT_DOUBLE_24); + if (timeFormatSetting == TIME_FORMAT_DOUBLE_24) + { + current = TIME_FORMAT_DOUBLE_24; + match = true; + } + } + else + { + list.emplace_back(ToSettingTimeFormat(now, TIME_FORMAT_SINGLE_12), TIME_FORMAT_SINGLE_12); + if (timeFormatSetting == TIME_FORMAT_SINGLE_12) + { + current = TIME_FORMAT_SINGLE_12; + match = true; + } + + list.emplace_back(ToSettingTimeFormat(now, TIME_FORMAT_DOUBLE_12), TIME_FORMAT_DOUBLE_12); + if (timeFormatSetting == TIME_FORMAT_DOUBLE_12) + { + current = TIME_FORMAT_DOUBLE_12; + match = true; + } + + std::string timeFormatSingle12Meridiem = ToTimeFormat(false, true, true); + list.emplace_back(ToSettingTimeFormat(now, timeFormatSingle12Meridiem), timeFormatSingle12Meridiem); + if (timeFormatSetting == timeFormatSingle12Meridiem) + { + current = timeFormatSingle12Meridiem; + match = true; + } + + std::string timeFormatDouble12Meridiem = ToTimeFormat(false, false, true); + list.emplace_back(ToSettingTimeFormat(now, timeFormatDouble12Meridiem), timeFormatDouble12Meridiem); + if (timeFormatSetting == timeFormatDouble12Meridiem) + { + current = timeFormatDouble12Meridiem; + match = true; + } + } + + if (!match && !list.empty()) + current = list[0].value; +} + +void CLangInfo::SettingOptions24HourClockFormatsFiller(const SettingConstPtr& setting, + std::vector<StringSettingOption>& list, + std::string& current, + void* data) +{ + bool match = false; + const std::string& clock24HourFormatSetting = std::static_pointer_cast<const CSettingString>(setting)->GetValue(); + + // determine the 24-hour clock format of the regional setting + int regionalClock24HourFormatLabel = DetermineUse24HourClockFromTimeFormat(g_langInfo.m_currentRegion->m_strTimeFormat) ? 12384 : 12383; + list.emplace_back(StringUtils::Format(g_localizeStrings.Get(20035), + g_localizeStrings.Get(regionalClock24HourFormatLabel)), + SETTING_REGIONAL_DEFAULT); + if (clock24HourFormatSetting == SETTING_REGIONAL_DEFAULT) + { + match = true; + current = SETTING_REGIONAL_DEFAULT; + } + + list.emplace_back(g_localizeStrings.Get(12383), TIME_FORMAT_12HOURS); + if (clock24HourFormatSetting == TIME_FORMAT_12HOURS) + { + current = TIME_FORMAT_12HOURS; + match = true; + } + + list.emplace_back(g_localizeStrings.Get(12384), TIME_FORMAT_24HOURS); + if (clock24HourFormatSetting == TIME_FORMAT_24HOURS) + { + current = TIME_FORMAT_24HOURS; + match = true; + } + + if (!match && !list.empty()) + current = list[0].value; +} + +void CLangInfo::SettingOptionsTemperatureUnitsFiller(const SettingConstPtr& setting, + std::vector<StringSettingOption>& list, + std::string& current, + void* data) +{ + bool match = false; + const std::string& temperatureUnitSetting = std::static_pointer_cast<const CSettingString>(setting)->GetValue(); + + list.emplace_back( + StringUtils::Format(g_localizeStrings.Get(20035), + GetTemperatureUnitString(g_langInfo.m_currentRegion->m_tempUnit)), + SETTING_REGIONAL_DEFAULT); + if (temperatureUnitSetting == SETTING_REGIONAL_DEFAULT) + { + match = true; + current = SETTING_REGIONAL_DEFAULT; + } + + for (const TemperatureInfo& info : temperatureInfo) + { + list.emplace_back(GetTemperatureUnitString(info.unit), info.name); + + if (!match && temperatureUnitSetting == info.name) + { + match = true; + current = info.name; + } + } + + if (!match && !list.empty()) + current = list[0].value; +} + +void CLangInfo::SettingOptionsSpeedUnitsFiller(const SettingConstPtr& setting, + std::vector<StringSettingOption>& list, + std::string& current, + void* data) +{ + bool match = false; + const std::string& speedUnitSetting = std::static_pointer_cast<const CSettingString>(setting)->GetValue(); + + list.emplace_back( + StringUtils::Format(g_localizeStrings.Get(20035), + GetSpeedUnitString(g_langInfo.m_currentRegion->m_speedUnit)), + SETTING_REGIONAL_DEFAULT); + if (speedUnitSetting == SETTING_REGIONAL_DEFAULT) + { + match = true; + current = SETTING_REGIONAL_DEFAULT; + } + + for (const SpeedInfo& info : speedInfo) + { + list.emplace_back(GetSpeedUnitString(info.unit), info.name); + + if (!match && speedUnitSetting == info.name) + { + match = true; + current = info.name; + } + } + + if (!match && !list.empty()) + current = list[0].value; +} + +void CLangInfo::AddLanguages(std::vector<StringSettingOption> &list) +{ + std::vector<std::string> languages = g_LangCodeExpander.GetLanguageNames( + CLangCodeExpander::ISO_639_1, CLangCodeExpander::LANG_LIST::INCLUDE_ADDONS_USERDEFINED); + + for (const auto& language : languages) + list.emplace_back(language, language); +} |