Koha/gulpfile.js
Jonathan Druart 312ecca51f
Bug 35024: Do not wrap translations
PO files wrap differently depending on versions.

Looks like (thanks Julian!) it's coming from libunistring, between 0.9.10 and 1.0 I am seeing a change in how the strings in the PO files are wrapped.

Debian 11 (using libunistring 0.9):
"%s %sPrintemps%s %s %sÉté%s %s %sAutomne%s %s %sHiver%s %s %sPrtps%s %s %sÉté"
"%s %s %sAut%s %s %sHiv%s %s %s %s"

Debian 12, Ubuntu 22.04(using libunistring 1.0)
"%s %sPrintemps%s %s %sÉté%s %s %sAutomne%s %s %sHiver%s %s %sPrtps%s %s "
"%sÉté%s %s %sAut%s %s %sHiv%s %s %s %s"

It *could* come from this changes:
https://git.savannah.gnu.org/gitweb/?p=libunistring.git;a=blob;f=NEWS;h=5a43ddd7011d62a952733f6c0b7ad52aa4f385c7;hb=HEAD
   * The functions u*_possible_linebreaks and u*_width_linebreaks now make it
     easier to work with strings that contain CR-LF sequences: In this case,
     in the returned array, it will return UC_BREAK_CR_BEFORE_LF followed by
     UC_BREAK_MANDATORY (instead of twice UC_BREAK_MANDATORY).

The command used is:
% msgmerge --backup=off --quiet -F --update misc/translator/po/fr-FR-staff-prog.po misc/translator/Koha-staff-prog.pot

No matter the value of --width.

One solution is to use --no-wrap, and never wrap :)

I sent an email to the list about this suggestion https://lists.katipo.co.nz/pipermail/koha/2023-October/060143.html

Test plan:
% gulp po:update --lang LANG

And notice that the lines are no wrapped anymore.

Note that in master there were already not wrapped (?)
commit edd378f4d1
Translation updates for Koha 23.05.00

Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Martin Renvoize <martin.renvoize@ptfs-europe.com>
Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io>
2023-10-19 16:00:47 -03:00

384 lines
13 KiB
JavaScript

