File indexing completed on 2024-05-19 04:56:13

0001 /**
0002  * \file loadtranslation.cpp
0003  * Load application translation.
0004  *
0005  * \b Project: Kid3
0006  * \author Urs Fleisch
0007  * \date 26 Mar 2013
0008  *
0009  * Copyright (C) 2013-2024  Urs Fleisch
0010  *
0011  * This file is part of Kid3.
0012  *
0013  * Kid3 is free software; you can redistribute it and/or modify
0014  * it under the terms of the GNU General Public License as published by
0015  * the Free Software Foundation; either version 2 of the License, or
0016  * (at your option) any later version.
0017  *
0018  * Kid3 is distributed in the hope that it will be useful,
0019  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0020  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0021  * GNU General Public License for more details.
0022  *
0023  * You should have received a copy of the GNU General Public License
0024  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
0025  */
0026 
0027 #include "loadtranslation.h"
0028 #include <QCoreApplication>
0029 #include <QStringList>
0030 #include <QLibraryInfo>
0031 #include <QLocale>
0032 #include <QTranslator>
0033 #include <QFileInfo>
0034 #include <QDir>
0035 #include "config.h"
0036 
0037 namespace {
0038 
0039 const char* const QT_TRANSLATION_PREFIX = "qtbase_";
0040 
0041 }
0042 
0043 /**
0044  * @brief Load application translation.
0045  *
0046  * @param lang preferred language, if not set, the language is determined by
0047  * the system configuration
0048  */
0049 void Utils::loadTranslation(const QString& lang)
0050 {
0051   QLocale locale;
0052 
0053   QStringList languages(
0054 #ifndef Q_OS_WIN32
0055         locale.uiLanguages()
0056 #else
0057         locale.name()
0058 #endif
0059         );
0060   if (!lang.isEmpty()) {
0061     languages.prepend(lang);
0062   }
0063 
0064   // Fix the translations returned from QLocale::uiLanguages() if '_' or '@'
0065   // have been replaced by '-'. "zh_CN" is returned as "zh-CN", "sr@latin" as
0066   // "sr-latin". Both "sr@ijekavian" and "sr@ijekavianlatin" give "sr-ijekavia",
0067   // so this case cannot be fixed.
0068   for (auto it = languages.begin(); it != languages.end(); ++it) {
0069     const int len = it->length();
0070     if (const int dashPos = it->lastIndexOf(QLatin1Char('-'));
0071         dashPos > 0 && dashPos < len -1) {
0072       (*it)[dashPos] = QLatin1Char(dashPos == len - 3 ? '_' : '@');
0073     }
0074     // Some more fixes for languages encountered on macOS,
0075     // e.g. "sr-Latn_SP", "zh-Hant_TW".
0076     it->replace(QLatin1String("@Latn"), QLatin1String("@latin"))
0077         .replace(QLatin1String("-Latn"), QLatin1String("@latin"))
0078         .remove(QLatin1String("-Hant"))
0079         .remove(QLatin1String("-Hans"));
0080   }
0081 
0082   QString translationsDir;
0083 #ifdef CFG_TRANSLATIONSDIR
0084   translationsDir = QLatin1String(CFG_TRANSLATIONSDIR);
0085   prependApplicationDirPathIfRelative(translationsDir);
0086 #endif
0087 
0088   // '-' is added to default delimiters because it is used on Mac OS X instead
0089   // of '_'.
0090   const QString searchDelimiters(QLatin1String("_.-"));
0091 
0092   // translation file for Qt
0093   auto qtTr = new QTranslator(qApp);
0094   const auto localeNames = languages;
0095   for (const QString& localeName : localeNames) {
0096     if (
0097         localeName.startsWith(QLatin1String("en")) ||
0098         (!translationsDir.isNull() &&
0099          qtTr->load(QLatin1String(QT_TRANSLATION_PREFIX) + localeName,
0100                     translationsDir, searchDelimiters)) ||
0101         qtTr->load(QLatin1String(QT_TRANSLATION_PREFIX) + localeName,
0102                    QLatin1String("."), searchDelimiters)
0103 #if !(defined Q_OS_WIN32 || defined Q_OS_MAC || defined Q_OS_ANDROID)
0104         || qtTr->load(QLatin1String(QT_TRANSLATION_PREFIX) + localeName,
0105 #if QT_VERSION >= 0x060000
0106                    QLibraryInfo::path(QLibraryInfo::TranslationsPath),
0107 #else
0108                    QLibraryInfo::location(QLibraryInfo::TranslationsPath),
0109 #endif
0110                    searchDelimiters)
0111 #endif
0112         ) {
0113       break;
0114     }
0115   }
0116   qApp->installTranslator(qtTr);
0117 
0118   // translation file for application strings
0119   auto kid3Tr = new QTranslator(qApp);
0120   for (const QString& localeName : localeNames) {
0121     if (
0122         (!translationsDir.isNull() &&
0123          kid3Tr->load(QLatin1String("kid3_") + localeName, translationsDir,
0124                       searchDelimiters)) ||
0125         kid3Tr->load(QLatin1String("kid3_") + localeName, QLatin1String("."),
0126                      searchDelimiters) ||
0127         localeName.startsWith(QLatin1String("en"))
0128         ) {
0129       break;
0130     }
0131   }
0132   qApp->installTranslator(kid3Tr);
0133 }
0134 
0135 /**
0136  * Prepend the application directory path to a path if it is relative.
0137  *
0138  * @param path file or directory path, will be modified if relative
0139  */
0140 void Utils::prependApplicationDirPathIfRelative(QString& path)
0141 {
0142   if (QFileInfo(path).isRelative()) {
0143     if (QString appDir = QCoreApplication::applicationDirPath();
0144         !appDir.isEmpty()) {
0145       if (!appDir.endsWith(QLatin1Char('/'))) {
0146         appDir.append(QLatin1Char('/'));
0147       }
0148       path.prepend(appDir);
0149     }
0150   }
0151 }
0152 
0153 /**
0154  * Load list of available translations.
0155  * @return language codes of installed translations, e.g. {"de", "en", ...}.
0156  */
0157 QStringList Utils::availableTranslations()
0158 {
0159   QString translationsDir;
0160 #ifdef CFG_TRANSLATIONSDIR
0161   translationsDir = QLatin1String(CFG_TRANSLATIONSDIR);
0162   prependApplicationDirPathIfRelative(translationsDir);
0163 #endif
0164   QDir dir(translationsDir);
0165   const QStringList fileNames = dir.entryList({QLatin1String("kid3_*.qm")},
0166                                               QDir::Files, QDir::Name);
0167   QStringList languages;
0168   for (const auto& fileName : fileNames) {
0169     languages.append(fileName.mid(5, fileName.length() - 8));
0170   }
0171   return languages;
0172 }