diff --git a/share/qtcreator/templates/wizards/files/translation/wizard.json b/share/qtcreator/templates/wizards/files/translation/wizard.json
new file mode 100644
index 00000000000..04c2f145d1f
--- /dev/null
+++ b/share/qtcreator/templates/wizards/files/translation/wizard.json
@@ -0,0 +1,45 @@
+{
+ "version": 1,
+ "supportedProjectTypes": [ ],
+ "id": "Q.Translation",
+ "category": "R.Qt",
+ "trDescription": "Creates a translation file that you can add to a Qt project.",
+ "trDisplayName": "Qt Translation File",
+ "trDisplayCategory": "Qt",
+ "iconText": "ts",
+ "enabled": "%{JS: value('Plugins').indexOf('QtSupport') >= 0}",
+
+ "options": [
+ {
+ "key": "TargetPath", "value": "%{InitialPath}"
+ }
+ ],
+
+ "pages" : [
+ {
+ "trDisplayName": "Location",
+ "trShortTitle": "Location",
+ "typeId": "QtTranslation",
+ "data": {
+ "singleFile": true
+ }
+ },
+ {
+ "trDisplayName": "Project Management",
+ "trShortTitle": "Summary",
+ "typeId": "Summary"
+ }
+ ],
+ "generators" : [
+ {
+ "typeId": "File",
+ "data": [
+ {
+ "source": "../../projects/translation.ts",
+ "target": "%{TsFileName}",
+ "openInEditor": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp
index 157160c33f6..4e10ec6ecfe 100644
--- a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp
@@ -219,16 +219,17 @@ bool CMakeBuildSystem::supportsAction(Node *context, ProjectAction action, const
return BuildSystem::supportsAction(context, action, node);
}
+static QString relativeFilePaths(const FilePaths &filePaths, const FilePath &projectDir)
+{
+ return Utils::transform(filePaths, [projectDir](const FilePath &path) {
+ return path.canonicalPath().relativePathFrom(projectDir).cleanPath().toString();
+ }).join(' ');
+};
+
static QString newFilesForFunction(const std::string &cmakeFunction,
const FilePaths &filePaths,
const FilePath &projDir)
{
- auto relativeFilePaths = [projDir](const FilePaths &filePaths) {
- return Utils::transform(filePaths, [projDir](const FilePath &path) {
- return path.canonicalPath().relativePathFrom(projDir).cleanPath().toString();
- });
- };
-
if (cmakeFunction == "qt_add_qml_module" || cmakeFunction == "qt6_add_qml_module") {
FilePaths sourceFiles;
FilePaths resourceFiles;
@@ -255,16 +256,16 @@ static QString newFilesForFunction(const std::string &cmakeFunction,
QStringList result;
if (!sourceFiles.isEmpty())
- result << QString("SOURCES %1").arg(relativeFilePaths(sourceFiles).join(" "));
+ result << QString("SOURCES %1").arg(relativeFilePaths(sourceFiles, projDir));
if (!resourceFiles.isEmpty())
- result << QString("RESOURCES %1").arg(relativeFilePaths(resourceFiles).join(" "));
+ result << QString("RESOURCES %1").arg(relativeFilePaths(resourceFiles, projDir));
if (!qmlFiles.isEmpty())
- result << QString("QML_FILES %1").arg(relativeFilePaths(qmlFiles).join(" "));
+ result << QString("QML_FILES %1").arg(relativeFilePaths(qmlFiles, projDir));
return result.join("\n");
}
- return relativeFilePaths(filePaths).join(" ");
+ return relativeFilePaths(filePaths, projDir);
}
static std::optional cmakeFileForBuildKey(const QString &buildKey,
@@ -392,10 +393,58 @@ static expected_str insertSnippetSilently(const FilePath &cmakeFile,
return true;
}
-bool CMakeBuildSystem::addFiles(Node *context, const FilePaths &filePaths, FilePaths *notAdded)
+bool CMakeBuildSystem::addTsFiles(Node *context, const FilePaths &filePaths, FilePaths *notAdded)
{
if (notAdded)
- *notAdded = filePaths;
+ notAdded->append(filePaths);
+
+ if (auto n = dynamic_cast(context)) {
+ const std::optional cmakeFile = cmakeFileForBuildKey(n->buildKey(), buildTargets());
+ if (!cmakeFile.has_value())
+ return false;
+
+ const FilePath targetCMakeFile = cmakeFile->targetFilePath;
+ std::optional cmakeListFile = getUncachedCMakeListFile(targetCMakeFile);
+ if (!cmakeListFile.has_value())
+ return false;
+
+ auto findTs = [](const auto &func) {
+ if (func.LowerCaseName() != "set")
+ return false;
+ std::vector args = func.Arguments();
+ return args.size() && args.front().Value == "TS_FILES";
+ };
+ std::optional function = findFunction(*cmakeListFile, findTs);
+ if (!function.has_value())
+ return false;
+
+ const QString filesToAdd = relativeFilePaths(filePaths, n->filePath().canonicalPath());
+ auto lastArgument = function->Arguments().back();
+ const int lastArgLength = static_cast(lastArgument.Value.size()) - 1;
+ SnippetAndLocation snippetLocation{QString("\n%1").arg(filesToAdd),
+ lastArgument.Line, lastArgument.Column + lastArgLength};
+ // Take into consideration the quotes
+ if (lastArgument.Delim == cmListFileArgument::Quoted)
+ snippetLocation.column += 2;
+
+ expected_str inserted = insertSnippetSilently(targetCMakeFile, snippetLocation);
+ if (!inserted) {
+ qCCritical(cmakeBuildSystemLog) << inserted.error();
+ return false;
+ }
+
+ if (notAdded)
+ notAdded->removeIf([filePaths](const FilePath &p) { return filePaths.contains(p); });
+ return true;
+ }
+ return false;
+}
+
+bool CMakeBuildSystem::addSrcFiles(Node *context, const FilePaths &filePaths, FilePaths *notAdded)
+{
+ if (notAdded)
+ notAdded->append(filePaths);
+
if (auto n = dynamic_cast(context)) {
const QString targetName = n->buildKey();
const std::optional cmakeFile = cmakeFileForBuildKey(targetName, buildTargets());
@@ -443,10 +492,28 @@ bool CMakeBuildSystem::addFiles(Node *context, const FilePaths &filePaths, FileP
}
if (notAdded)
- notAdded->clear();
+ notAdded->removeIf([filePaths](const FilePath &p) { return filePaths.contains(p); });
return true;
}
+ return false;
+}
+
+bool CMakeBuildSystem::addFiles(Node *context, const FilePaths &filePaths, FilePaths *notAdded)
+{
+ FilePaths tsFiles, srcFiles;
+ std::tie(tsFiles, srcFiles) = Utils::partition(filePaths, [](const FilePath &fp) {
+ return Utils::mimeTypeForFile(fp.toString()).name() == Utils::Constants::LINGUIST_MIMETYPE;
+ });
+ bool success = true;
+ if (!srcFiles.isEmpty())
+ success = addSrcFiles(context, srcFiles, notAdded);
+
+ if (!tsFiles.isEmpty())
+ success = addTsFiles(context, tsFiles, notAdded) || success;
+
+ if (success)
+ return true;
return BuildSystem::addFiles(context, filePaths, notAdded);
}
diff --git a/src/plugins/cmakeprojectmanager/cmakebuildsystem.h b/src/plugins/cmakeprojectmanager/cmakebuildsystem.h
index 6086146223b..dd85a52e228 100644
--- a/src/plugins/cmakeprojectmanager/cmakebuildsystem.h
+++ b/src/plugins/cmakeprojectmanager/cmakebuildsystem.h
@@ -146,6 +146,11 @@ private:
void setError(const QString &message);
void setWarning(const QString &message);
+ bool addSrcFiles(ProjectExplorer::Node *context, const Utils::FilePaths &filePaths,
+ Utils::FilePaths *);
+ bool addTsFiles(ProjectExplorer::Node *context, const Utils::FilePaths &filePaths,
+ Utils::FilePaths *);
+
// Actually ask for parsing:
enum ReparseParameters {
REPARSE_DEFAULT = 0, // Nothing special:-)
diff --git a/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp b/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp
index ac8af4d3cee..d04bd262d0d 100644
--- a/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp
@@ -1028,6 +1028,9 @@ QString QmakePriFile::varNameForAdding(const QString &mimeType)
if (mimeType == QLatin1String(PROFILE_MIMETYPE))
return QLatin1String("SUBDIRS");
+ if (mimeType == QLatin1String(Utils::Constants::LINGUIST_MIMETYPE))
+ return QLatin1String("TRANSLATIONS");
+
return QLatin1String("DISTFILES");
}
@@ -1052,6 +1055,7 @@ QStringList QmakePriFile::varNamesForRemoving()
vars << QLatin1String("ICON");
vars << QLatin1String("QMAKE_INFO_PLIST");
vars << QLatin1String("STATECHARTS");
+ vars << QLatin1String("TRANSLATIONS");
return vars;
}
diff --git a/src/plugins/qtsupport/translationwizardpage.cpp b/src/plugins/qtsupport/translationwizardpage.cpp
index 968701cdee5..79dde408d0a 100644
--- a/src/plugins/qtsupport/translationwizardpage.cpp
+++ b/src/plugins/qtsupport/translationwizardpage.cpp
@@ -6,6 +6,8 @@
#include "qtsupporttr.h"
#include
+#include
+#include
#include
#include
@@ -36,7 +38,7 @@ class TranslationWizardPage : public WizardPage
Q_OBJECT
public:
- TranslationWizardPage(const QString &enabledExpr);
+ TranslationWizardPage(const QString &enabledExpr, bool singleFile);
private:
void initializePage() override;
@@ -49,6 +51,7 @@ private:
QComboBox m_languageComboBox;
QLineEdit m_fileNameLineEdit;
const QString m_enabledExpr;
+ const bool m_isProjectWizard;
};
TranslationWizardPageFactory::TranslationWizardPageFactory()
@@ -61,17 +64,22 @@ WizardPage *TranslationWizardPageFactory::create(JsonWizard *wizard, Id typeId,
{
Q_UNUSED(wizard)
Q_UNUSED(typeId)
- return new TranslationWizardPage(data.toMap().value("enabled").toString());
+ return new TranslationWizardPage(data.toMap().value("enabled").toString(),
+ data.toMap().value("singleFile").toBool());
}
-TranslationWizardPage::TranslationWizardPage(const QString &enabledExpr)
+TranslationWizardPage::TranslationWizardPage(const QString &enabledExpr, bool singleFile)
: m_enabledExpr(enabledExpr)
+ , m_isProjectWizard(!singleFile)
{
const auto mainLayout = new QVBoxLayout(this);
const auto descriptionLabel = new QLabel(
- Tr::tr("If you plan to provide translations for your project's "
- "user interface via the Qt Linguist tool, please select a language here. "
- "A corresponding translation (.ts) file will be generated for you."));
+ singleFile ? Tr::tr("Please select a language for which a corresponding "
+ "translation (.ts) file will be generated for you.")
+ : Tr::tr("If you plan to provide translations for your project's "
+ "user interface via the Qt Linguist tool, please select a "
+ "language here. A corresponding translation (.ts) file will be "
+ "generated for you."));
descriptionLabel->setWordWrap(true);
mainLayout->addWidget(descriptionLabel);
const auto formLayout = new QFormLayout;
@@ -116,7 +124,9 @@ void TranslationWizardPage::initializePage()
bool TranslationWizardPage::isComplete() const
{
- return m_languageComboBox.currentIndex() == 0 || !tsBaseName().isEmpty();
+ if (m_isProjectWizard)
+ return m_languageComboBox.currentIndex() == 0 || !tsBaseName().isEmpty();
+ return m_languageComboBox.currentIndex() > 0 && !tsBaseName().isEmpty();
}
bool TranslationWizardPage::validatePage()
@@ -131,7 +141,14 @@ void TranslationWizardPage::updateLineEdit()
{
m_fileNameLineEdit.setEnabled(m_languageComboBox.currentIndex() != 0);
if (m_fileNameLineEdit.isEnabled()) {
- const QString projectName = static_cast(wizard())->stringValue("ProjectName");
+ auto jsonWizard = static_cast(wizard());
+ QString projectName = jsonWizard->stringValue("ProjectName");
+ if (!m_isProjectWizard && projectName.isEmpty()) {
+ if (auto project = ProjectExplorer::ProjectManager::startupProject())
+ projectName = FileUtils::fileSystemFriendlyName(project->displayName());
+ else
+ projectName = FilePath::fromUserInput(jsonWizard->stringValue("InitialPath")).baseName();
+ }
m_fileNameLineEdit.setText(projectName + '_' + m_languageComboBox.currentData().toString());
} else {
m_fileNameLineEdit.clear();