File indexing completed on 2025-01-19 03:50:58

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2022-08-30
0007  * Description : translate metadata batch tool.
0008  *
0009  * SPDX-FileCopyrightText: 2021-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0010  *
0011  * SPDX-License-Identifier: GPL-2.0-or-later
0012  *
0013  * ============================================================ */
0014 
0015 #include "translate.h"
0016 
0017 // Qt includes
0018 
0019 #include <QGridLayout>
0020 #include <QCheckBox>
0021 #include <QComboBox>
0022 #include <QWidget>
0023 #include <QListWidget>
0024 #include <QLabel>
0025 #include <QFile>
0026 #include <QMenu>
0027 
0028 // KDE includes
0029 
0030 #include <klocalizedstring.h>
0031 
0032 // Local includes
0033 
0034 #include "digikam_debug.h"
0035 #include "dimg.h"
0036 #include "dfileoperations.h"
0037 #include "donlinetranslator.h"
0038 #include "localizesettings.h"
0039 #include "localizeselector.h"
0040 #include "altlangstredit.h"
0041 
0042 namespace DigikamBqmTranslatePlugin
0043 {
0044 
0045 class Q_DECL_HIDDEN Translate::Private
0046 {
0047 
0048 public:
0049 
0050     enum Entries
0051     {
0052         Title       = 0x01,
0053         Caption     = 0x02,
0054         Copyrights  = 0x04,
0055         UsageTerms  = 0x08
0056     };
0057 
0058 public:
0059 
0060     explicit Private()
0061       : titleCB       (nullptr),
0062         captionCB     (nullptr),
0063         copyrightsCB  (nullptr),
0064         usageTermsCB  (nullptr),
0065         trLabel       (nullptr),
0066         tagsLabel     (nullptr),
0067         trSelectorList(nullptr),
0068         changeSettings(true)
0069     {
0070     }
0071 
0072     QCheckBox*            titleCB;
0073     QCheckBox*            captionCB;
0074     QCheckBox*            copyrightsCB;
0075     QCheckBox*            usageTermsCB;
0076 
0077     QLabel*               trLabel;
0078     QLabel*               tagsLabel;
0079 
0080     LocalizeSelectorList* trSelectorList;
0081 
0082     bool                  changeSettings;
0083 };
0084 
0085 Translate::Translate(QObject* const parent)
0086     : BatchTool(QLatin1String("Translate"), MetadataTool, parent),
0087       d        (new Private)
0088 {
0089 }
0090 
0091 Translate::~Translate()
0092 {
0093     delete d;
0094 }
0095 
0096 BatchTool* Translate::clone(QObject* const parent) const
0097 {
0098     return new Translate(parent);
0099 }
0100 
0101 void Translate::registerSettingsWidget()
0102 {
0103     QWidget* const panel     = new QWidget;
0104     QGridLayout* const grid  = new QGridLayout(panel);
0105 
0106     d->tagsLabel             = new QLabel(panel);
0107     d->titleCB               = new QCheckBox(i18nc("@option:check metadata entry", "Title"),       panel);
0108     d->captionCB             = new QCheckBox(i18nc("@option:check metadata entry", "Caption"),     panel);
0109     d->copyrightsCB          = new QCheckBox(i18nc("@option:check metadata entry", "Copyrights"),  panel);
0110     d->usageTermsCB          = new QCheckBox(i18nc("@option:check metadata entry", "Usage Terms"), panel);
0111 
0112     d->trSelectorList        = new LocalizeSelectorList(panel);
0113     d->trSelectorList->setTitle(i18nc("@label", "Translate to:"));
0114 
0115     grid->addWidget(d->tagsLabel,      0, 0, 1, 2);
0116     grid->addWidget(d->titleCB,        1, 0, 1, 2);
0117     grid->addWidget(d->captionCB,      2, 0, 1, 2);
0118     grid->addWidget(d->copyrightsCB,   3, 0, 1, 2);
0119     grid->addWidget(d->usageTermsCB,   4, 0, 1, 2);
0120     grid->addWidget(d->trSelectorList, 5, 0, 1, 2);
0121     grid->setColumnStretch(0, 10);
0122     grid->setRowStretch(6, 10);
0123 
0124     slotLocalizeChanged();
0125 
0126     m_settingsWidget = panel;
0127 
0128     connect(d->titleCB, SIGNAL(toggled(bool)),
0129             this, SLOT(slotSettingsChanged()));
0130 
0131     connect(d->captionCB, SIGNAL(toggled(bool)),
0132             this, SLOT(slotSettingsChanged()));
0133 
0134     connect(d->copyrightsCB, SIGNAL(toggled(bool)),
0135             this, SLOT(slotSettingsChanged()));
0136 
0137     connect(d->usageTermsCB, SIGNAL(toggled(bool)),
0138             this, SLOT(slotSettingsChanged()));
0139 
0140     connect(d->trSelectorList, SIGNAL(signalSettingsChanged()),
0141             this, SLOT(slotSettingsChanged()));
0142 
0143     connect(LocalizeSettings::instance(), &LocalizeSettings::signalSettingsChanged,
0144             this, &Translate::slotLocalizeChanged);
0145 
0146     BatchTool::registerSettingsWidget();
0147 }
0148 
0149 void Translate::slotLocalizeChanged()
0150 {
0151     d->tagsLabel->setText(i18nc("@label", "Entries to Translate with %1:",
0152                           DOnlineTranslator::engineName(LocalizeSettings::instance()->settings().translatorEngine)));
0153 }
0154 
0155 BatchToolSettings Translate::defaultSettings()
0156 {
0157     BatchToolSettings settings;
0158 
0159     settings.insert(QLatin1String("Title"),      false);
0160     settings.insert(QLatin1String("Caption"),    false);
0161     settings.insert(QLatin1String("Copyrights"), false);
0162     settings.insert(QLatin1String("UsageTerms"), false);
0163     settings.insert(QLatin1String("TrLangs"),    QStringList() << QLatin1String("en-US"));
0164 
0165     return settings;
0166 }
0167 
0168 void Translate::slotAssignSettings2Widget()
0169 {
0170     d->changeSettings = false;
0171 
0172     d->titleCB->setChecked(settings()[QLatin1String("Title")].toBool());
0173     d->captionCB->setChecked(settings()[QLatin1String("Caption")].toBool());
0174     d->copyrightsCB->setChecked(settings()[QLatin1String("Copyrights")].toBool());
0175     d->usageTermsCB->setChecked(settings()[QLatin1String("UsageTerms")].toBool());
0176 
0177     QStringList langs = settings()[QLatin1String("TrLangs")].toStringList();
0178 
0179     d->trSelectorList->clearLanguages();
0180 
0181     Q_FOREACH (const QString& lg, langs)
0182     {
0183         d->trSelectorList->addLanguage(lg);
0184     }
0185 
0186     d->changeSettings = true;
0187 }
0188 
0189 void Translate::slotSettingsChanged()
0190 {
0191     if (d->changeSettings)
0192     {
0193         BatchToolSettings settings;
0194 
0195         settings.insert(QLatin1String("Title"),       d->titleCB->isChecked());
0196         settings.insert(QLatin1String("Caption"),     d->captionCB->isChecked());
0197         settings.insert(QLatin1String("Copyrights"),  d->copyrightsCB->isChecked());
0198         settings.insert(QLatin1String("UsageTerms"),  d->usageTermsCB->isChecked());
0199 
0200         QStringList langs = d->trSelectorList->languagesList();
0201         settings.insert(QLatin1String("TrLangs"),     langs);
0202 
0203         BatchTool::slotSettingsChanged(settings);
0204     }
0205 }
0206 
0207 bool Translate::toolOperations()
0208 {
0209     bool ret = true;
0210     QScopedPointer<DMetadata> meta(new DMetadata);
0211 
0212     if (image().isNull())
0213     {
0214         if (!meta->load(inputUrl().toLocalFile()))
0215         {
0216             return false;
0217         }
0218     }
0219     else
0220     {
0221         meta->setData(image().getMetadata());
0222     }
0223 
0224     bool titleStage      = settings()[QLatin1String("Title")].toBool();
0225     bool captionStage    = settings()[QLatin1String("Caption")].toBool();
0226     bool copyrightsStage = settings()[QLatin1String("Copyrights")].toBool();
0227     bool usageTermsStage = settings()[QLatin1String("UsageTerms")].toBool();
0228     QStringList langs    = settings()[QLatin1String("TrLangs")].toStringList();
0229 
0230     Q_FOREACH (const QString& trLang, langs)
0231     {
0232         if (titleStage)
0233         {
0234             qCDebug(DIGIKAM_DPLUGIN_BQM_LOG) << "Translate Title";
0235             ret &= insertTranslation(Private::Title, trLang, meta.data());
0236         }
0237 
0238         if (captionStage)
0239         {
0240             qCDebug(DIGIKAM_DPLUGIN_BQM_LOG) << "Translate Caption";
0241             ret &= insertTranslation(Private::Caption, trLang, meta.data());
0242         }
0243 
0244         if (copyrightsStage)
0245         {
0246             qCDebug(DIGIKAM_DPLUGIN_BQM_LOG) << "Translate Copyrights";
0247             ret &= insertTranslation(Private::Copyrights, trLang, meta.data());
0248         }
0249 
0250         if (usageTermsStage)
0251         {
0252             qCDebug(DIGIKAM_DPLUGIN_BQM_LOG) << "Translate Usage Terms";
0253             ret &= insertTranslation(Private::UsageTerms, trLang, meta.data());
0254         }
0255 
0256         qCDebug(DIGIKAM_DPLUGIN_BQM_LOG) << "Title     :" << meta->getXmpTagStringListLangAlt("Xmp.dc.title", false);
0257         qCDebug(DIGIKAM_DPLUGIN_BQM_LOG) << "Caption   :" << meta->getXmpTagStringListLangAlt("Xmp.dc.description", false);
0258         qCDebug(DIGIKAM_DPLUGIN_BQM_LOG) << "CopyRights:" << meta->getXmpTagStringListLangAlt("Xmp.dc.rights", false);
0259         qCDebug(DIGIKAM_DPLUGIN_BQM_LOG) << "UsageTerms:" << meta->getXmpTagStringListLangAlt("Xmp.xmpRights.UsageTerms", false);
0260 
0261         if (image().isNull())
0262         {
0263             QFile::remove(outputUrl().toLocalFile());
0264             ret &= DFileOperations::copyFile(inputUrl().toLocalFile(), outputUrl().toLocalFile());
0265 
0266             if (ret && (titleStage || captionStage || copyrightsStage || usageTermsStage))
0267             {
0268                 ret &= meta->save(outputUrl().toLocalFile());
0269                 qCDebug(DIGIKAM_DPLUGIN_BQM_LOG) << "Save metadata to file:" << ret;
0270             }
0271         }
0272         else
0273         {
0274             if (titleStage || captionStage || copyrightsStage || usageTermsStage)
0275             {
0276                 qCDebug(DIGIKAM_DPLUGIN_BQM_LOG) << "Save metadata to image";
0277                 image().setMetadata(meta->data());
0278             }
0279 
0280             ret &= savefromDImg();
0281         }
0282     }
0283 
0284     return ret;
0285 }
0286 
0287 bool Translate::insertTranslation(int entry, const QString& trLang, DMetadata* const meta) const
0288 {
0289     bool ret = true;
0290     CaptionsMap captions;
0291     DMetadata::AltLangMap map;
0292 
0293     switch (entry)
0294     {
0295         case Private::Title:
0296         {
0297             captions = meta->getItemTitles();
0298             map      = captions.toAltLangMap();
0299             break;
0300         }
0301 
0302         case Private::Caption:
0303         {
0304             captions = meta->getItemComments();
0305             map      = captions.toAltLangMap();
0306             break;
0307         }
0308 
0309         case Private::Copyrights:
0310         {
0311             map = meta->getXmpTagStringListLangAlt("Xmp.dc.rights", false);
0312             break;
0313         }
0314 
0315         case Private::UsageTerms:
0316         {
0317             map = meta->getXmpTagStringListLangAlt("Xmp.xmpRights.UsageTerms", false);
0318             break;
0319         }
0320     }
0321 
0322     if (!map.isEmpty() && map.contains(QLatin1String("x-default")))
0323     {
0324         QString sc = map.value(QLatin1String("x-default"));
0325 
0326         if (!sc.isEmpty())
0327         {
0328             QString tr;
0329             QString error;
0330             ret = s_inlineTranslateString(sc, trLang, tr, error);
0331 
0332             if (ret)
0333             {
0334                 switch (entry)
0335                 {
0336                     case Private::Title:
0337                     {
0338                         CaptionValues val = captions[QLatin1String("x-default")];
0339                         val.caption       = tr;
0340                         captions[trLang]  = val;
0341                         meta->setItemTitles(captions);
0342                         break;
0343                     }
0344 
0345                     case Private::Caption:
0346                     {
0347                         CaptionValues val = captions[QLatin1String("x-default")];
0348                         val.caption       = tr;
0349                         captions[trLang]  = val;
0350                         meta->setItemComments(captions);
0351                         break;
0352                     }
0353 
0354                     case Private::Copyrights:
0355                     {
0356                         map[trLang] = tr;
0357                         meta->setXmpTagStringListLangAlt("Xmp.dc.rights", map);
0358                         break;
0359                     }
0360 
0361                     case Private::UsageTerms:
0362                     {
0363                         map[trLang] = tr;
0364                         meta->setXmpTagStringListLangAlt("Xmp.xmpRights.UsageTerms", map);
0365                         break;
0366                     }
0367                 }
0368             }
0369             else
0370             {
0371                 qCDebug(DIGIKAM_DPLUGIN_BQM_LOG) << "Cannot process online translation from"
0372                                                  << meta->getFilePath()
0373                                                  << " - Error:" << error;
0374             }
0375         }
0376         else
0377         {
0378             qCDebug(DIGIKAM_DPLUGIN_BQM_LOG) << "No x-default language found from" << meta->getFilePath();
0379         }
0380     }
0381     else
0382     {
0383         qCDebug(DIGIKAM_DPLUGIN_BQM_LOG) << "No alternative language string found from" << meta->getFilePath();
0384     }
0385 
0386     return ret;
0387 }
0388 
0389 } // namespace DigikamBqmTranslatePlugin
0390 
0391 #include "moc_translate.cpp"