• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Stellarium / stellarium / 15291801018

28 May 2025 04:52AM UTC coverage: 11.931% (-0.02%) from 11.951%
15291801018

push

github

alex-w
Added new set of navigational stars (XIX century)

0 of 6 new or added lines in 2 files covered. (0.0%)

14124 existing lines in 74 files now uncovered.

14635 of 122664 relevant lines covered (11.93%)

18291.42 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/core/StelTranslator.cpp
1
/*
2
* Stellarium
3
* Copyright (C) 2005 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 "StelTranslator.hpp"
21
#include "StelFileMgr.hpp"
22

23
#include <QFile>
24
#include <QDebug>
25
#include <QStringList>
26
#include <QRegularExpression>
27
#include <QLocale>
28
#include <QDir>
29
#include <QTranslator>
30

31
namespace
32
{
33
int parseRomanNumeral(const QStringView& roman)
×
34
{
35
        const auto romanLat = roman.toLatin1();
×
36
        int v = 0;
×
37
        const char* p = romanLat.data();
×
38

39
        /**/ if (strncmp(p, "XC",   2) == 0) { v += 90; p += 2; }
×
40
        else if (strncmp(p, "LXXX", 4) == 0) { v += 80; p += 4; }
×
41
        else if (strncmp(p, "LXX",  3) == 0) { v += 70; p += 3; }
×
42
        else if (strncmp(p, "LX",   2) == 0) { v += 60; p += 2; }
×
43
        else if (strncmp(p, "L",    1) == 0) { v += 50; p += 1; }
×
44
        else if (strncmp(p, "XL",   2) == 0) { v += 40; p += 2; }
×
45
        else if (strncmp(p, "XXX",  3) == 0) { v += 30; p += 3; }
×
46
        else if (strncmp(p, "XX",   2) == 0) { v += 20; p += 2; }
×
47
        else if (strncmp(p, "X",    1) == 0) { v += 10; p += 1; }
×
48

49
        /**/ if (strncmp(p, "IX",   2) == 0) { v += 9;  p += 2; }
×
50
        else if (strncmp(p, "VIII", 4) == 0) { v += 8;  p += 4; }
×
51
        else if (strncmp(p, "VII",  3) == 0) { v += 7;  p += 3; }
×
52
        else if (strncmp(p, "VI",   2) == 0) { v += 6;  p += 2; }
×
53
        else if (strncmp(p, "V",    1) == 0) { v += 5;  p += 1; }
×
54
        else if (strncmp(p, "IV",   2) == 0) { v += 4;  p += 2; }
×
55
        else if (strncmp(p, "III",  3) == 0) { v += 3;  p += 3; }
×
56
        else if (strncmp(p, "II",   2) == 0) { v += 2;  p += 2; }
×
57
        else if (strncmp(p, "I",    1) == 0) { v += 1;  p += 1; }
×
58

59
        return v;
×
60
}
×
61
}
62

63
// Init static members
64
QMap<QString, QString> StelTranslator::iso639codes;
65
QString StelTranslator::systemLangName;
66

67
// Use system locale language by default
68
StelTranslator* StelTranslator::globalTranslator = Q_NULLPTR;
69

70
StelTranslator::StelTranslator(const QString& adomain, const QString& alangName)
×
71
        : domain(adomain),
×
72
          langName(alangName)
×
73
{
74
        translator = new QTranslator();
×
75
        bool res = translator->load(StelFileMgr::getLocaleDir()+"/"+adomain+"/"+getTrueLocaleName()+".qm");
×
76
        if (!res)
×
77
                qWarning() << "Couldn't load translations for language " << getTrueLocaleName() << "in section" << adomain;
×
78
        if (translator->isEmpty())
×
79
                qWarning() << "Empty translation file for language " << getTrueLocaleName() << "in section" << adomain;
×
80
}
×
81

82
StelTranslator::~StelTranslator()
×
83
{
84
        delete translator;
×
85
        translator = Q_NULLPTR;
×
86
}
×
87

88
bool StelTranslator::isEmpty() const
×
89
{
90
        return translator->isEmpty();
×
91
}
92

93
QString StelTranslator::qtranslate(const QString& s, const QString& c) const
×
94
{
95
        if (s.isEmpty())
×
96
                return "";
×
97
        const auto res = tryQtranslate(s, c);
×
98
        if (res.isEmpty())
×
99
                return s;
×
100
        return res;
×
101
}
×
102

103
QString StelTranslator::qTranslateStar(const QString& s, const QString& c) const
×
104
{
105
        if (s.isEmpty())
×
106
                return "";
×
107
        const auto res = tryQtranslateStar(s, c);
×
108
        if (res.isEmpty())
×
109
                return s;
×
110
        return res;
×
111
}
×
112

