[PVR][Estuary] Timer settings dialog: Show client name in timer type selection dialog...
[xbmc.git] / xbmc / utils / Locale.cpp
blobff63ed48066c742b0a5c48ce07ac4c363b713e04
1 /*
2 * Copyright (C) 2015-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 "Locale.h"
11 #include "utils/StringUtils.h"
13 const CLocale CLocale::Empty;
15 CLocale::CLocale()
16 : m_language(),
17 m_territory(),
18 m_codeset(),
19 m_modifier()
20 { }
22 CLocale::CLocale(const std::string& language)
23 : m_language(),
24 m_territory(),
25 m_codeset(),
26 m_modifier()
28 m_valid = ParseLocale(language, m_language, m_territory, m_codeset, m_modifier);
31 CLocale::CLocale(const std::string& language, const std::string& territory)
32 : m_language(language),
33 m_territory(territory),
34 m_codeset(),
35 m_modifier()
37 Initialize();
40 CLocale::CLocale(const std::string& language, const std::string& territory, const std::string& codeset)
41 : m_language(language),
42 m_territory(territory),
43 m_codeset(codeset),
44 m_modifier()
46 Initialize();
49 CLocale::CLocale(const std::string& language, const std::string& territory, const std::string& codeset, const std::string& modifier)
50 : m_language(language),
51 m_territory(territory),
52 m_codeset(codeset),
53 m_modifier(modifier)
55 Initialize();
58 CLocale::~CLocale() = default;
60 CLocale CLocale::FromString(const std::string& locale)
62 return CLocale(locale);
65 bool CLocale::operator==(const CLocale& other) const
67 if (!m_valid && !other.m_valid)
68 return true;
70 return m_valid == other.m_valid &&
71 StringUtils::EqualsNoCase(m_language, other.m_language) &&
72 StringUtils::EqualsNoCase(m_territory, other.m_territory) &&
73 StringUtils::EqualsNoCase(m_codeset, other.m_codeset) &&
74 StringUtils::EqualsNoCase(m_modifier, other.m_modifier);
77 std::string CLocale::ToString() const
79 if (!m_valid)
80 return "";
82 std::string locale = ToShortString();
84 if (!m_codeset.empty())
85 locale += "." + m_codeset;
87 if (!m_modifier.empty())
88 locale += "@" + m_modifier;
90 return locale;
93 std::string CLocale::ToStringLC() const
95 if (!m_valid)
96 return "";
98 std::string locale = ToString();
99 StringUtils::ToLower(locale);
101 return locale;
104 std::string CLocale::ToShortString() const
106 if (!m_valid)
107 return "";
109 std::string locale = m_language;
111 if (!m_territory.empty())
112 locale += "_" + m_territory;
114 return locale;
117 std::string CLocale::ToShortStringLC() const
119 if (!m_valid)
120 return "";
122 std::string locale = ToShortString();
123 StringUtils::ToLower(locale);
125 return locale;
128 bool CLocale::Equals(const std::string& locale) const
130 CLocale other = FromString(locale);
132 return *this == other;
135 bool CLocale::Matches(const std::string& locale) const
137 CLocale other = FromString(locale);
139 if (!m_valid && !other.m_valid)
140 return true;
141 if (!m_valid || !other.m_valid)
142 return false;
144 if (!StringUtils::EqualsNoCase(m_language, other.m_language))
145 return false;
146 if (!m_territory.empty() && !other.m_territory.empty() && !StringUtils::EqualsNoCase(m_territory, other.m_territory))
147 return false;
148 if (!m_codeset.empty() && !other.m_codeset.empty() && !StringUtils::EqualsNoCase(m_codeset, other.m_codeset))
149 return false;
150 if (!m_modifier.empty() && !other.m_modifier.empty() && !StringUtils::EqualsNoCase(m_modifier, other.m_modifier))
151 return false;
153 return true;
156 std::string CLocale::FindBestMatch(const std::set<std::string>& locales) const
158 std::string bestMatch = "";
159 int bestMatchRank = -1;
161 for (auto const& locale : locales)
163 // check if there is an exact match
164 if (Equals(locale))
165 return locale;
167 int matchRank = GetMatchRank(locale);
168 if (matchRank > bestMatchRank)
170 bestMatchRank = matchRank;
171 bestMatch = locale;
175 return bestMatch;
178 std::string CLocale::FindBestMatch(const std::unordered_map<std::string, std::string>& locales) const
180 std::string bestMatch = "";
181 int bestMatchRank = -1;
183 for (auto const& locale : locales)
185 // check if there is an exact match
186 if (Equals(locale.first))
187 return locale.first;
189 int matchRank = GetMatchRank(locale.first);
190 if (matchRank > bestMatchRank)
192 bestMatchRank = matchRank;
193 bestMatch = locale.first;
197 return bestMatch;
200 bool CLocale::CheckValidity(const std::string& language, const std::string& territory, const std::string& codeset, const std::string& modifier)
202 static_cast<void>(territory);
203 static_cast<void>(codeset);
204 static_cast<void>(modifier);
206 return !language.empty();
209 bool CLocale::ParseLocale(const std::string &locale, std::string &language, std::string &territory, std::string &codeset, std::string &modifier)
211 if (locale.empty())
212 return false;
214 language.clear();
215 territory.clear();
216 codeset.clear();
217 modifier.clear();
219 // the format for a locale is [language[_territory][.codeset][@modifier]]
220 std::string tmp = locale;
222 // look for the modifier after @
223 size_t pos = tmp.find('@');
224 if (pos != std::string::npos)
226 modifier = tmp.substr(pos + 1);
227 tmp = tmp.substr(0, pos);
230 // look for the codeset after .
231 pos = tmp.find('.');
232 if (pos != std::string::npos)
234 codeset = tmp.substr(pos + 1);
235 tmp = tmp.substr(0, pos);
238 // look for the codeset after _
239 pos = tmp.find('_');
240 if (pos != std::string::npos)
242 territory = tmp.substr(pos + 1);
243 StringUtils::ToUpper(territory);
244 tmp = tmp.substr(0, pos);
247 // what remains is the language
248 language = tmp;
249 StringUtils::ToLower(language);
251 return CheckValidity(language, territory, codeset, modifier);
254 void CLocale::Initialize()
256 m_valid = CheckValidity(m_language, m_territory, m_codeset, m_modifier);
257 if (m_valid)
259 StringUtils::ToLower(m_language);
260 StringUtils::ToUpper(m_territory);
264 int CLocale::GetMatchRank(const std::string& locale) const
266 CLocale other = FromString(locale);
268 // both locales must be valid and match in language
269 if (!m_valid || !other.m_valid ||
270 !StringUtils::EqualsNoCase(m_language, other.m_language))
271 return -1;
273 int rank = 0;
274 // matching in territory is considered more important than matching in
275 // codeset and/or modifier
276 if (!m_territory.empty() && !other.m_territory.empty() && StringUtils::EqualsNoCase(m_territory, other.m_territory))
277 rank += 3;
278 if (!m_codeset.empty() && !other.m_codeset.empty() && StringUtils::EqualsNoCase(m_codeset, other.m_codeset))
279 rank += 1;
280 if (!m_modifier.empty() && !other.m_modifier.empty() && StringUtils::EqualsNoCase(m_modifier, other.m_modifier))
281 rank += 1;
283 return rank;