diff options
Diffstat (limited to '')
-rw-r--r-- | xbmc/utils/Locale.cpp | 284 |
1 files changed, 284 insertions, 0 deletions
diff --git a/xbmc/utils/Locale.cpp b/xbmc/utils/Locale.cpp new file mode 100644 index 0000000..ff63ed4 --- /dev/null +++ b/xbmc/utils/Locale.cpp @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2015-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 "Locale.h" + +#include "utils/StringUtils.h" + +const CLocale CLocale::Empty; + +CLocale::CLocale() + : m_language(), + m_territory(), + m_codeset(), + m_modifier() +{ } + +CLocale::CLocale(const std::string& language) + : m_language(), + m_territory(), + m_codeset(), + m_modifier() +{ + m_valid = ParseLocale(language, m_language, m_territory, m_codeset, m_modifier); +} + +CLocale::CLocale(const std::string& language, const std::string& territory) + : m_language(language), + m_territory(territory), + m_codeset(), + m_modifier() +{ + Initialize(); +} + +CLocale::CLocale(const std::string& language, const std::string& territory, const std::string& codeset) + : m_language(language), + m_territory(territory), + m_codeset(codeset), + m_modifier() +{ + Initialize(); +} + +CLocale::CLocale(const std::string& language, const std::string& territory, const std::string& codeset, const std::string& modifier) + : m_language(language), + m_territory(territory), + m_codeset(codeset), + m_modifier(modifier) +{ + Initialize(); +} + +CLocale::~CLocale() = default; + +CLocale CLocale::FromString(const std::string& locale) +{ + return CLocale(locale); +} + +bool CLocale::operator==(const CLocale& other) const +{ + if (!m_valid && !other.m_valid) + return true; + + return m_valid == other.m_valid && + StringUtils::EqualsNoCase(m_language, other.m_language) && + StringUtils::EqualsNoCase(m_territory, other.m_territory) && + StringUtils::EqualsNoCase(m_codeset, other.m_codeset) && + StringUtils::EqualsNoCase(m_modifier, other.m_modifier); +} + +std::string CLocale::ToString() const +{ + if (!m_valid) + return ""; + + std::string locale = ToShortString(); + + if (!m_codeset.empty()) + locale += "." + m_codeset; + + if (!m_modifier.empty()) + locale += "@" + m_modifier; + + return locale; +} + +std::string CLocale::ToStringLC() const +{ + if (!m_valid) + return ""; + + std::string locale = ToString(); + StringUtils::ToLower(locale); + + return locale; +} + +std::string CLocale::ToShortString() const +{ + if (!m_valid) + return ""; + + std::string locale = m_language; + + if (!m_territory.empty()) + locale += "_" + m_territory; + + return locale; +} + +std::string CLocale::ToShortStringLC() const +{ + if (!m_valid) + return ""; + + std::string locale = ToShortString(); + StringUtils::ToLower(locale); + + return locale; +} + +bool CLocale::Equals(const std::string& locale) const +{ + CLocale other = FromString(locale); + + return *this == other; +} + +bool CLocale::Matches(const std::string& locale) const +{ + CLocale other = FromString(locale); + + if (!m_valid && !other.m_valid) + return true; + if (!m_valid || !other.m_valid) + return false; + + if (!StringUtils::EqualsNoCase(m_language, other.m_language)) + return false; + if (!m_territory.empty() && !other.m_territory.empty() && !StringUtils::EqualsNoCase(m_territory, other.m_territory)) + return false; + if (!m_codeset.empty() && !other.m_codeset.empty() && !StringUtils::EqualsNoCase(m_codeset, other.m_codeset)) + return false; + if (!m_modifier.empty() && !other.m_modifier.empty() && !StringUtils::EqualsNoCase(m_modifier, other.m_modifier)) + return false; + + return true; +} + +std::string CLocale::FindBestMatch(const std::set<std::string>& locales) const +{ + std::string bestMatch = ""; + int bestMatchRank = -1; + + for (auto const& locale : locales) + { + // check if there is an exact match + if (Equals(locale)) + return locale; + + int matchRank = GetMatchRank(locale); + if (matchRank > bestMatchRank) + { + bestMatchRank = matchRank; + bestMatch = locale; + } + } + + return bestMatch; +} + +std::string CLocale::FindBestMatch(const std::unordered_map<std::string, std::string>& locales) const +{ + std::string bestMatch = ""; + int bestMatchRank = -1; + + for (auto const& locale : locales) + { + // check if there is an exact match + if (Equals(locale.first)) + return locale.first; + + int matchRank = GetMatchRank(locale.first); + if (matchRank > bestMatchRank) + { + bestMatchRank = matchRank; + bestMatch = locale.first; + } + } + + return bestMatch; +} + +bool CLocale::CheckValidity(const std::string& language, const std::string& territory, const std::string& codeset, const std::string& modifier) +{ + static_cast<void>(territory); + static_cast<void>(codeset); + static_cast<void>(modifier); + + return !language.empty(); +} + +bool CLocale::ParseLocale(const std::string &locale, std::string &language, std::string &territory, std::string &codeset, std::string &modifier) +{ + if (locale.empty()) + return false; + + language.clear(); + territory.clear(); + codeset.clear(); + modifier.clear(); + + // the format for a locale is [language[_territory][.codeset][@modifier]] + std::string tmp = locale; + + // look for the modifier after @ + size_t pos = tmp.find('@'); + if (pos != std::string::npos) + { + modifier = tmp.substr(pos + 1); + tmp = tmp.substr(0, pos); + } + + // look for the codeset after . + pos = tmp.find('.'); + if (pos != std::string::npos) + { + codeset = tmp.substr(pos + 1); + tmp = tmp.substr(0, pos); + } + + // look for the codeset after _ + pos = tmp.find('_'); + if (pos != std::string::npos) + { + territory = tmp.substr(pos + 1); + StringUtils::ToUpper(territory); + tmp = tmp.substr(0, pos); + } + + // what remains is the language + language = tmp; + StringUtils::ToLower(language); + + return CheckValidity(language, territory, codeset, modifier); +} + +void CLocale::Initialize() +{ + m_valid = CheckValidity(m_language, m_territory, m_codeset, m_modifier); + if (m_valid) + { + StringUtils::ToLower(m_language); + StringUtils::ToUpper(m_territory); + } +} + +int CLocale::GetMatchRank(const std::string& locale) const +{ + CLocale other = FromString(locale); + + // both locales must be valid and match in language + if (!m_valid || !other.m_valid || + !StringUtils::EqualsNoCase(m_language, other.m_language)) + return -1; + + int rank = 0; + // matching in territory is considered more important than matching in + // codeset and/or modifier + if (!m_territory.empty() && !other.m_territory.empty() && StringUtils::EqualsNoCase(m_territory, other.m_territory)) + rank += 3; + if (!m_codeset.empty() && !other.m_codeset.empty() && StringUtils::EqualsNoCase(m_codeset, other.m_codeset)) + rank += 1; + if (!m_modifier.empty() && !other.m_modifier.empty() && StringUtils::EqualsNoCase(m_modifier, other.m_modifier)) + rank += 1; + + return rank; +} |