/* eslint-env node */
/* eslint no-console:"off" */
const { dest, parallel, series, src, watch } = require('gulp');
const child_process = require('child_process');
const fs = require('fs');
const os = require('os');
const path = require('path');
const util = require('util');
const sass = require('gulp-sass')(require('sass'));
const rtlcss = require('gulp-rtlcss');
const sourcemaps = require('gulp-sourcemaps');
const autoprefixer = require('gulp-autoprefixer');
const concatPo = require('gulp-concat-po');
const exec = require('gulp-exec');
const merge = require('merge-stream');
const through2 = require('through2');
const Vinyl = require('vinyl');
const args = require('minimist')(process.argv.slice(2));
const rename = require('gulp-rename');
const STAFF_CSS_BASE = "koha-tmpl/intranet-tmpl/prog/css";
const OPAC_CSS_BASE = "koha-tmpl/opac-tmpl/bootstrap/css";
var CSS_BASE = args.view == "opac"
? OPAC_CSS_BASE
: STAFF_CSS_BASE;
var sassOptions = {
includePaths: [
__dirname + '/node_modules',
__dirname + '/../node_modules'
]
}
// CSS processing for development
function css(css_base) {
css_base = css_base || CSS_BASE
var stream = src(css_base + "/src/**/*.scss")
.pipe(sourcemaps.init())
.pipe(sass(sassOptions).on('error', sass.logError))
.pipe(autoprefixer())
.pipe(dest(css_base));
if (args.view == "opac") {
stream = stream
.pipe(rtlcss())
.pipe(rename({
suffix: '-rtl'
})) // Append "-rtl" to the filename.
.pipe(dest(css_base));
}
stream = stream.pipe(sourcemaps.write('./maps'))
.pipe(dest(css_base));
return stream;
}
// CSS processing for production
function build(css_base) {
css_base = css_base || CSS_BASE;
sassOptions.outputStyle = "compressed";
var stream = src(css_base + "/src/**/*.scss")
.pipe(sass(sassOptions).on('error', sass.logError))
.pipe(autoprefixer())
.pipe(dest(css_base));
if( args.view == "opac" ){
stream = stream.pipe(rtlcss())
.pipe(rename({
suffix: '-rtl'
})) // Append "-rtl" to the filename.
.pipe(dest(css_base));
}
return stream;
}
function opac_css(){
return css(OPAC_CSS_BASE);
}
function staff_css(){
return css(STAFF_CSS_BASE);
}
const poTasks = {
'marc-MARC21': {
extract: po_extract_marc_marc21,
create: po_create_marc_marc21,
update: po_update_marc_marc21,
},
'marc-UNIMARC': {
extract: po_extract_marc_unimarc,
create: po_create_marc_unimarc,
update: po_update_marc_unimarc,
},
'staff-prog': {
extract: po_extract_staff,
create: po_create_staff,
update: po_update_staff,
},
'opac-bootstrap': {
extract: po_extract_opac,
create: po_create_opac,
update: po_update_opac,
},
'pref': {
extract: po_extract_pref,
create: po_create_pref,
update: po_update_pref,
},
'messages': {
extract: po_extract_messages,
create: po_create_messages,
update: po_update_messages,
},
'messages-js': {
extract: po_extract_messages_js,
create: po_create_messages_js,
update: po_update_messages_js,
},
'installer': {
extract: po_extract_installer,
create: po_create_installer,
update: po_update_installer,
},
'installer-MARC21': {
extract: po_extract_installer_marc21,
create: po_create_installer_marc21,
update: po_update_installer_marc21,
},
'installer-UNIMARC': {
extract: po_extract_installer_unimarc,
create: po_create_installer_unimarc,
update: po_update_installer_unimarc,
},
};
const poTypes = Object.keys(poTasks);
function po_extract_marc (type) {
return src(`koha-tmpl/*-tmpl/*/en/**/*${type}*`, { read: false, nocase: true })
.pipe(xgettext('misc/translator/xgettext.pl --charset=UTF-8 -F', `Koha-marc-${type}.pot`))
.pipe(dest('misc/translator'))
}
function po_extract_marc_marc21 () { return po_extract_marc('MARC21') }
function po_extract_marc_unimarc () { return po_extract_marc('UNIMARC') }
function po_extract_staff () {
const globs = [
'koha-tmpl/intranet-tmpl/prog/en/**/*.tt',
'koha-tmpl/intranet-tmpl/prog/en/**/*.inc',
'koha-tmpl/intranet-tmpl/prog/en/xslt/*.xsl',
'!koha-tmpl/intranet-tmpl/prog/en/**/*MARC21*',
'!koha-tmpl/intranet-tmpl/prog/en/**/*UNIMARC*',
'!koha-tmpl/intranet-tmpl/prog/en/**/*marc21*',
'!koha-tmpl/intranet-tmpl/prog/en/**/*unimarc*',
];
return src(globs, { read: false, nocase: true })
.pipe(xgettext('misc/translator/xgettext.pl --charset=UTF-8 -F', 'Koha-staff-prog.pot'))
.pipe(dest('misc/translator'))
}
function po_extract_opac () {
const globs = [
'koha-tmpl/opac-tmpl/bootstrap/en/**/*.tt',
'koha-tmpl/opac-tmpl/bootstrap/en/**/*.inc',
'koha-tmpl/opac-tmpl/bootstrap/en/xslt/*.xsl',
'!koha-tmpl/opac-tmpl/bootstrap/en/**/*MARC21*',
'!koha-tmpl/opac-tmpl/bootstrap/en/**/*UNIMARC*',
'!koha-tmpl/opac-tmpl/bootstrap/en/**/*marc21*',
'!koha-tmpl/opac-tmpl/bootstrap/en/**/*unimarc*',
];
return src(globs, { read: false, nocase: true })
.pipe(xgettext('misc/translator/xgettext.pl --charset=UTF-8 -F', 'Koha-opac-bootstrap.pot'))
.pipe(dest('misc/translator'))
}
const xgettext_options = '--from-code=UTF-8 --package-name Koha '
+ '--package-version= -k -k__ -k__x -k__n:1,2 -k__nx:1,2 -k__xn:1,2 '
+ '-k__p:1c,2 -k__px:1c,2 -k__np:1c,2,3 -k__npx:1c,2,3 -kN__ '
+ '-kN__n:1,2 -kN__p:1c,2 -kN__np:1c,2,3 '
+ '-k -k$__ -k$__x -k$__n:1,2 -k$__nx:1,2 -k$__xn:1,2 '
+ '--force-po';
function po_extract_messages_js () {
const globs = [
'koha-tmpl/intranet-tmpl/prog/js/vue/**/*.vue',
'koha-tmpl/intranet-tmpl/prog/js/**/*.js',
'koha-tmpl/opac-tmpl/bootstrap/js/**/*.js',
];
return src(globs, { read: false, nocase: true })
.pipe(xgettext(`xgettext -L JavaScript ${xgettext_options}`, 'Koha-messages-js.pot'))
.pipe(dest('misc/translator'))
}
function po_extract_messages () {
const perlStream = src(['**/*.pl', '**/*.pm'], { read: false, nocase: true })
.pipe(xgettext(`xgettext -L Perl ${xgettext_options}`, 'Koha-perl.pot'))
const ttStream = src([
'koha-tmpl/intranet-tmpl/prog/en/**/*.tt',
'koha-tmpl/intranet-tmpl/prog/en/**/*.inc',
'koha-tmpl/opac-tmpl/bootstrap/en/**/*.tt',
'koha-tmpl/opac-tmpl/bootstrap/en/**/*.inc',
], { read: false, nocase: true })
.pipe(xgettext('misc/translator/xgettext-tt2 --from-code=UTF-8', 'Koha-tt.pot'))
const headers = {
'Project-Id-Version': 'Koha',
'Content-Type': 'text/plain; charset=UTF-8',
};
return merge(perlStream, ttStream)
.pipe(concatPo('Koha-messages.pot', { headers }))
.pipe(dest('misc/translator'))
}
function po_extract_pref () {
return src('koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/*.pref', { read: false })
.pipe(xgettext('misc/translator/xgettext-pref', 'Koha-pref.pot'))
.pipe(dest('misc/translator'))
}
function po_extract_installer () {
const globs = [
'installer/data/mysql/en/mandatory/*.yml',
'installer/data/mysql/en/optional/*.yml',
];
return src(globs, { read: false, nocase: true })
.pipe(xgettext('misc/translator/xgettext-installer', 'Koha-installer.pot'))
.pipe(dest('misc/translator'))
}
function po_extract_installer_marc (type) {
const globs = `installer/data/mysql/en/marcflavour/${type}/**/*.yml`;
return src(globs, { read: false, nocase: true })
.pipe(xgettext('misc/translator/xgettext-installer', `Koha-installer-${type}.pot`))
.pipe(dest('misc/translator'))
}
function po_extract_installer_marc21 () { return po_extract_installer_marc('MARC21') }
function po_extract_installer_unimarc () { return po_extract_installer_marc('UNIMARC') }
function po_create_type (type) {
const access = util.promisify(fs.access);
const exec = util.promisify(child_process.exec);
const languages = getLanguages();
const promises = [];
for (const language of languages) {
const locale = language.split('-').filter(s => s.length !== 4).join('_');
const po = `misc/translator/po/${language}-${type}.po`;
const pot = `misc/translator/Koha-${type}.pot`;
const promise = access(po)
.catch(() => exec(`msginit -o ${po} -i ${pot} -l ${locale} --no-translator`))
promises.push(promise);
}
return Promise.all(promises);
}
function po_create_marc_marc21 () { return po_create_type('marc-MARC21') }
function po_create_marc_unimarc () { return po_create_type('marc-UNIMARC') }
function po_create_staff () { return po_create_type('staff-prog') }
function po_create_opac () { return po_create_type('opac-bootstrap') }
function po_create_pref () { return po_create_type('pref') }
function po_create_messages () { return po_create_type('messages') }
function po_create_messages_js () { return po_create_type('messages-js') }
function po_create_installer () { return po_create_type('installer') }
function po_create_installer_marc21 () { return po_create_type('installer-MARC21') }
function po_create_installer_unimarc () { return po_create_type('installer-UNIMARC') }
function po_update_type (type) {
const msgmerge_opts = '--backup=off --no-wrap --quiet -F --update';
const cmd = `msgmerge ${msgmerge_opts} <%= file.path %> misc/translator/Koha-${type}.pot`;
const languages = getLanguages();
const globs = languages.map(language => `misc/translator/po/${language}-${type}.po`);
return src(globs)
.pipe(exec(cmd, { continueOnError: true }))
.pipe(exec.reporter({ err: false, stdout: false }))
}
function po_update_marc_marc21 () { return po_update_type('marc-MARC21') }
function po_update_marc_unimarc () { return po_update_type('marc-UNIMARC') }
function po_update_staff () { return po_update_type('staff-prog') }
function po_update_opac () { return po_update_type('opac-bootstrap') }
function po_update_pref () { return po_update_type('pref') }
function po_update_messages () { return po_update_type('messages') }
function po_update_messages_js () { return po_update_type('messages-js') }
function po_update_installer () { return po_update_type('installer') }
function po_update_installer_marc21 () { return po_update_type('installer-MARC21') }
function po_update_installer_unimarc () { return po_update_type('installer-UNIMARC') }
/**
* Gulp plugin that executes xgettext-like command `cmd` on all files given as
* input, and then outputs the result as a POT file named `filename`.
* `cmd` should accept -o and -f options
*/
function xgettext (cmd, filename) {
const filenames = [];
function transform (file, encoding, callback) {
filenames.push(path.relative(file.cwd, file.path));
callback();
}
function flush (callback) {
fs.mkdtemp(path.join(os.tmpdir(), 'koha-'), (err, folder) => {
const outputFilename = path.join(folder, filename);
const filesFilename = path.join(folder, 'files');
fs.writeFile(filesFilename, filenames.join(os.EOL), err => {
if (err) return callback(err);
const command = `${cmd} -o ${outputFilename} -f ${filesFilename}`;
child_process.exec(command, err => {
if (err) return callback(err);
fs.readFile(outputFilename, (err, data) => {
if (err) return callback(err);
const file = new Vinyl();
file.path = path.join(file.base, filename);
file.contents = data;
callback(null, file);
});
});
});
})
}
return through2.obj(transform, flush);
}
/**
* Return languages selected for PO-related tasks
*
* This can be either languages given on command-line with --lang option, or
* all the languages found in misc/translator/po otherwise
*/
function getLanguages () {
if (Array.isArray(args.lang)) {
return args.lang;
}
if (args.lang) {
return [args.lang];
}
const filenames = fs.readdirSync('misc/translator/po')
.filter(filename => filename.endsWith('.po'))
.filter(filename => !filename.startsWith('.'))
const re = new RegExp('-(' + poTypes.join('|') + ')\.po$');
languages = filenames.map(filename => filename.replace(re, ''))
return Array.from(new Set(languages));
}
exports.build = function(next){build(); next();};
exports.css = function(next){css(); next();};
exports.opac_css = opac_css;
exports.staff_css = staff_css;
exports.watch = function () {
watch(OPAC_CSS_BASE + "/src/**/*.scss", series('opac_css'));
watch(STAFF_CSS_BASE + "/src/**/*.scss", series('staff_css'));
};
exports['po:create'] = parallel(...poTypes.map(type => series(poTasks[type].extract, poTasks[type].create)));
exports['po:update'] = parallel(...poTypes.map(type => series(poTasks[type].extract, poTasks[type].update)));
exports['po:extract'] = parallel(...poTypes.map(type => poTasks[type].extract));