Module to update ts files.
This commit is contained in:
parent
44b9e1b77c
commit
0a681d7f93
205
qbs/modules/i18n/i18n.qbs
Normal file
205
qbs/modules/i18n/i18n.qbs
Normal file
|
@ -0,0 +1,205 @@
|
||||||
|
import qbs.File
|
||||||
|
import qbs.FileInfo
|
||||||
|
import qbs.TextFile
|
||||||
|
|
||||||
|
/**
|
||||||
|
This module generates 'i18n.pro' artifact, which then acts as an input for 'lupdate' program, which in turn produces
|
||||||
|
translation files, which are compiled by 'lrelease' program into 'qm' files, which can be loaded by an application.
|
||||||
|
*/
|
||||||
|
Module {
|
||||||
|
Depends { name: "Qt.core" }
|
||||||
|
|
||||||
|
additionalProductTypes: ["i18n"]
|
||||||
|
|
||||||
|
/*
|
||||||
|
Unfortunately you can not simply add empty files to the product, cause 'Qt.core' module has a rule, which calls 'lrelease' on
|
||||||
|
every 'ts' file in the product and 'lrelease' triggers error if these files are empty. Additionaly 'lupdate' also triggers
|
||||||
|
errors, when parsing 'pro' file. Instead this property can be used to create new translation file.
|
||||||
|
*/
|
||||||
|
property stringList additionalTranslations: []
|
||||||
|
|
||||||
|
// Explicitly trigger build even if build a product
|
||||||
|
property bool update: false
|
||||||
|
|
||||||
|
// Build with legacy way though .pro file
|
||||||
|
property bool buildWithPro: true
|
||||||
|
|
||||||
|
property string lupdateName: "lupdate"
|
||||||
|
|
||||||
|
Rule {
|
||||||
|
condition: update && buildWithPro
|
||||||
|
multiplex: true
|
||||||
|
inputs: ["i18n.hpp", "i18n.src", "i18n.ui", "i18n.res", "i18n.ts"]
|
||||||
|
|
||||||
|
prepare: {
|
||||||
|
var proCmd = new JavaScriptCommand();
|
||||||
|
proCmd.description = 'generating ' + output.filePath;
|
||||||
|
proCmd.highlight = 'codegen';
|
||||||
|
proCmd.sourceCode = function() {
|
||||||
|
var f = new TextFile(output.filePath, TextFile.WriteOnly);
|
||||||
|
try {
|
||||||
|
f.writeLine("lupdate_only {");
|
||||||
|
if (inputs["i18n.hpp"] !== undefined)
|
||||||
|
for (var i = 0; i < inputs["i18n.hpp"].length; i++)
|
||||||
|
f.writeLine("HEADERS += " + FileInfo.relativePath(product.sourceDirectory, inputs["i18n.hpp"][i].filePath));
|
||||||
|
f.writeLine("");
|
||||||
|
if (inputs["i18n.src"] !== undefined)
|
||||||
|
for (var i = 0; i < inputs["i18n.src"].length; i++)
|
||||||
|
f.writeLine("SOURCES += " + FileInfo.relativePath(product.sourceDirectory, inputs["i18n.src"][i].filePath));
|
||||||
|
f.writeLine("");
|
||||||
|
if (inputs["i18n.ui"] !== undefined)
|
||||||
|
for (var i = 0; i < inputs["i18n.ui"].length; i++)
|
||||||
|
f.writeLine("FORMS += " + FileInfo.relativePath(product.sourceDirectory, inputs["i18n.ui"][i].filePath));
|
||||||
|
f.writeLine("");
|
||||||
|
// lupdate processes QML files that are listed in the .qrc file
|
||||||
|
if (inputs["i18n.res"] !== undefined)
|
||||||
|
for (var i = 0; i < inputs["i18n.res"].length; i++)
|
||||||
|
f.writeLine("RESOURCES += " + FileInfo.relativePath(product.sourceDirectory, inputs["i18n.res"][i].filePath));
|
||||||
|
f.writeLine("}");
|
||||||
|
|
||||||
|
f.writeLine("");
|
||||||
|
if (inputs["i18n.ts"] !== undefined)
|
||||||
|
for (var i = 0; i < inputs["i18n.ts"].length; i++)
|
||||||
|
f.writeLine("TRANSLATIONS += " + FileInfo.relativePath(product.sourceDirectory, inputs["i18n.ts"][i].filePath));
|
||||||
|
for (var i = 0; i < product.i18n.additionalTranslations.length; i++) {
|
||||||
|
var targetDirectory = product.sourceDirectory + "/" + FileInfo.path(product.i18n.additionalTranslations[i]);
|
||||||
|
if (!File.exists(targetDirectory))
|
||||||
|
console.error("Directory '" + targetDirectory + "' does not exists. Please create it.");
|
||||||
|
f.writeLine("TRANSLATIONS += " + product.i18n.additionalTranslations[i]);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
f.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [proCmd];
|
||||||
|
}
|
||||||
|
|
||||||
|
Artifact {
|
||||||
|
filePath: product.sourceDirectory + "/" + product.name + ".i18n.pro"
|
||||||
|
fileTags: ["i18n.pro"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rule {
|
||||||
|
condition: buildWithPro
|
||||||
|
inputs: ["i18n.pro"]
|
||||||
|
|
||||||
|
prepare: {
|
||||||
|
var lupdateName = product.i18n.lupdateName;
|
||||||
|
var cmdLupdate = new Command(product.Qt.core.binPath + '/' + lupdateName, ["-verbose", input.filePath]);
|
||||||
|
cmdLupdate.description = "Invoking '" + lupdateName + "' program";
|
||||||
|
cmdLupdate.highlight = 'filegen';
|
||||||
|
|
||||||
|
var cmdClean = new JavaScriptCommand();
|
||||||
|
cmdClean.description = "Removing " + input.fileName;
|
||||||
|
cmdClean.highlight = "filegen";
|
||||||
|
cmdClean.sourceCode = function() {
|
||||||
|
File.remove(input.filePath);
|
||||||
|
}
|
||||||
|
return [cmdLupdate, cmdClean]
|
||||||
|
}
|
||||||
|
|
||||||
|
outputFileTags: ["i18n"]
|
||||||
|
}
|
||||||
|
|
||||||
|
Rule {
|
||||||
|
condition: update && !buildWithPro
|
||||||
|
multiplex: true
|
||||||
|
inputs: ["i18n.hpp", "i18n.src", "i18n.ui", "i18n.ts"]
|
||||||
|
|
||||||
|
prepare: {
|
||||||
|
var proCmd = new JavaScriptCommand();
|
||||||
|
proCmd.description = 'generating ' + output.filePath;
|
||||||
|
proCmd.highlight = 'codegen';
|
||||||
|
proCmd.sourceCode = function() {
|
||||||
|
var f = new TextFile(output.filePath, TextFile.WriteOnly);
|
||||||
|
try {
|
||||||
|
// Since Qt 5.13 lupdate supports passing a project description in JSON file. For producing such a
|
||||||
|
// description from .pro file we can use new tool lprodump. But tehnically we don't need it. We can
|
||||||
|
// totally fake format.
|
||||||
|
// JSON file structure:
|
||||||
|
// Project ::= {
|
||||||
|
// string projectFile // Name of the project file. (required)
|
||||||
|
// string codec // Source code codec. Valid values are
|
||||||
|
// // currently "utf-16" or "utf-8" (default).
|
||||||
|
// string[] translations // List of .ts files of the project. (required)
|
||||||
|
// string[] includePaths // List of include paths.
|
||||||
|
// string[] sources // List of source files. (required)
|
||||||
|
// string[] excluded // List of source files, which are
|
||||||
|
// // excluded for translation.
|
||||||
|
// Project[] subProjects // List of sub-projects.
|
||||||
|
// }
|
||||||
|
// It seems all we need are projectFile, sources and translations options.
|
||||||
|
|
||||||
|
var sources = [];
|
||||||
|
if (inputs["i18n.hpp"] !== undefined)
|
||||||
|
for (var i = 0; i < inputs["i18n.hpp"].length; i++)
|
||||||
|
sources.push(inputs["i18n.hpp"][i].filePath);
|
||||||
|
|
||||||
|
if (inputs["i18n.src"] !== undefined)
|
||||||
|
for (var i = 0; i < inputs["i18n.src"].length; i++)
|
||||||
|
sources.push(inputs["i18n.src"][i].filePath);
|
||||||
|
|
||||||
|
if (inputs["i18n.ui"] !== undefined)
|
||||||
|
for (var i = 0; i < inputs["i18n.ui"].length; i++)
|
||||||
|
sources.push(inputs["i18n.ui"][i].filePath);
|
||||||
|
|
||||||
|
// lupdate processes QML files that are listed in the .qrc file
|
||||||
|
if (inputs["i18n.res"] !== undefined)
|
||||||
|
for (var i = 0; i < inputs["i18n.res"].length; i++)
|
||||||
|
sources.push(inputs["i18n.res"][i].filePath);
|
||||||
|
|
||||||
|
var translations = [];
|
||||||
|
if (inputs["i18n.ts"] !== undefined)
|
||||||
|
for (var i = 0; i < inputs["i18n.ts"].length; i++)
|
||||||
|
translations.push(inputs["i18n.ts"][i].filePath);
|
||||||
|
|
||||||
|
for (var i = 0; i < product.i18n.additionalTranslations.length; i++) {
|
||||||
|
var targetDirectory = product.sourceDirectory + "/" + FileInfo.path(product.i18n.additionalTranslations[i]);
|
||||||
|
if (!File.exists(targetDirectory))
|
||||||
|
console.error("Directory '" + targetDirectory + "' does not exists. Please create it.");
|
||||||
|
translations.push(product.i18n.additionalTranslations[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
var project = {
|
||||||
|
projectFile: "", // Looks like can be empty
|
||||||
|
sources: sources.sort(),
|
||||||
|
translations: translations.sort()
|
||||||
|
};
|
||||||
|
|
||||||
|
f.write(JSON.stringify([project], null, 2));
|
||||||
|
} finally {
|
||||||
|
f.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [proCmd];
|
||||||
|
}
|
||||||
|
|
||||||
|
Artifact {
|
||||||
|
filePath: product.sourceDirectory + "/" + product.name + ".i18n.json"
|
||||||
|
fileTags: ["i18n.json"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rule {
|
||||||
|
condition: !buildWithPro
|
||||||
|
inputs: ["i18n.json"]
|
||||||
|
|
||||||
|
prepare: {
|
||||||
|
var lupdateName = product.i18n.lupdateName;
|
||||||
|
var cmdLupdate = new Command(product.Qt.core.binPath + '/' + lupdateName, ["-verbose", "-project", input.filePath]);
|
||||||
|
cmdLupdate.description = "Invoking '" + lupdateName + "' program";
|
||||||
|
cmdLupdate.highlight = 'filegen';
|
||||||
|
|
||||||
|
var cmdClean = new JavaScriptCommand();
|
||||||
|
cmdClean.description = "Removing " + input.fileName;
|
||||||
|
cmdClean.highlight = "filegen";
|
||||||
|
cmdClean.sourceCode = function() {
|
||||||
|
File.remove(input.filePath);
|
||||||
|
}
|
||||||
|
return [cmdLupdate, cmdClean]
|
||||||
|
}
|
||||||
|
|
||||||
|
outputFileTags: ["i18n"]
|
||||||
|
}
|
||||||
|
}
|
40
scripts/qbs_lupdate.sh
Executable file
40
scripts/qbs_lupdate.sh
Executable file
|
@ -0,0 +1,40 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# Run this script if you want to find and update all strings in the code.
|
||||||
|
# Please, run this script from folder <root_folder>/scripts.
|
||||||
|
|
||||||
|
start=$(date +%s)
|
||||||
|
|
||||||
|
# Download all translations from transifex.com.
|
||||||
|
cd ../share/translations
|
||||||
|
RESOURCES=`find . -regextype sed -regex ".*/measurements_p[0-9]\{1,2\}\.ts"`
|
||||||
|
cd ../../scripts
|
||||||
|
|
||||||
|
# Empty means unstable branch
|
||||||
|
MEASUREMENTS_BRANCH='' # For example _05x
|
||||||
|
VALENTINA_BRANCH='' # for example 05x
|
||||||
|
|
||||||
|
NUMBER=( $RESOURCES )
|
||||||
|
NUMBER=${#NUMBER[@]}
|
||||||
|
|
||||||
|
# Certant languages like he_IL and zh_CN are not supported by math parser
|
||||||
|
for ((i=0;i<NUMBER;i++)); do
|
||||||
|
tx pull -r valentina-project.measurements_p${i}${MEASUREMENTS_BRANCH}ts --mode=developer -f --skip -l "uk,de_DE,cs,he_IL,fr_FR,it_IT,nl,id,es,fi,en_US,en_CA,en_IN,ro_RO,zh_CN,pt_BR,el_GR,pl_PL" &
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
tx pull -r valentina-project.valentina_${VALENTINA_BRANCH}ts --mode=developer -f --skip &
|
||||||
|
sleep 1
|
||||||
|
tx pull -r valentina-project.measurements_p998${MEASUREMENTS_BRANCH}ts --mode=developer -f --skip -l "uk,de_DE,cs,he_IL,fr_FR,it_IT,nl,id,es,fi,en_US,en_CA,en_IN,ro_RO,zh_CN,pt_BR,el_GR,pl_PL" &
|
||||||
|
|
||||||
|
wait
|
||||||
|
|
||||||
|
# Resolve any changes to config
|
||||||
|
qbs resolve -d ../../build_translations modules.i18n.update:true
|
||||||
|
# Update local strings
|
||||||
|
qbs -d ../../build_translations -f ../valentina.qbs -p 'Translations' modules.i18n.update:true
|
||||||
|
qbs -d ../../build_translations -f ../valentina.qbs -p 'MTranslations' modules.i18n.update:true
|
||||||
|
|
||||||
|
end=$(date +%s)
|
||||||
|
runtime=$(python -c "print('Time passed %u:%02u seconds' % ((${end} - ${start})/60, (${end} - ${start})%60))")
|
||||||
|
echo $runtime
|
||||||
|
|
72
share/translations/measurements.qbs
Normal file
72
share/translations/measurements.qbs
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
import qbs.FileInfo
|
||||||
|
|
||||||
|
Product {
|
||||||
|
Depends { name: "i18n" }
|
||||||
|
|
||||||
|
name: "MTranslations"
|
||||||
|
type: "ts"
|
||||||
|
builtByDefault: false
|
||||||
|
|
||||||
|
Group {
|
||||||
|
name: "Headers"
|
||||||
|
prefix: FileInfo.joinPaths(project.sourceDirectory, "src", FileInfo.pathSeparator())
|
||||||
|
files: [
|
||||||
|
"libs/vpatterndb/vtranslatemeasurements.h"
|
||||||
|
]
|
||||||
|
fileTags: "i18n.hpp"
|
||||||
|
}
|
||||||
|
|
||||||
|
Group {
|
||||||
|
name: "Sources"
|
||||||
|
prefix: FileInfo.joinPaths(project.sourceDirectory, "src", FileInfo.pathSeparator())
|
||||||
|
files: [
|
||||||
|
"libs/vpatterndb/vtranslatemeasurements.cpp"
|
||||||
|
]
|
||||||
|
fileTags: "i18n.src"
|
||||||
|
}
|
||||||
|
|
||||||
|
Group {
|
||||||
|
name: "Translations"
|
||||||
|
files: {
|
||||||
|
var files = [];
|
||||||
|
|
||||||
|
var locales = [
|
||||||
|
"uk_UA",
|
||||||
|
"de_DE",
|
||||||
|
"cs_CZ",
|
||||||
|
"he_IL",
|
||||||
|
"fr_FR",
|
||||||
|
"it_IT",
|
||||||
|
"nl_NL",
|
||||||
|
"id_ID",
|
||||||
|
"es_ES",
|
||||||
|
"fi_FI",
|
||||||
|
"en_US",
|
||||||
|
"en_CA",
|
||||||
|
"en_IN",
|
||||||
|
"ro_RO",
|
||||||
|
"zh_CN",
|
||||||
|
"pt_BR",
|
||||||
|
"el_GR",
|
||||||
|
"pl_PL"
|
||||||
|
];
|
||||||
|
|
||||||
|
var pmSystems = [
|
||||||
|
"p0", "p1", "p2", "p3", "p4", "p5", "p6", "p7", "p8", "p9", "p10", "p11", "p12", "p13", "p14", "p15",
|
||||||
|
"p16", "p17", "p18", "p19", "p20", "p21", "p22", "p23", "p24", "p25", "p26", "p27", "p28", "p29",
|
||||||
|
"p30", "p31", "p32", "p33", "p34", "p35", "p36", "p37", "p38", "p39", "p40", "p41", "p42", "p43",
|
||||||
|
"p44", "p45", "p46", "p47", "p48", "p49", "p50", "p51", "p52", "p53", "p54", "p998"
|
||||||
|
]
|
||||||
|
|
||||||
|
for (var i = 0; i < pmSystems.length; i++) {
|
||||||
|
files.push("measurements_" + pmSystems[i] + ".ts");
|
||||||
|
for (var j = 0; j < locales.length; j++) {
|
||||||
|
files.push("measurements_" + pmSystems[i] + "_" + locales[j] + ".ts");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
fileTags: "i18n.ts"
|
||||||
|
}
|
||||||
|
}
|
88
share/translations/translations.qbs
Normal file
88
share/translations/translations.qbs
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
import qbs.FileInfo
|
||||||
|
|
||||||
|
Product {
|
||||||
|
Depends { name: "i18n" }
|
||||||
|
|
||||||
|
name: "Translations"
|
||||||
|
type: "ts"
|
||||||
|
builtByDefault: false
|
||||||
|
|
||||||
|
Group {
|
||||||
|
name: "Headers"
|
||||||
|
prefix: FileInfo.joinPaths(project.sourceDirectory, "src", FileInfo.pathSeparator())
|
||||||
|
files: [
|
||||||
|
"app/**/*.h",
|
||||||
|
"app/**/*.hpp",
|
||||||
|
"libs/**/*.h",
|
||||||
|
"libs/**/*.hpp"
|
||||||
|
]
|
||||||
|
excludeFiles: [
|
||||||
|
"libs/vpatterndb/vtranslatemeasurements.h"
|
||||||
|
]
|
||||||
|
fileTags: "i18n.hpp"
|
||||||
|
}
|
||||||
|
|
||||||
|
Group {
|
||||||
|
name: "Sources"
|
||||||
|
prefix: FileInfo.joinPaths(project.sourceDirectory, "src", FileInfo.pathSeparator())
|
||||||
|
files: [
|
||||||
|
"app/**/*.cpp",
|
||||||
|
"app/**/*.js",
|
||||||
|
"app/**/*.qml",
|
||||||
|
"libs/**/*.cpp",
|
||||||
|
"libs/**/*.js",
|
||||||
|
"libs/**/*.qml"
|
||||||
|
]
|
||||||
|
excludeFiles: [
|
||||||
|
"libs/vpatterndb/vtranslatemeasurements.cpp"
|
||||||
|
]
|
||||||
|
fileTags: "i18n.src"
|
||||||
|
}
|
||||||
|
|
||||||
|
Group {
|
||||||
|
name: "Forms"
|
||||||
|
prefix: FileInfo.joinPaths(project.sourceDirectory, "src", FileInfo.pathSeparator())
|
||||||
|
files: [
|
||||||
|
"app/**/*.ui",
|
||||||
|
"libs/**/*.ui"
|
||||||
|
]
|
||||||
|
fileTags: "i18n.ui"
|
||||||
|
}
|
||||||
|
|
||||||
|
Group {
|
||||||
|
name: "Translations"
|
||||||
|
files: {
|
||||||
|
var files = [];
|
||||||
|
|
||||||
|
var locales = [
|
||||||
|
"uk_UA",
|
||||||
|
"de_DE",
|
||||||
|
"cs_CZ",
|
||||||
|
"he_IL",
|
||||||
|
"fr_FR",
|
||||||
|
"it_IT",
|
||||||
|
"nl_NL",
|
||||||
|
"id_ID",
|
||||||
|
"es_ES",
|
||||||
|
"fi_FI",
|
||||||
|
"en_US",
|
||||||
|
"en_CA",
|
||||||
|
"en_IN",
|
||||||
|
"ro_RO",
|
||||||
|
"zh_CN",
|
||||||
|
"pt_BR",
|
||||||
|
"el_GR",
|
||||||
|
"pl_PL"
|
||||||
|
];
|
||||||
|
|
||||||
|
files.push("valentina.ts");
|
||||||
|
|
||||||
|
for (var i = 0; i < locales.length; i++) {
|
||||||
|
files.push("valentina_" + locales[i] + ".ts");
|
||||||
|
}
|
||||||
|
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
fileTags: "i18n.ts"
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,8 @@ Project {
|
||||||
minimumQbsVersion: "1.16"
|
minimumQbsVersion: "1.16"
|
||||||
references: [
|
references: [
|
||||||
"src/src.qbs",
|
"src/src.qbs",
|
||||||
|
"share/translations/translations.qbs",
|
||||||
|
"share/translations/measurements.qbs",
|
||||||
]
|
]
|
||||||
qbsSearchPaths: "qbs"
|
qbsSearchPaths: "qbs"
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user