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.
11 #include "utils/StringUtils.h"
13 const CLocale
CLocale::Empty
;
22 CLocale::CLocale(const std::string
& language
)
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
),
40 CLocale::CLocale(const std::string
& language
, const std::string
& territory
, const std::string
& codeset
)
41 : m_language(language
),
42 m_territory(territory
),
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
),
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
)
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
82 std::string locale
= ToShortString();
84 if (!m_codeset
.empty())
85 locale
+= "." + m_codeset
;
87 if (!m_modifier
.empty())
88 locale
+= "@" + m_modifier
;
93 std::string
CLocale::ToStringLC() const
98 std::string locale
= ToString();
99 StringUtils::ToLower(locale
);
104 std::string
CLocale::ToShortString() const
109 std::string locale
= m_language
;
111 if (!m_territory
.empty())
112 locale
+= "_" + m_territory
;
117 std::string
CLocale::ToShortStringLC() const
122 std::string locale
= ToShortString();
123 StringUtils::ToLower(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
)
141 if (!m_valid
|| !other
.m_valid
)
144 if (!StringUtils::EqualsNoCase(m_language
, other
.m_language
))
146 if (!m_territory
.empty() && !other
.m_territory
.empty() && !StringUtils::EqualsNoCase(m_territory
, other
.m_territory
))
148 if (!m_codeset
.empty() && !other
.m_codeset
.empty() && !StringUtils::EqualsNoCase(m_codeset
, other
.m_codeset
))
150 if (!m_modifier
.empty() && !other
.m_modifier
.empty() && !StringUtils::EqualsNoCase(m_modifier
, other
.m_modifier
))
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
167 int matchRank
= GetMatchRank(locale
);
168 if (matchRank
> bestMatchRank
)
170 bestMatchRank
= matchRank
;
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
))
189 int matchRank
= GetMatchRank(locale
.first
);
190 if (matchRank
> bestMatchRank
)
192 bestMatchRank
= matchRank
;
193 bestMatch
= locale
.first
;
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
)
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 .
232 if (pos
!= std::string::npos
)
234 codeset
= tmp
.substr(pos
+ 1);
235 tmp
= tmp
.substr(0, pos
);
238 // look for the codeset after _
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
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
);
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
))
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
))
278 if (!m_codeset
.empty() && !other
.m_codeset
.empty() && StringUtils::EqualsNoCase(m_codeset
, other
.m_codeset
))
280 if (!m_modifier
.empty() && !other
.m_modifier
.empty() && StringUtils::EqualsNoCase(m_modifier
, other
.m_modifier
))