113
QString StelTranslator::tryTranslateChineseStar(const QString& s, const QString& c) const
×
114
{
115
        static const auto re = []{ QRegularExpression re("(.+)( [IXVLCDM]+)([*?]*)$"); re.optimize(); return re; }();
×
116
        const auto match = re.match(s);
×
117
        if (!match.hasMatch()) return {};
×
118

119
        auto constellation = match.captured(1);
×
120
        bool addedPresent = false;
×
121
        static const QString addedPattern(" Added");
×
122
        if (constellation.endsWith(addedPattern))
×
123
        {
124
                constellation.chop(addedPattern.length());
×
125
                addedPresent = true;
×
126
        }
127

128
        const auto translatedConstellation = tryQtranslate(constellation, c);
×
129
        if (translatedConstellation.isEmpty()) return {};
×
130

131
        QString number = match.captured(2);
×
132
        if (getTrueLocaleName().startsWith("zh"))
×
133
        {
134
                const auto num = parseRomanNumeral(QStringView(number).mid(1));
×
135
                Q_ASSERT(num < 100);
×
136

137
                static const char16_t chars[10+1] = u"十一二三四五六七八九";
138

139
                number = "";
×
140
                int tens = num / 10;
×
141
                const int units = num % 10;
×
142
                if (tens >= 2)
×
143
                {
144
                        number += chars[tens];
×
145
                        tens = 1;
×
146
                }
147
                if (tens == 1)
×
148
                {
149
                        number += QChar(chars[0]);
×
150
                }
151
                if (units)
×
152
                {
153
                        number += chars[units];
×
154
                }
155
        }
156
        const QString extra = match.captured(3);
×
157
        if (!addedPresent) return translatedConstellation + number + extra;
×
158

159
        // TRANSLATORS: The string " Added" stars with a space. Carefully keep the space if needed with your language.
160
        const QString& translatedAdded = qtranslate(" Added", "chinese skycultures");
×
161
        return translatedConstellation + translatedAdded + number + extra;
×
UNCOV
162
}
×
163

UNCOV
164
QString StelTranslator::tryQtranslate(const QString &s, const QString &c) const
×
165
{
UNCOV
166
        return translator->translate("", s.toUtf8().constData(),c.toUtf8().constData());
×
167
}
168

UNCOV
169
QString StelTranslator::tryQtranslateStar(const QString &s, const QString &c) const
×
170
{
171
        const auto translated = tryTranslateChineseStar(s, c);
×
172
        if (!translated.isEmpty()) return translated;
×
173
        return tryQtranslate(s, c);
×
UNCOV
174
}
×
175

176
        
177
//! Initialize Translation
178
//! @param fileName file containing the list of language codes
UNCOV
179
void StelTranslator::init(const QString& fileName)
×
180
{
181
        StelTranslator::initSystemLanguage();
×
UNCOV
182
        StelTranslator::initIso639_1LanguageCodes(fileName);
×
183
        
184
        Q_ASSERT(StelTranslator::globalTranslator==Q_NULLPTR);
×
185
        StelTranslator::globalTranslator = new StelTranslator("stellarium", "system");
×
UNCOV
186
}
×
187

188
//! Try to determine system language from system configuration
UNCOV
189
void StelTranslator::initSystemLanguage()
×
190
{
191
        systemLangName = QLocale::system().name();
×
192
        if (systemLangName.isEmpty())
×
UNCOV
193
                systemLangName = "en";
×
194

195
        //change systemLangName to ISO 639 / ISO 3166.
196
        int pos = systemLangName.indexOf(':', 0);
×
197
        if (pos != -1) systemLangName.resize(pos);
×
198
        pos = systemLangName.indexOf('.', 0);
×
UNCOV
199
        if (pos != -1) systemLangName.resize(pos);
×
200

201
        qInfo().noquote() << "System language (ISO 639 / ISO 3166):" << systemLangName;
×
UNCOV
202
}
×
203

204

205
//! Convert from ISO639-1 2(+3) letters language code to native language name
UNCOV
206
QString StelTranslator::iso639_1CodeToNativeName(const QString& languageCode)
×
207
{
208
        if (languageCode=="C")
×
UNCOV
209
                return "English";
×
210

211
        //QLocale loc(languageCode);
212
        //QString l = loc.name();
213
        // There is a QLocale for this code.  This should be the case for most
214
        // language codes, but there are a few without QLocales, e.g. Interlingua
215
        //if (l.contains('_'))
216
        //        l.truncate(l.indexOf('_'));
217
        //if (iso639codes.find(l)!=iso639codes.end())
218
        //        return iso639codes[l]+ (languageCode.size()==2 ? "" : QString(" (")+QLocale::countryToString(loc.country())+")");
219

220
        // For codes which return the locale C, use the language code to do the lookup
221
        if (iso639codes.contains(languageCode))
×
UNCOV
222
                return iso639codes[languageCode];
×
223

224
        // qWarning() << "Cannot determine name of language for code" << languageCode;
UNCOV
225
        return languageCode;
×
226
}
227

