summaryrefslogtreecommitdiffstats
path: root/xbmc/utils/Locale.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--xbmc/utils/Locale.cpp284
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;
+}