2 * Copyright (C) 2005-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
22 #include "settings/AdvancedSettings.h"
23 #include "settings/GUISettings.h"
24 #include "guilib/LocalizeStrings.h"
25 #include "utils/log.h"
26 #include "utils/XBMCTinyXML.h"
27 #include "utils/LangCodeExpander.h"
31 #define TEMP_UNIT_STRINGS 20027
33 #define SPEED_UNIT_STRINGS 20200
35 CLangInfo::CRegion::CRegion(const CRegion& region)
37 m_strName=region.m_strName;
38 m_forceUnicodeFont=region.m_forceUnicodeFont;
39 m_strGuiCharSet=region.m_strGuiCharSet;
40 m_strSubtitleCharSet=region.m_strSubtitleCharSet;
41 m_strDVDMenuLanguage=region.m_strDVDMenuLanguage;
42 m_strDVDAudioLanguage=region.m_strDVDAudioLanguage;
43 m_strDVDSubtitleLanguage=region.m_strDVDSubtitleLanguage;
44 m_strLangLocaleName = region.m_strLangLocaleName;
45 m_strRegionLocaleName = region.m_strRegionLocaleName;
47 m_strDateFormatShort=region.m_strDateFormatShort;
48 m_strDateFormatLong=region.m_strDateFormatLong;
49 m_strTimeFormat=region.m_strTimeFormat;
50 m_strMeridiemSymbols[MERIDIEM_SYMBOL_PM]=region.m_strMeridiemSymbols[MERIDIEM_SYMBOL_PM];
51 m_strMeridiemSymbols[MERIDIEM_SYMBOL_AM]=region.m_strMeridiemSymbols[MERIDIEM_SYMBOL_AM];
52 m_strTimeFormat=region.m_strTimeFormat;
53 m_tempUnit=region.m_tempUnit;
54 m_speedUnit=region.m_speedUnit;
55 m_strTimeZone = region.m_strTimeZone;
58 CLangInfo::CRegion::CRegion()
63 CLangInfo::CRegion::~CRegion()
68 void CLangInfo::CRegion::SetDefaults()
71 m_forceUnicodeFont=false;
72 m_strGuiCharSet="CP1252";
73 m_strSubtitleCharSet="CP1252";
74 m_strDVDMenuLanguage="en";
75 m_strDVDAudioLanguage="en";
76 m_strDVDSubtitleLanguage="en";
77 m_strLangLocaleName = "English";
79 m_strDateFormatShort="DD/MM/YYYY";
80 m_strDateFormatLong="DDDD, D MMMM YYYY";
81 m_strTimeFormat="HH:mm:ss";
82 m_tempUnit=TEMP_UNIT_CELSIUS;
83 m_speedUnit=SPEED_UNIT_KMH;
84 m_strTimeZone.clear();
87 void CLangInfo::CRegion::SetTempUnit(const CStdString& strUnit)
89 if (strUnit.Equals("F"))
90 m_tempUnit=TEMP_UNIT_FAHRENHEIT;
91 else if (strUnit.Equals("K"))
92 m_tempUnit=TEMP_UNIT_KELVIN;
93 else if (strUnit.Equals("C"))
94 m_tempUnit=TEMP_UNIT_CELSIUS;
95 else if (strUnit.Equals("Re"))
96 m_tempUnit=TEMP_UNIT_REAUMUR;
97 else if (strUnit.Equals("Ra"))
98 m_tempUnit=TEMP_UNIT_RANKINE;
99 else if (strUnit.Equals("Ro"))
100 m_tempUnit=TEMP_UNIT_ROMER;
101 else if (strUnit.Equals("De"))
102 m_tempUnit=TEMP_UNIT_DELISLE;
103 else if (strUnit.Equals("N"))
104 m_tempUnit=TEMP_UNIT_NEWTON;
107 void CLangInfo::CRegion::SetSpeedUnit(const CStdString& strUnit)
109 if (strUnit.Equals("kmh"))
110 m_speedUnit=SPEED_UNIT_KMH;
111 else if (strUnit.Equals("mpmin"))
112 m_speedUnit=SPEED_UNIT_MPMIN;
113 else if (strUnit.Equals("mps"))
114 m_speedUnit=SPEED_UNIT_MPS;
115 else if (strUnit.Equals("fth"))
116 m_speedUnit=SPEED_UNIT_FTH;
117 else if (strUnit.Equals("ftm"))
118 m_speedUnit=SPEED_UNIT_FTMIN;
119 else if (strUnit.Equals("fts"))
120 m_speedUnit=SPEED_UNIT_FTS;
121 else if (strUnit.Equals("mph"))
122 m_speedUnit=SPEED_UNIT_MPH;
123 else if (strUnit.Equals("kts"))
124 m_speedUnit=SPEED_UNIT_KTS;
125 else if (strUnit.Equals("beaufort"))
126 m_speedUnit=SPEED_UNIT_BEAUFORT;
127 else if (strUnit.Equals("inchs"))
128 m_speedUnit=SPEED_UNIT_INCHPS;
129 else if (strUnit.Equals("yards"))
130 m_speedUnit=SPEED_UNIT_YARDPS;
131 else if (strUnit.Equals("fpf"))
132 m_speedUnit=SPEED_UNIT_FPF;
135 void CLangInfo::CRegion::SetTimeZone(const CStdString& strTimeZone)
137 m_strTimeZone = strTimeZone;
140 // set the locale associated with this region global. This affects string
141 // sorting & transformations
142 void CLangInfo::CRegion::SetGlobalLocale()
144 CStdString strLocale;
145 if (m_strRegionLocaleName.length() > 0)
147 strLocale = m_strLangLocaleName + "_" + m_strRegionLocaleName;
149 strLocale += ".UTF-8";
153 CLog::Log(LOGDEBUG, "trying to set locale to %s", strLocale.c_str());
155 // We need to set the locale to only change the collate. Otherwise,
156 // decimal separator is changed depending of the current language
157 // (ie. "," in French or Dutch instead of "."). This breaks atof() and
158 // others similar functions.
159 #if defined(__FreeBSD__) || defined(TARGET_DARWIN_OSX)
160 // on FreeBSD and darwin libstdc++ is compiled with "generic" locale support
161 if (setlocale(LC_COLLATE, strLocale.c_str()) == NULL
162 || setlocale(LC_CTYPE, strLocale.c_str()) == NULL)
165 setlocale(LC_COLLATE, strLocale.c_str());
166 setlocale(LC_CTYPE, strLocale.c_str());
169 locale current_locale = locale::classic(); // C-Locale
172 locale lcl = locale(strLocale);
173 strLocale = lcl.name();
174 current_locale = current_locale.combine< collate<wchar_t> >(lcl);
175 current_locale = current_locale.combine< ctype<wchar_t> >(lcl);
177 assert(use_facet< numpunct<char> >(current_locale).decimal_point() == '.');
180 current_locale = locale::classic();
184 locale::global(current_locale);
186 CLog::Log(LOGINFO, "global locale set to %s", strLocale.c_str());
189 CLangInfo::CLangInfo()
194 CLangInfo::~CLangInfo()
198 bool CLangInfo::Load(const CStdString& strFileName)
203 if (!xmlDoc.LoadFile(strFileName))
205 CLog::Log(LOGERROR, "unable to load %s: %s at line %d", strFileName.c_str(), xmlDoc.ErrorDesc(), xmlDoc.ErrorRow());
209 TiXmlElement* pRootElement = xmlDoc.RootElement();
210 CStdString strValue = pRootElement->Value();
211 if (strValue != CStdString("language"))
213 CLog::Log(LOGERROR, "%s Doesn't contain <language>", strFileName.c_str());
217 if (pRootElement->Attribute("locale"))
218 m_defaultRegion.m_strLangLocaleName = pRootElement->Attribute("locale");
221 // Windows need 3 chars isolang code
222 if (m_defaultRegion.m_strLangLocaleName.length() == 2)
224 if (! g_LangCodeExpander.ConvertTwoToThreeCharCode(m_defaultRegion.m_strLangLocaleName, m_defaultRegion.m_strLangLocaleName, true))
225 m_defaultRegion.m_strLangLocaleName = "";
228 if (!g_LangCodeExpander.ConvertWindowsToGeneralCharCode(m_defaultRegion.m_strLangLocaleName, m_languageCodeGeneral))
229 m_languageCodeGeneral = "";
231 if (m_defaultRegion.m_strLangLocaleName.length() != 3)
233 if (!g_LangCodeExpander.ConvertToThreeCharCode(m_languageCodeGeneral, m_defaultRegion.m_strLangLocaleName))
234 m_languageCodeGeneral = "";
237 m_languageCodeGeneral = m_defaultRegion.m_strLangLocaleName;
240 const TiXmlNode *pCharSets = pRootElement->FirstChild("charsets");
241 if (pCharSets && !pCharSets->NoChildren())
243 const TiXmlNode *pGui = pCharSets->FirstChild("gui");
244 if (pGui && !pGui->NoChildren())
246 CStdString strForceUnicodeFont = ((TiXmlElement*) pGui)->Attribute("unicodefont");
248 if (strForceUnicodeFont.Equals("true"))
249 m_defaultRegion.m_forceUnicodeFont=true;
251 m_defaultRegion.m_strGuiCharSet=pGui->FirstChild()->Value();
254 const TiXmlNode *pSubtitle = pCharSets->FirstChild("subtitle");
255 if (pSubtitle && !pSubtitle->NoChildren())
256 m_defaultRegion.m_strSubtitleCharSet=pSubtitle->FirstChild()->Value();
259 const TiXmlNode *pDVD = pRootElement->FirstChild("dvd");
260 if (pDVD && !pDVD->NoChildren())
262 const TiXmlNode *pMenu = pDVD->FirstChild("menu");
263 if (pMenu && !pMenu->NoChildren())
264 m_defaultRegion.m_strDVDMenuLanguage=pMenu->FirstChild()->Value();
266 const TiXmlNode *pAudio = pDVD->FirstChild("audio");
267 if (pAudio && !pAudio->NoChildren())
268 m_defaultRegion.m_strDVDAudioLanguage=pAudio->FirstChild()->Value();
270 const TiXmlNode *pSubtitle = pDVD->FirstChild("subtitle");
271 if (pSubtitle && !pSubtitle->NoChildren())
272 m_defaultRegion.m_strDVDSubtitleLanguage=pSubtitle->FirstChild()->Value();
275 const TiXmlNode *pRegions = pRootElement->FirstChild("regions");
276 if (pRegions && !pRegions->NoChildren())
278 const TiXmlElement *pRegion=pRegions->FirstChildElement("region");
281 CRegion region(m_defaultRegion);
282 region.m_strName=pRegion->Attribute("name");
283 if (region.m_strName.IsEmpty())
284 region.m_strName="N/A";
286 if (pRegion->Attribute("locale"))
287 region.m_strRegionLocaleName = pRegion->Attribute("locale");
290 // Windows need 3 chars regions code
291 if (region.m_strRegionLocaleName.length() == 2)
293 if (! g_LangCodeExpander.ConvertLinuxToWindowsRegionCodes(region.m_strRegionLocaleName, region.m_strRegionLocaleName))
294 region.m_strRegionLocaleName = "";
298 const TiXmlNode *pDateLong=pRegion->FirstChild("datelong");
299 if (pDateLong && !pDateLong->NoChildren())
300 region.m_strDateFormatLong=pDateLong->FirstChild()->Value();
302 const TiXmlNode *pDateShort=pRegion->FirstChild("dateshort");
303 if (pDateShort && !pDateShort->NoChildren())
304 region.m_strDateFormatShort=pDateShort->FirstChild()->Value();
306 const TiXmlElement *pTime=pRegion->FirstChildElement("time");
307 if (pTime && !pTime->NoChildren())
309 region.m_strTimeFormat=pTime->FirstChild()->Value();
310 region.m_strMeridiemSymbols[MERIDIEM_SYMBOL_AM]=pTime->Attribute("symbolAM");
311 region.m_strMeridiemSymbols[MERIDIEM_SYMBOL_PM]=pTime->Attribute("symbolPM");
314 const TiXmlNode *pTempUnit=pRegion->FirstChild("tempunit");
315 if (pTempUnit && !pTempUnit->NoChildren())
316 region.SetTempUnit(pTempUnit->FirstChild()->Value());
318 const TiXmlNode *pSpeedUnit=pRegion->FirstChild("speedunit");
319 if (pSpeedUnit && !pSpeedUnit->NoChildren())
320 region.SetSpeedUnit(pSpeedUnit->FirstChild()->Value());
322 const TiXmlNode *pTimeZone=pRegion->FirstChild("timezone");
323 if (pTimeZone && !pTimeZone->NoChildren())
324 region.SetTimeZone(pTimeZone->FirstChild()->Value());
326 m_regions.insert(PAIR_REGIONS(region.m_strName, region));
328 pRegion=pRegion->NextSiblingElement("region");
331 const CStdString& strName=g_guiSettings.GetString("locale.country");
332 SetCurrentRegion(strName);
335 LoadTokens(pRootElement->FirstChild("sorttokens"),g_advancedSettings.m_vecTokens);
340 void CLangInfo::LoadTokens(const TiXmlNode* pTokens, vector<CStdString>& vecTokens)
342 if (pTokens && !pTokens->NoChildren())
344 const TiXmlElement *pToken = pTokens->FirstChildElement("token");
347 CStdString strSep= " ._";
348 if (pToken->Attribute("separators"))
349 strSep = pToken->Attribute("separators");
350 if (pToken->FirstChild() && pToken->FirstChild()->Value())
352 if (strSep.IsEmpty())
353 vecTokens.push_back(pToken->FirstChild()->Value());
355 for (unsigned int i=0;i<strSep.size();++i)
356 vecTokens.push_back(CStdString(pToken->FirstChild()->Value())+strSep[i]);
358 pToken = pToken->NextSiblingElement();
363 void CLangInfo::SetDefaults()
367 //Reset default region
368 m_defaultRegion.SetDefaults();
370 // Set the default region, we may be unable to load langinfo.xml
371 m_currentRegion=&m_defaultRegion;
373 m_languageCodeGeneral = "eng";
376 CStdString CLangInfo::GetGuiCharSet() const
378 CStdString strCharSet;
379 strCharSet=g_guiSettings.GetString("locale.charset");
380 if (strCharSet=="DEFAULT")
381 strCharSet=m_currentRegion->m_strGuiCharSet;
386 CStdString CLangInfo::GetSubtitleCharSet() const
388 CStdString strCharSet=g_guiSettings.GetString("subtitles.charset");
389 if (strCharSet=="DEFAULT")
390 strCharSet=m_currentRegion->m_strSubtitleCharSet;
395 // three char language code (not win32 specific)
396 const CStdString& CLangInfo::GetAudioLanguage() const
398 if (!m_audioLanguage.empty())
399 return m_audioLanguage;
401 return m_languageCodeGeneral;
404 void CLangInfo::SetAudioLanguage(const CStdString &language)
406 if (language.empty() || !g_LangCodeExpander.ConvertToThreeCharCode(m_audioLanguage, language))
407 m_audioLanguage.clear();
410 // three char language code (not win32 specific)
411 const CStdString& CLangInfo::GetSubtitleLanguage() const
413 if (!m_subtitleLanguage.empty())
414 return m_subtitleLanguage;
416 return m_languageCodeGeneral;
419 void CLangInfo::SetSubtitleLanguage(const CStdString &language)
421 if (language.empty() || !g_LangCodeExpander.ConvertToThreeCharCode(m_subtitleLanguage, language))
422 m_subtitleLanguage.clear();
425 // two character codes as defined in ISO639
426 const CStdString& CLangInfo::GetDVDMenuLanguage() const
428 return m_currentRegion->m_strDVDMenuLanguage;
431 // two character codes as defined in ISO639
432 const CStdString& CLangInfo::GetDVDAudioLanguage() const
434 return m_currentRegion->m_strDVDAudioLanguage;
437 // two character codes as defined in ISO639
438 const CStdString& CLangInfo::GetDVDSubtitleLanguage() const
440 return m_currentRegion->m_strDVDSubtitleLanguage;
443 const CStdString& CLangInfo::GetLanguageLocale() const
445 return m_currentRegion->m_strLangLocaleName;
448 const CStdString& CLangInfo::GetRegionLocale() const
450 return m_currentRegion->m_strRegionLocaleName;
453 // Returns the format string for the date of the current language
454 const CStdString& CLangInfo::GetDateFormat(bool bLongDate/*=false*/) const
457 return m_currentRegion->m_strDateFormatLong;
459 return m_currentRegion->m_strDateFormatShort;
462 // Returns the format string for the time of the current language
463 const CStdString& CLangInfo::GetTimeFormat() const
465 return m_currentRegion->m_strTimeFormat;
468 const CStdString& CLangInfo::GetTimeZone() const
470 return m_currentRegion->m_strTimeZone;
473 // Returns the AM/PM symbol of the current language
474 const CStdString& CLangInfo::GetMeridiemSymbol(MERIDIEM_SYMBOL symbol) const
476 return m_currentRegion->m_strMeridiemSymbols[symbol];
479 // Fills the array with the region names available for this language
480 void CLangInfo::GetRegionNames(CStdStringArray& array)
482 for (ITMAPREGIONS it=m_regions.begin(); it!=m_regions.end(); ++it)
484 CStdString strName=it->first;
486 strName=g_localizeStrings.Get(416);
487 array.push_back(strName);
491 // Set the current region by its name, names from GetRegionNames() are valid.
492 // If the region is not found the first available region is set.
493 void CLangInfo::SetCurrentRegion(const CStdString& strName)
495 ITMAPREGIONS it=m_regions.find(strName);
496 if (it!=m_regions.end())
497 m_currentRegion=&it->second;
498 else if (!m_regions.empty())
499 m_currentRegion=&m_regions.begin()->second;
501 m_currentRegion=&m_defaultRegion;
503 m_currentRegion->SetGlobalLocale();
506 // Returns the current region set for this language
507 const CStdString& CLangInfo::GetCurrentRegion() const
509 return m_currentRegion->m_strName;
512 CLangInfo::TEMP_UNIT CLangInfo::GetTempUnit() const
514 return m_currentRegion->m_tempUnit;
517 // Returns the temperature unit string for the current language
518 const CStdString& CLangInfo::GetTempUnitString() const
520 return g_localizeStrings.Get(TEMP_UNIT_STRINGS+m_currentRegion->m_tempUnit);
523 CLangInfo::SPEED_UNIT CLangInfo::GetSpeedUnit() const
525 return m_currentRegion->m_speedUnit;
528 // Returns the speed unit string for the current language
529 const CStdString& CLangInfo::GetSpeedUnitString() const
531 return g_localizeStrings.Get(SPEED_UNIT_STRINGS+m_currentRegion->m_speedUnit);