228
//! Convert from native language name to ISO639-1 2(+3) letters language code
UNCOV
229
QString StelTranslator::nativeNameToIso639_1Code(const QString& languageName)
×
230
{
231
        QMap<QString, QString>::ConstIterator iter;
×
232
        for (iter=iso639codes.constBegin();iter!=iso639codes.constEnd();++iter)
×
233
                if (iter.value() == languageName)
×
UNCOV
234
                        return iter.key();
×
235

UNCOV
236
        return languageName;
×
237
}
238

239
//! Get available native language names from directory tree
UNCOV
240
QStringList StelTranslator::getAvailableLanguagesNamesNative(const QString& localeDir, const QString& section) const
×
241
{
242
        QString tmpDir = localeDir;
×
243
        if (section.isEmpty() || section=="stellarium")
×
UNCOV
244
                tmpDir.append("/stellarium/");
×
245
        else
246
                tmpDir.append("/stellarium-" + section + "/");
×
247
        const QStringList codeList = getAvailableIso639_1Codes(tmpDir);
×
248
        QStringList output;
×
UNCOV
249
        for (const auto& lang : codeList)
×
250
        {
UNCOV
251
                output += iso639_1CodeToNativeName(lang);
×
252
        }
253
        return output;
×
UNCOV
254
}
×
255

256
//! Get available language codes from directory tree
UNCOV
257
QStringList StelTranslator::getAvailableIso639_1Codes(const QString& localeDir)
×
258
{
UNCOV
259
        QDir dir(localeDir);
×
260

UNCOV
261
        if (!dir.exists())
×
262
        {
263
                qWarning() << "Unable to find locale directory containing translations:" << QDir::toNativeSeparators(localeDir);
×
UNCOV
264
                return QStringList();
×
265
        }
266

267
        const QStringList entries=dir.entryList(QDir::Files, QDir::Name);
×
268
        QStringList result;
×
UNCOV
269
        for (auto path : entries)
×
270
        {
271
                if (!path.endsWith(".qm"))
×
272
                        continue;
×
273
                path.chop(3);
×
274
                result.append(path);
×
275
        }
×
276
        return result;
×
UNCOV
277
}
×
278

279
//! Initialize the languages code list from the passed file
280
//! @param fileName file containing the list of language codes
UNCOV
281
void StelTranslator::initIso639_1LanguageCodes(const QString& fileName)
×
282
{
283
        QFile inf(fileName);
×
UNCOV
284
        if (!inf.open(QIODevice::ReadOnly))
×
285
        {
286
                qWarning() << "Can't open ISO639 codes file " << QDir::toNativeSeparators(fileName);
×
UNCOV
287
                Q_ASSERT(0);
×
288
        }
289

290
        if (!iso639codes.empty())
×
UNCOV
291
                iso639codes.clear();
×
292

UNCOV
293
        while (!inf.atEnd())
×
294
        {
UNCOV
295
                QString record = QString::fromUtf8(inf.readLine());
×
296

297
                if (record.startsWith("//") || record.startsWith("#") || record.isEmpty()) // skip comments and empty lines
×
UNCOV
298
                        continue;
×
299

300
                static const QRegularExpression nlExp("[\\n\\r]*$");
×
UNCOV
301
                record.remove(nlExp); // chomp new lines
×
302
                #if (QT_VERSION>=QT_VERSION_CHECK(5, 14, 0))
UNCOV
303
                const QStringList& fields = record.split("\t", Qt::SkipEmptyParts);
×
304
                #else
305
                const QStringList& fields = record.split("\t", QString::SkipEmptyParts);
306
                #endif
307
                iso639codes.insert(fields.at(0), fields.at(2));
×
308
        }
×
UNCOV
309
}
×
310

UNCOV
311
StelSkyTranslator::StelSkyTranslator(const QString& langName)
×
312
        : StelTranslator("stellarium-skycultures", langName)
UNCOV
313
        , commonSkyTranslator("stellarium-sky", langName)
×
314
{
315
        if (commonSkyTranslator.isEmpty())
×
316
                qWarning() << "Empty skyculture-independent translation file for language " << getTrueLocaleName();
×
UNCOV
317
}
×
318

UNCOV
319
QString StelSkyTranslator::tryQtranslate(const QString& s, const QString& c) const
×
320
{
321
        const auto res = StelTranslator::tryQtranslate(s, c);
×
322
        if (!res.isEmpty()) return res;
×
323
        return commonSkyTranslator.tryQtranslate(s, c);
×
UNCOV
324
}
×
325

UNCOV
326
bool StelSkyTranslator::isEmpty() const
×
327
{
UNCOV
328
        return StelTranslator::isEmpty() && commonSkyTranslator.isEmpty();
×
329
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc