localization.cpp
1 #include "localization.h"
2 
3 #include <QApplication>
4 #include <QDebug>
5 #include <QDir>
6 #include <QFile>
7 #include <QLibraryInfo>
8 #include <QMessageBox>
9 #include <QTranslator>
10 #include <QSet>
11 #include <QStringList>
12 #include "plugins/engineplugin.h"
13 #include "plugins/pluginloader.h"
14 #include "datapaths.h"
15 #include "log.h"
16 
17 const QString DEFINITION_FILE_PATTERN = "*.def";
18 const QString TRANSLATIONS_LOCATION_SUBDIR = "translations";
19 const int NUM_VALID_VERSION_TOKENS = 2;
20 const int NUM_VALID_TOKENS = 3;
21 
22 QList<QTranslator*> Localization::currentlyLoadedTranslations;
23 
24 class Localization::LocalizationLoader
25 {
26  public:
27  LocalizationLoader() {};
28 
29  QList<LocalizationInfo> loadLocalizationsList(const QStringList& definitionsFileSearchDirs);
30 
31  private:
32  QList<LocalizationInfo> localizations;
33 
34  void loadLocalizationsListFile(const QString& definitionsFilePath);
35  void loadLocalizationsListFile(QIODevice& io);
36 
43  int obtainVersion(QIODevice& io);
44 
45  void sort();
46 };
47 
48 bool localizationInfoLessThan(const LocalizationInfo &o1, const LocalizationInfo &o2)
49 {
50  return o1.localeName.toLower() < o2.localeName.toLower();
51 }
52 
53 QList<LocalizationInfo> Localization::loadLocalizationsList(const QStringList& definitionsFileSearchDirs)
54 {
55  LocalizationLoader l;
56  return l.loadLocalizationsList(definitionsFileSearchDirs);
57 }
58 
59 bool Localization::loadTranslation(const QString& localeName)
60 {
61  // Out with the old.
62  qDeleteAll(currentlyLoadedTranslations);
63  currentlyLoadedTranslations.clear();
64  // In with the new.
65  QStringList searchPaths = DataPaths::staticDataSearchDirs(
66  TRANSLATIONS_LOCATION_SUBDIR);
67  // Qt library translator.
68  // First let's try to load translation that is bundled with program.
69  // This behavior is valid for Windows.
70  QTranslator* qtTranslator = loadTranslationFile("qt_" + localeName, searchPaths);
71  if (qtTranslator == NULL)
72  {
73  // If Qt translation is not bundled with program then try to load
74  // it from system location. This behavior is valid for Linux.
75  qtTranslator = new QTranslator();
76  qtTranslator->load("qt_" + localeName,
77  QLibraryInfo::location(QLibraryInfo::TranslationsPath));
78  QCoreApplication::installTranslator(qtTranslator);
79  }
80  currentlyLoadedTranslations.append(qtTranslator);
81 
82  // Doomseeker translator.
83  QTranslator* myappTranslator = loadTranslationFile(localeName, searchPaths);
84  if (myappTranslator != NULL)
85  {
86  currentlyLoadedTranslations.append(myappTranslator);
87  }
88 
89  // Plugins translators.
90  foreach (const PluginLoader::Plugin *plugin, gPlugins->plugins())
91  {
92  QString name = plugin->info()->nameCanonical();
93  QTranslator *pluginTranslator = loadTranslationFile(
94  QString("%1_%2").arg(name, localeName),
95  searchPaths);
96  if (pluginTranslator)
97  {
98  gLog << QString("Loaded translation for plugin %1").arg(name);
99  currentlyLoadedTranslations.append(pluginTranslator);
100  }
101  }
102  return myappTranslator != NULL;
103 }
104 
105 QTranslator* Localization::loadTranslationFile(const QString& localeName, const QStringList& searchPaths)
106 {
107  QTranslator* pTranslator = new QTranslator();
108  bool bLoaded = false;
109  foreach (const QString& dir, searchPaths)
110  {
111  if (pTranslator->load(localeName, dir))
112  {
113  QCoreApplication::installTranslator(pTranslator);
114  bLoaded = true;
115  break;
116  }
117  }
118  if (!bLoaded)
119  {
120  delete pTranslator;
121  pTranslator = NULL;
122  }
123  return pTranslator;
124 }
126 QList<LocalizationInfo> Localization::LocalizationLoader::loadLocalizationsList(const QStringList& definitionsFileSearchDirs)
127 {
128  foreach (const QString& dirPath, definitionsFileSearchDirs)
129  {
130  loadLocalizationsListFile(dirPath);
131  }
132  sort();
133  return localizations;
134 }
135 
136 void Localization::LocalizationLoader::loadLocalizationsListFile(const QString& definitionsFilePath)
137 {
138  QDir dir(definitionsFilePath);
139  QStringList defFiles = dir.entryList(QStringList(DEFINITION_FILE_PATTERN), QDir::Files);
140  foreach (const QString& defFileName, defFiles)
141  {
142  // No point in translating strings in this class because
143  // translation is not loaded yet.
144  gLog << QString("Reading localizations definitions file: %1").arg(defFileName);
145  QString filePath = dir.absoluteFilePath(defFileName);
146  QFile file(filePath);
147  if (file.open(QIODevice::ReadOnly))
148  {
149  loadLocalizationsListFile(file);
150  file.close();
151  }
152  else
153  {
154  gLog << QString("Failed to open localizations definitions file: %1").arg(definitionsFilePath);
155  }
156  }
157 }
158 
159 void Localization::LocalizationLoader::loadLocalizationsListFile(QIODevice& io)
160 {
161  int version = obtainVersion(io);
162  if (version <= 0)
163  {
164  gLog << QString("Translation definition file doesn't contain valid protocol version information.");
165  return;
166  }
167 
168  QString line = io.readLine();
169  while (!line.isEmpty())
170  {
171  line = line.trimmed();
172  // Discard empty and comment lines.
173  if (!line.isEmpty() && !line.startsWith("#"))
174  {
175  QStringList tokens = line.split(";");
176  if (tokens.size() == NUM_VALID_TOKENS)
177  {
178  LocalizationInfo info;
179  info.countryCodeName = tokens[0].trimmed();
180  info.localeName = tokens[1].trimmed();
181  info.niceName = tokens[2].trimmed();
182  if (!localizations.contains(info))
183  {
184  localizations.append(info);
185  }
186  }
187  else
188  {
189  gLog << QString("Invalid localization definition: %1").arg(line);
190  }
191  }
192  line = io.readLine();
193  }
194 }
195 
196 int Localization::LocalizationLoader::obtainVersion(QIODevice& io)
197 {
198  QString line = io.readLine();
199  int version = -1;
200  if (!line.isNull())
201  {
202  // First line contains protocol version.
203  QStringList tokens = line.split("=");
204  if (tokens.size() == NUM_VALID_VERSION_TOKENS)
205  {
206  QString versionToken = tokens[1];
207  bool bOk = false;
208  version = versionToken.toInt(&bOk);
209  if (!bOk)
210  {
211  version = -1;
212  }
213  }
214  }
215  return version;
216 }
217 
218 void Localization::LocalizationLoader::sort()
219 {
220  qSort(localizations.begin(), localizations.end(), localizationInfoLessThan);
221 }
QString localeName
Compliant with language_country standard. See QLocale::name()
QString niceName
Name that will be displayed to user.
EnginePlugin * info() const
Main plugin interface.
QString countryCodeName
The same as code used for country flags in IP2C.
QString nameCanonical() const
Derived from actual plugin name.
static QStringList staticDataSearchDirs(const QString &subdir=QString())
Paths to directories where program should search for its static data.
Definition: datapaths.cpp:255