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

Stellarium / stellarium / 13260145531

11 Feb 2025 09:41AM UTC coverage: 12.127% (+0.03%) from 12.101%
13260145531

Pull #3751

github

10110111
Restore deleted additional SC files
Pull Request #3751: Switch skycultures to the new format

14613 of 120497 relevant lines covered (12.13%)

18620.19 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
        auto 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 += chars[0];
×
150
                }
151
                if (units)
×
152
                {
153
                        number += chars[units];
×
154
                }
155
        }
156

157
        if (!addedPresent) return translatedConstellation + number;
×
158

159
        const auto& translatedAdded = qtranslate("Added", "skyculture");
×
160
        return QString("%1 %2%3").arg(translatedConstellation, translatedAdded, number);
×
161
}
×
162

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

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

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

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

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

200
        qWarning().noquote() << "System language (ISO 639 / ISO 3166):" << systemLangName;
×
201
}
×
202

203

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

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

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

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

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

235
        return languageName;
×
236
}
237

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

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

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

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

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

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

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

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

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

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

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

325
bool StelSkyTranslator::isEmpty() const
×
326
{
327
        return StelTranslator::isEmpty() && commonSkyTranslator.isEmpty();
×
328
}
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