1 /*
2  * Stellarium
3  * Copyright (C) 2006 Fabien Chereau
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
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.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA  02110-1335, USA.
18  */
19 
20 #include "StelLocaleMgr.hpp"
21 #include "StelApp.hpp"
22 #include "StelUtils.hpp"
23 #include "StelFileMgr.hpp"
24 
25 #include <QLocale>
26 #include <QDebug>
27 #include <QSettings>
28 #include <QString>
29 #include <QTextStream>
30 #include <QFile>
31 
32 #include <ctime>
33 
StelLocaleMgr()34 StelLocaleMgr::StelLocaleMgr()
35 	: skyTranslator(Q_NULLPTR)
36 	, planetaryFeaturesTranslator(Q_NULLPTR)
37 	, scriptsTranslator(Q_NULLPTR)
38 	, timeFormat()
39 	, dateFormat()
40 {
41 	core = StelApp::getInstance().getCore();
42 	createNameLists();
43 }
44 
45 
~StelLocaleMgr()46 StelLocaleMgr::~StelLocaleMgr()
47 {
48 	delete skyTranslator;
49 	skyTranslator = Q_NULLPTR;
50 	delete planetaryFeaturesTranslator;
51 	planetaryFeaturesTranslator = Q_NULLPTR;
52 	delete scriptsTranslator;
53 	scriptsTranslator = Q_NULLPTR;
54 }
55 
init()56 void StelLocaleMgr::init()
57 {
58 	QSettings* conf = StelApp::getInstance().getSettings();
59 	Q_ASSERT(conf);
60 
61 	setSkyLanguage(conf->value("localization/sky_locale", "system").toString(), false);
62 	setAppLanguage(conf->value("localization/app_locale", "system").toString(), false);
63 
64 	timeFormat = stringToSTimeFormat(conf->value("localization/time_display_format", "system_default").toString());
65 	dateFormat = stringToSDateFormat(conf->value("localization/date_display_format", "system_default").toString());
66 }
67 
68 /*************************************************************************
69  Set the application locale. This apply to GUI, console messages etc..
70 *************************************************************************/
setAppLanguage(const QString & newAppLanguageName,bool refreshAll)71 void StelLocaleMgr::setAppLanguage(const QString& newAppLanguageName, bool refreshAll)
72 {
73 	// Update the translator with new locale name
74 	Q_ASSERT(StelTranslator::globalTranslator);
75 	delete StelTranslator::globalTranslator;
76 	StelTranslator::globalTranslator = new StelTranslator("stellarium", newAppLanguageName);
77 	qDebug() << "Application language is " << StelTranslator::globalTranslator->getTrueLocaleName();
78 
79 	delete scriptsTranslator;
80 	// Update the translator with new locale name
81 	scriptsTranslator = new StelTranslator("stellarium-scripts", newAppLanguageName);
82 	qDebug() << "Scripts language is " << scriptsTranslator->getTrueLocaleName();
83 
84 	createNameLists();
85 	if (refreshAll)
86 		StelApp::getInstance().updateI18n();
87 }
88 
isAppRTL() const89 bool StelLocaleMgr::isAppRTL() const
90 {
91 	return QString("ar fa ckb ug ur he yi").contains(getAppLanguage());
92 }
93 
94 /*************************************************************************
95  Set the sky language.
96 *************************************************************************/
setSkyLanguage(const QString & newSkyLanguageName,bool refreshAll)97 void StelLocaleMgr::setSkyLanguage(const QString& newSkyLanguageName, bool refreshAll)
98 {
99 	delete skyTranslator;
100 	// Update the translator with new locale name
101 	skyTranslator = new StelTranslator("stellarium-skycultures", newSkyLanguageName);
102 	qDebug() << "Sky language is " << skyTranslator->getTrueLocaleName();
103 
104 	delete planetaryFeaturesTranslator;
105 	// Update the translator with new locale name
106 	planetaryFeaturesTranslator = new StelTranslator("stellarium-planetary-features", newSkyLanguageName);
107 	qDebug() << "Planetary features language is " << planetaryFeaturesTranslator->getTrueLocaleName();
108 
109 	if (refreshAll)
110 		StelApp::getInstance().updateI18n();
111 }
112 
113 /*************************************************************************
114  Get the language currently used for sky objects..
115 *************************************************************************/
getSkyLanguage() const116 QString StelLocaleMgr::getSkyLanguage() const
117 {
118 	return skyTranslator->getTrueLocaleName();
119 }
120 
isSkyRTL() const121 bool StelLocaleMgr::isSkyRTL() const
122 {
123 	return QString("ar fa ckb ug ur he yi").contains(getSkyLanguage());
124 }
125 
126 // Get the StelTranslator currently used for sky objects.
getSkyTranslator() const127 const StelTranslator& StelLocaleMgr::getSkyTranslator() const
128 {
129 	return *skyTranslator;
130 }
131 
getPlanetaryFeaturesTranslator() const132 const StelTranslator& StelLocaleMgr::getPlanetaryFeaturesTranslator() const
133 {
134 	return *planetaryFeaturesTranslator;
135 }
136 
getAppStelTranslator() const137 const StelTranslator &StelLocaleMgr::getAppStelTranslator() const
138 {
139 	return *StelTranslator::globalTranslator;
140 }
141 
getScriptsTranslator() const142 const StelTranslator& StelLocaleMgr::getScriptsTranslator() const
143 {
144 	return *scriptsTranslator;
145 }
146 
147 // Return the time in ISO 8601 format that is : %Y-%m-%d %H:%M:%S
getISO8601TimeLocal(double JD) const148 QString StelLocaleMgr::getISO8601TimeLocal(double JD) const
149 {
150 	return StelUtils::julianDayToISO8601String(JD + core->getUTCOffset(JD)*0.041666666666);
151 }
152 
153 //! get the six ints from an ISO8601 date time, understood to be local time, make a jdate out
154 //! of them.
getJdFromISO8601TimeLocal(const QString & t,bool * ok) const155 double StelLocaleMgr::getJdFromISO8601TimeLocal(const QString& t, bool* ok) const
156 {
157 	double jd = StelUtils::getJulianDayFromISO8601String(t, ok);
158 	if (!*ok)
159 	{
160 		qWarning() << "StelLocaleMgr::getJdFromISO8601TimeLocal: invalid ISO8601 date. Returning JD=0";
161 		return 0.0;
162 	}
163 
164 	jd -= core->getUTCOffset(jd)*0.041666666666;
165 	return jd;
166 }
167 
168 
169 // Return a string with the local date formated according to the dateFormat variable
getPrintableDateLocal(double JD) const170 QString StelLocaleMgr::getPrintableDateLocal(double JD) const
171 {
172 	int year, month, day, dayOfWeek;
173 	const double shift = core->getUTCOffset(JD)*0.041666666666;
174 	StelUtils::getDateFromJulianDay(JD+shift, &year, &month, &day);
175 	dayOfWeek = StelUtils::getDayOfWeek(year, month, day);
176 	QString str;
177 	switch (dateFormat)
178 	{
179 		case SDateMMDDYYYY:
180 			str = QString("%1-%2-%3").arg(month,2,10,QLatin1Char('0')).arg(day,2,10,QLatin1Char('0')).arg(year,4,10);
181 			break;
182 		case SDateDDMMYYYY:
183 			str = QString("%1-%2-%3").arg(day,2,10,QLatin1Char('0')).arg(month,2,10,QLatin1Char('0')).arg(year,4,10);
184 			break;
185 		case SDateYYYYMMDD:
186 			str = QString("%1-%2-%3").arg(year,4,10).arg(month,2,10,QLatin1Char('0')).arg(day,2,10,QLatin1Char('0'));
187 			break;
188 		case SDateWWMMDDYYYY:
189 			str = QString("%1, %2-%3-%4").arg(shortDayName(dayOfWeek)).arg(month,2,10,QLatin1Char('0')).arg(day,2,10,QLatin1Char('0')).arg(year,4,10);
190 			break;
191 		case SDateWWDDMMYYYY:
192 			str = QString("%1, %2-%3-%4").arg(shortDayName(dayOfWeek)).arg(day,2,10,QLatin1Char('0')).arg(month,2,10,QLatin1Char('0')).arg(year,4,10);
193 			break;
194 		case SDateWWYYYYMMDD:
195 			str = QString("%1, %2-%3-%4").arg(shortDayName(dayOfWeek)).arg(year,4,10).arg(month,2,10,QLatin1Char('0')).arg(day,2,10,QLatin1Char('0'));
196 			break;
197 		case SDateSystemDefault:
198 			str = StelUtils::localeDateString(year, month, day, dayOfWeek);
199 			break;
200 		default:
201 			qWarning() << "WARNING: unknown date format fallback to system default";
202 			str = StelUtils::localeDateString(year, month, day, dayOfWeek);
203 	}
204 	return str;
205 }
206 
207 // Return a string with the local time (according to timeZoneMode variable) formated
208 // according to the timeFormat variable
getPrintableTimeLocal(double JD) const209 QString StelLocaleMgr::getPrintableTimeLocal(double JD) const
210 {
211 	int hour, minute, second, millsec;
212 	const double shift = core->getUTCOffset(JD)*0.041666666666;
213 	StelUtils::getTimeFromJulianDay(JD+shift, &hour, &minute, &second, &millsec);
214 	QTime t(hour, minute, second, millsec);
215 	switch (timeFormat)
216 	{
217 		case STimeSystemDefault:
218 			return t.toString();
219 		case STime24h:
220 			return t.toString("hh:mm:ss");
221 		case STime12h:
222 			return t.toString("hh:mm:ss AP");
223 		default:
224 			qWarning() << "WARNING: unknown time format, fallback to system default";
225 			return t.toString(Qt::DefaultLocaleShortDate);
226 	}
227 }
228 
getPrintableTimeZoneLocal(double JD) const229 QString StelLocaleMgr::getPrintableTimeZoneLocal(double JD) const
230 {
231 	QString timeZone = "";
232 	QString timeZoneST = "";
233 	if (core->getCurrentLocation().planetName=="Earth")
234 	{
235 		QString currTZ = core->getCurrentTimeZone();
236 
237 		if (JD<=StelCore::TZ_ERA_BEGINNING || currTZ.contains("auto") || currTZ.contains("LMST"))
238 		{
239 			// TRANSLATORS: Local Mean Solar Time. Please use abbreviation.
240 			timeZoneST = qc_("LMST", "solar time");
241 		}
242 
243 		if (currTZ.contains("LTST"))
244 		{
245 			// TRANSLATORS: Local True Solar Time. Please use abbreviation.
246 			timeZoneST = qc_("LTST", "solar time");
247 		}
248 
249 		const double shift = core->getUTCOffset(JD);
250 		QTime tz = QTime(0, 0, 0).addSecs(static_cast<int>(3600*qAbs(shift)));
251 		if(shift<0.0)
252 			timeZone = QString("UTC-%1").arg(tz.toString("hh:mm"));
253 		else
254 			timeZone = QString("UTC+%1").arg(tz.toString("hh:mm"));
255 
256 		if (!timeZoneST.isEmpty() && !core->getUseCustomTimeZone())
257 			timeZone = QString("%1 (%2)").arg(timeZone, timeZoneST);
258 	}
259 	else
260 	{
261 		// TODO: Make sure LMST/LTST would make sense on other planet, or inhibit it?
262 		const double shift = core->getUTCOffset(JD);
263 		QTime tz = QTime(0, 0, 0).addSecs(static_cast<int>(3600*qAbs(shift)));
264 		if(shift<0.0)
265 			timeZone = QString("UTC-%1").arg(tz.toString("hh:mm"));
266 		else
267 			timeZone = QString("UTC+%1").arg(tz.toString("hh:mm"));
268 
269 		//if (!timeZoneST.isEmpty() && !core->getUseCustomTimeZone())
270 		//	timeZone = QString("%1 (%2)").arg(timeZone, timeZoneST);
271 	}
272 	return timeZone;
273 }
274 
275 // Convert the time format enum to its associated string and reverse
stringToSTimeFormat(const QString & tf)276 StelLocaleMgr::STimeFormat StelLocaleMgr::stringToSTimeFormat(const QString& tf)
277 {
278 	static const QMap<QString, StelLocaleMgr::STimeFormat> map = {
279 		{"system_default", STimeSystemDefault},
280 		{"24h", STime24h},
281 		{"12h", STime12h}};
282 	if (!map.contains(tf))
283 		qWarning() << "WARNING: unrecognized time_display_format : " << tf << " system_default used.";
284 	return map.value(tf, STimeSystemDefault);
285 }
286 
sTimeFormatToString(STimeFormat tf)287 QString StelLocaleMgr::sTimeFormatToString(STimeFormat tf)
288 {
289 	static const QStringList tfmt={"system_default", "24h", "12h"};
290 	return tfmt[tf];
291 }
292 
293 // Convert the date format enum to its associated string and reverse
stringToSDateFormat(const QString & df)294 StelLocaleMgr::SDateFormat StelLocaleMgr::stringToSDateFormat(const QString& df)
295 {
296 	static const QMap<QString, StelLocaleMgr::SDateFormat> map = {
297 		{"system_default", SDateSystemDefault},
298 		{"mmddyyyy",       SDateMMDDYYYY},
299 		{"ddmmyyyy",       SDateDDMMYYYY},
300 		{"yyyymmdd",       SDateYYYYMMDD}, // iso8601
301 		{"wwmmddyyyy",     SDateWWMMDDYYYY},
302 		{"wwddmmyyyy",     SDateWWDDMMYYYY},
303 		{"wwyyyymmdd",     SDateWWYYYYMMDD}};
304 	if (!map.contains(df))
305 		qWarning() << "WARNING: unrecognized date_display_format : " << df << " system_default used.";
306 	return map.value(df, SDateSystemDefault);
307 }
308 
sDateFormatToString(SDateFormat df)309 QString StelLocaleMgr::sDateFormatToString(SDateFormat df)
310 {
311 	QStringList dfmt = {
312 		"system_default",
313 		"mmddyyyy",
314 		"ddmmyyyy",
315 		"yyyymmdd",
316 		"wwddmmyyyy",
317 		"wwddmmyyyy",
318 		"wwyyyymmdd"};
319 	return dfmt[df];
320 }
321 
getQtDateFormatStr() const322 QString StelLocaleMgr::getQtDateFormatStr() const
323 {
324 	QStringList dfmt = {
325 		"yyyy.MM.dd",
326 		"MM.dd.yyyy",
327 		"dd.MM.yyyy",
328 		"yyyy.MM.dd",
329 		"ddd, MM.dd.yyyy",
330 		"ddd, dd.MM.yyyy",
331 		"ddd, yyyy.MM.dd"};
332 	return dfmt[dateFormat];
333 }
334 
shortDayName(int weekday)335 QString StelLocaleMgr::shortDayName(int weekday)
336 {
337 	Q_ASSERT(weekday>=0);
338 	Q_ASSERT(shortWeekDays.length()==7);
339 	return shortWeekDays[weekday % 7];
340 }
341 
longDayName(int weekday)342 QString StelLocaleMgr::longDayName(int weekday)
343 {
344 	Q_ASSERT(weekday>=0);
345 	Q_ASSERT(longWeekDays.length()==7);
346 	return longWeekDays[weekday % 7];
347 }
348 
shortMonthName(int month)349 QString StelLocaleMgr::shortMonthName(int month)
350 {
351 	Q_ASSERT(month >= 0);
352 	Q_ASSERT(shortMonthNames.length()==12);
353 	return shortMonthNames[month % 12];
354 }
355 
longMonthName(int month)356 QString StelLocaleMgr::longMonthName(int month)
357 {
358 	Q_ASSERT(month >= 0);
359 	Q_ASSERT(longMonthNames.length()==12);
360 	return longMonthNames[month % 12];
361 }
362 
longGenitiveMonthName(int month)363 QString StelLocaleMgr::longGenitiveMonthName(int month)
364 {
365 	Q_ASSERT(month >= 0);
366 	Q_ASSERT(longGenitiveMonthNames.length()==12);
367 	return longGenitiveMonthNames[month % 12];
368 }
369 
romanMonthName(int month)370 QString StelLocaleMgr::romanMonthName(int month)
371 {
372 	Q_ASSERT(month >= 0);
373 	static const QStringList romanMonths = { "XII", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XI"};
374 	return romanMonths[month % 12];
375 }
376 
377 
createNameLists()378 void StelLocaleMgr::createNameLists()
379 {
380 	shortWeekDays.clear();
381 	shortWeekDays
382 		<< qc_("Sun", "short day name")
383 		<< qc_("Mon", "short day name")
384 		<< qc_("Tue", "short day name")
385 		<< qc_("Wed", "short day name")
386 		<< qc_("Thu", "short day name")
387 		<< qc_("Fri", "short day name")
388 		<< qc_("Sat", "short day name");
389 	longWeekDays.clear();
390 	longWeekDays
391 		<< qc_("Sunday",    "long day name")
392 		<< qc_("Monday",    "long day name")
393 		<< qc_("Tuesday",   "long day name")
394 		<< qc_("Wednesday", "long day name")
395 		<< qc_("Thursday",  "long day name")
396 		<< qc_("Friday",    "long day name")
397 		<< qc_("Saturday",  "long day name");
398 
399 	shortMonthNames.clear();
400 	shortMonthNames
401 		<< qc_("Dec", "short month name")
402 		<< qc_("Jan", "short month name")
403 		<< qc_("Feb", "short month name")
404 		<< qc_("Mar", "short month name")
405 		<< qc_("Apr", "short month name")
406 		<< qc_("May", "short month name")
407 		<< qc_("Jun", "short month name")
408 		<< qc_("Jul", "short month name")
409 		<< qc_("Aug", "short month name")
410 		<< qc_("Sep", "short month name")
411 		<< qc_("Oct", "short month name")
412 		<< qc_("Nov", "short month name");
413 
414 	longMonthNames.clear();
415 	longMonthNames
416 		<< qc_("December",  "long month name")
417 		<< qc_("January",   "long month name")
418 		<< qc_("February",  "long month name")
419 		<< qc_("March",     "long month name")
420 		<< qc_("April",     "long month name")
421 		<< qc_("May",       "long month name")
422 		<< qc_("June",      "long month name")
423 		<< qc_("July",      "long month name")
424 		<< qc_("August",    "long month name")
425 		<< qc_("September", "long month name")
426 		<< qc_("October",   "long month name")
427 		<< qc_("November",  "long month name");
428 	longGenitiveMonthNames.clear();
429 	longGenitiveMonthNames
430 		<< qc_("December",  "genitive")
431 		<< qc_("January",   "genitive")
432 		<< qc_("February",  "genitive")
433 		<< qc_("March",     "genitive")
434 		<< qc_("April",     "genitive")
435 		<< qc_("May",       "genitive")
436 		<< qc_("June",      "genitive")
437 		<< qc_("July",      "genitive")
438 		<< qc_("August",    "genitive")
439 		<< qc_("September", "genitive")
440 		<< qc_("October",   "genitive")
441 		<< qc_("November",  "genitive");
442 }
443 
444 QStringList StelLocaleMgr::shortWeekDays;
445 QStringList StelLocaleMgr::longWeekDays;
446 QStringList StelLocaleMgr::shortMonthNames;
447 QStringList StelLocaleMgr::longMonthNames;
448 QStringList StelLocaleMgr::longGenitiveMonthNames;
449