1 /*
2  * \copyright Copyright (c) 2014-2021 Governikus GmbH & Co. KG, Germany
3  */
4 
5 #include "LanguageLoader.h"
6 
7 #include "FileDestination.h"
8 #include "SingletonHelper.h"
9 
10 #include <QCoreApplication>
11 #include <QDir>
12 #include <QLoggingCategory>
13 #include <QRegularExpression>
14 
15 Q_DECLARE_LOGGING_CATEGORY(language)
16 
17 using namespace governikus;
18 
19 defineSingleton(LanguageLoader)
20 
21 const QLocale::Language LanguageLoader::mFallbackLanguage = QLocale::Language::English;
22 
23 QLocale LanguageLoader::mDefaultLanguage = QLocale::system();
24 
LanguageLoader()25 LanguageLoader::LanguageLoader()
26 	: mPath(FileDestination::getPath(QStringLiteral("translations"), QStandardPaths::LocateDirectory))
27 	, mTranslatorList()
28 	, mComponentList(
29 		{
30 			QStringLiteral("ausweisapp2"), QStringLiteral("qtbase")
31 		})
32 	, mUsedLocale(mFallbackLanguage)
33 {
34 }
35 
36 
~LanguageLoader()37 LanguageLoader::~LanguageLoader()
38 {
39 }
40 
41 
getDefaultLanguage()42 const QLocale& LanguageLoader::getDefaultLanguage()
43 {
44 	return mDefaultLanguage;
45 }
46 
47 
setDefaultLanguage(const QLocale & pLocale)48 void LanguageLoader::setDefaultLanguage(const QLocale& pLocale)
49 {
50 	mDefaultLanguage = pLocale;
51 }
52 
53 
getLocalCode(const QLocale & pLocale)54 QString LanguageLoader::getLocalCode(const QLocale& pLocale)
55 {
56 	return pLocale.bcp47Name().left(2);
57 }
58 
59 
60 #ifndef QT_NO_DEBUG
getPath() const61 const QString& LanguageLoader::getPath() const
62 {
63 	return mPath;
64 }
65 
66 
setPath(const QString & pPath)67 void LanguageLoader::setPath(const QString& pPath)
68 {
69 	mPath = pPath;
70 }
71 
72 
73 #endif
74 
75 
createTranslator(const QLocale & pLocale,const QString & pComponent)76 QSharedPointer<QTranslator> LanguageLoader::createTranslator(const QLocale& pLocale, const QString& pComponent)
77 {
78 	auto translator = QSharedPointer<QTranslator>::create();
79 	translator->setObjectName(pLocale.name() + QLatin1Char('/') + pComponent);
80 	bool loaded = translator->load(pLocale, pComponent, QStringLiteral("_"), mPath);
81 	qCDebug(language) << "Load translation:" << pComponent << '|' << loaded;
82 
83 	if (!loaded)
84 	{
85 		translator.clear();
86 	}
87 
88 	return translator;
89 }
90 
91 
getAvailableLocales() const92 QList<QLocale> LanguageLoader::getAvailableLocales() const
93 {
94 	qCDebug(language) << "Use path:" << mPath;
95 
96 	QList<QLocale> list;
97 	list << mFallbackLanguage;
98 
99 	QDir path(mPath);
100 	path.setFilter(QDir::Files);
101 
102 	const QRegularExpression regex(QStringLiteral("^ausweisapp2_([a-z]{2}(_[A-Z]{2})?)\\.qm$"));
103 	const auto entryList = path.entryInfoList();
104 	for (const auto& info : entryList)
105 	{
106 		QRegularExpressionMatch match = regex.match(info.fileName());
107 		if (match.hasMatch() && !match.captured(1).isNull())
108 		{
109 			QLocale locale(match.captured(1));
110 			if (locale != QLocale::C && !list.contains(locale))
111 			{
112 				list << locale;
113 			}
114 		}
115 	}
116 
117 	return list;
118 }
119 
120 
isLoaded() const121 bool LanguageLoader::isLoaded() const
122 {
123 	return !mTranslatorList.isEmpty();
124 }
125 
126 
load(const QLocale & pLocale)127 void LanguageLoader::load(const QLocale& pLocale)
128 {
129 	if (!mTranslatorList.isEmpty())
130 	{
131 		qCWarning(language) << "Loader is already in use. You need to unload before you load again...";
132 		return;
133 	}
134 
135 	qCDebug(language) << "Use path:" << mPath;
136 	bool loaded = false;
137 
138 	const auto uiLanguages = pLocale.uiLanguages();
139 	for (const auto& lang : uiLanguages)
140 	{
141 		qCDebug(language) << "UI language:" << lang;
142 		if (!loaded)
143 		{
144 			loaded = loadTranslationFiles(QLocale(lang));
145 		}
146 	}
147 
148 	if (!loaded && !pLocale.uiLanguages().contains(pLocale.name().replace(QLatin1Char('_'), QLatin1Char('-'))))
149 	{
150 		qCDebug(language) << "No UI translation found... try to use system locale:" << pLocale.name();
151 		loadTranslationFiles(QLocale(pLocale.name()));
152 	}
153 }
154 
155 
unload()156 void LanguageLoader::unload()
157 {
158 	if (mTranslatorList.isEmpty())
159 	{
160 		qCWarning(language) << "Cannot unload translation because it is nothing loaded";
161 	}
162 	else
163 	{
164 		qCDebug(language) << "Unload translations";
165 		for (const auto& translator : qAsConst(mTranslatorList))
166 		{
167 			QCoreApplication::removeTranslator(translator.data());
168 		}
169 
170 		mTranslatorList.clear();
171 		mUsedLocale = mFallbackLanguage;
172 	}
173 }
174 
175 
getUsedLocale() const176 const QLocale& LanguageLoader::getUsedLocale() const
177 {
178 	return mUsedLocale;
179 }
180 
181 
getFallbackLanguage() const182 QLocale::Language LanguageLoader::getFallbackLanguage() const
183 {
184 	return mFallbackLanguage;
185 }
186 
187 
loadTranslationFiles(const QLocale & pLocale)188 bool LanguageLoader::loadTranslationFiles(const QLocale& pLocale)
189 {
190 	if (pLocale.language() == mFallbackLanguage)
191 	{
192 		qCDebug(language) << "Using fallback language:" << QLocale::languageToString(mFallbackLanguage);
193 		return true;
194 	}
195 
196 	qCDebug(language) << "Try to load translation:" << pLocale;
197 	bool loaded = false;
198 
199 	for (const auto& component : mComponentList)
200 	{
201 		QSharedPointer<QTranslator> translator = createTranslator(pLocale, component);
202 
203 		if (!translator.isNull())
204 		{
205 			mTranslatorList += translator;
206 			QCoreApplication::installTranslator(translator.data());
207 			mUsedLocale = pLocale;
208 			loaded = true;
209 		}
210 	}
211 
212 	return loaded;
213 }
214