2 /* eslint no-console:"off" */
4 const { dest, parallel, series, src, watch } = require('gulp');
6 const child_process = require('child_process');
7 const fs = require('fs');
8 const os = require('os');
9 const path = require('path');
10 const util = require('util');
12 const sass = require("gulp-sass");
13 const cssnano = require("gulp-cssnano");
14 const rtlcss = require('gulp-rtlcss');
15 const sourcemaps = require('gulp-sourcemaps');
16 const autoprefixer = require('gulp-autoprefixer');
17 const concatPo = require('gulp-concat-po');
18 const exec = require('gulp-exec');
19 const merge = require('merge-stream');
20 const through2 = require('through2');
21 const Vinyl = require('vinyl');
22 const args = require('minimist')(process.argv.slice(2));
23 const rename = require('gulp-rename');
25 const STAFF_JS_BASE = "koha-tmpl/intranet-tmpl/prog/js";
26 const STAFF_CSS_BASE = "koha-tmpl/intranet-tmpl/prog/css";
27 const OPAC_JS_BASE = "koha-tmpl/opac-tmpl/bootstrap/js";
28 const OPAC_CSS_BASE = "koha-tmpl/opac-tmpl/bootstrap/css";
30 if (args.view == "opac") {
31 var css_base = OPAC_CSS_BASE;
32 var js_base = OPAC_JS_BASE;
34 var css_base = STAFF_CSS_BASE;
35 var js_base = STAFF_JS_BASE;
39 errLogToConsole: true,
43 // CSS processing for development
45 var stream = src(css_base + "/src/**/*.scss")
46 .pipe(sourcemaps.init())
47 .pipe(sass(sassOptions).on('error', sass.logError))
49 .pipe(dest(css_base));
51 if (args.view == "opac") {
56 })) // Append "-rtl" to the filename.
57 .pipe(dest(css_base));
60 stream = stream.pipe(sourcemaps.write('./maps'))
61 .pipe(dest(css_base));
67 // CSS processing for production
69 var stream = src(css_base + "/src/**/*.scss")
70 .pipe(sass(sassOptions).on('error', sass.logError))
75 .pipe(dest(css_base));
77 if( args.view == "opac" ){
78 stream = stream.pipe(rtlcss())
81 })) // Append "-rtl" to the filename.
82 .pipe(dest(css_base));
90 extract: po_extract_marc_marc21,
91 create: po_create_marc_marc21,
92 update: po_update_marc_marc21,
95 extract: po_extract_marc_unimarc,
96 create: po_create_marc_unimarc,
97 update: po_update_marc_unimarc,
100 extract: po_extract_staff,
101 create: po_create_staff,
102 update: po_update_staff,
105 extract: po_extract_opac,
106 create: po_create_opac,
107 update: po_update_opac,
110 extract: po_extract_pref,
111 create: po_create_pref,
112 update: po_update_pref,
115 extract: po_extract_messages,
116 create: po_create_messages,
117 update: po_update_messages,
120 extract: po_extract_messages_js,
121 create: po_create_messages_js,
122 update: po_update_messages_js,
125 extract: po_extract_installer,
126 create: po_create_installer,
127 update: po_update_installer,
129 'installer-MARC21': {
130 extract: po_extract_installer_marc21,
131 create: po_create_installer_marc21,
132 update: po_update_installer_marc21,
134 'installer-UNIMARC': {
135 extract: po_extract_installer_unimarc,
136 create: po_create_installer_unimarc,
137 update: po_update_installer_unimarc,
141 const poTypes = Object.keys(poTasks);
143 function po_extract_marc (type) {
144 return src(`koha-tmpl/*-tmpl/*/en/**/*${type}*`, { read: false, nocase: true })
145 .pipe(xgettext('misc/translator/xgettext.pl --charset=UTF-8 -s', `Koha-marc-${type}.pot`))
146 .pipe(dest('misc/translator'))
149 function po_extract_marc_marc21 () { return po_extract_marc('MARC21') }
150 function po_extract_marc_unimarc () { return po_extract_marc('UNIMARC') }
152 function po_extract_staff () {
154 'koha-tmpl/intranet-tmpl/prog/en/**/*.tt',
155 'koha-tmpl/intranet-tmpl/prog/en/**/*.inc',
156 'koha-tmpl/intranet-tmpl/prog/en/xslt/*.xsl',
157 '!koha-tmpl/intranet-tmpl/prog/en/**/*MARC21*',
158 '!koha-tmpl/intranet-tmpl/prog/en/**/*UNIMARC*',
159 '!koha-tmpl/intranet-tmpl/prog/en/**/*marc21*',
160 '!koha-tmpl/intranet-tmpl/prog/en/**/*unimarc*',
163 return src(globs, { read: false, nocase: true })
164 .pipe(xgettext('misc/translator/xgettext.pl --charset=UTF-8 -s', 'Koha-staff-prog.pot'))
165 .pipe(dest('misc/translator'))
168 function po_extract_opac () {
170 'koha-tmpl/opac-tmpl/bootstrap/en/**/*.tt',
171 'koha-tmpl/opac-tmpl/bootstrap/en/**/*.inc',
172 'koha-tmpl/opac-tmpl/bootstrap/en/xslt/*.xsl',
173 '!koha-tmpl/opac-tmpl/bootstrap/en/**/*MARC21*',
174 '!koha-tmpl/opac-tmpl/bootstrap/en/**/*UNIMARC*',
175 '!koha-tmpl/opac-tmpl/bootstrap/en/**/*marc21*',
176 '!koha-tmpl/opac-tmpl/bootstrap/en/**/*unimarc*',
179 return src(globs, { read: false, nocase: true })
180 .pipe(xgettext('misc/translator/xgettext.pl --charset=UTF-8 -s', 'Koha-opac-bootstrap.pot'))
181 .pipe(dest('misc/translator'))
184 const xgettext_options = '--from-code=UTF-8 --package-name Koha '
185 + '--package-version= -k -k__ -k__x -k__n:1,2 -k__nx:1,2 -k__xn:1,2 '
186 + '-k__p:1c,2 -k__px:1c,2 -k__np:1c,2,3 -k__npx:1c,2,3 -kN__ '
187 + '-kN__n:1,2 -kN__p:1c,2 -kN__np:1c,2,3 '
188 + '-k -k$__ -k$__x -k$__n:1,2 -k$__nx:1,2 -k$__xn:1,2 '
191 function po_extract_messages_js () {
193 'koha-tmpl/intranet-tmpl/prog/js/vue/**/*.vue',
194 'koha-tmpl/intranet-tmpl/prog/js/**/*.js',
195 'koha-tmpl/opac-tmpl/bootstrap/js/**/*.js',
198 return src(globs, { read: false, nocase: true })
199 .pipe(xgettext(`xgettext -L JavaScript ${xgettext_options}`, 'Koha-messages-js.pot'))
200 .pipe(dest('misc/translator'))
203 function po_extract_messages () {
204 const perlStream = src(['**/*.pl', '**/*.pm'], { read: false, nocase: true })
205 .pipe(xgettext(`xgettext -L Perl ${xgettext_options}`, 'Koha-perl.pot'))
207 const ttStream = src([
208 'koha-tmpl/intranet-tmpl/prog/en/**/*.tt',
209 'koha-tmpl/intranet-tmpl/prog/en/**/*.inc',
210 'koha-tmpl/opac-tmpl/bootstrap/en/**/*.tt',
211 'koha-tmpl/opac-tmpl/bootstrap/en/**/*.inc',
212 ], { read: false, nocase: true })
213 .pipe(xgettext('misc/translator/xgettext-tt2 --from-code=UTF-8', 'Koha-tt.pot'))
216 'Project-Id-Version': 'Koha',
217 'Content-Type': 'text/plain; charset=UTF-8',
220 return merge(perlStream, ttStream)
221 .pipe(concatPo('Koha-messages.pot', { headers }))
222 .pipe(dest('misc/translator'))
225 function po_extract_pref () {
226 return src('koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/*.pref', { read: false })
227 .pipe(xgettext('misc/translator/xgettext-pref', 'Koha-pref.pot'))
228 .pipe(dest('misc/translator'))
231 function po_extract_installer () {
233 'installer/data/mysql/en/mandatory/*.yml',
234 'installer/data/mysql/en/optional/*.yml',
237 return src(globs, { read: false, nocase: true })
238 .pipe(xgettext('misc/translator/xgettext-installer', 'Koha-installer.pot'))
239 .pipe(dest('misc/translator'))
242 function po_extract_installer_marc (type) {
243 const globs = `installer/data/mysql/en/marcflavour/${type}/**/*.yml`;
245 return src(globs, { read: false, nocase: true })
246 .pipe(xgettext('misc/translator/xgettext-installer', `Koha-installer-${type}.pot`))
247 .pipe(dest('misc/translator'))
250 function po_extract_installer_marc21 () { return po_extract_installer_marc('MARC21') }
252 function po_extract_installer_unimarc () { return po_extract_installer_marc('UNIMARC') }
254 function po_create_type (type) {
255 const access = util.promisify(fs.access);
256 const exec = util.promisify(child_process.exec);
258 const languages = getLanguages();
260 for (const language of languages) {
261 const locale = language.split('-').filter(s => s.length !== 4).join('_');
262 const po = `misc/translator/po/${language}-${type}.po`;
263 const pot = `misc/translator/Koha-${type}.pot`;
265 const promise = access(po)
266 .catch(() => exec(`msginit -o ${po} -i ${pot} -l ${locale} --no-translator`))
267 promises.push(promise);
270 return Promise.all(promises);
273 function po_create_marc_marc21 () { return po_create_type('marc-MARC21') }
274 function po_create_marc_unimarc () { return po_create_type('marc-UNIMARC') }
275 function po_create_staff () { return po_create_type('staff-prog') }
276 function po_create_opac () { return po_create_type('opac-bootstrap') }
277 function po_create_pref () { return po_create_type('pref') }
278 function po_create_messages () { return po_create_type('messages') }
279 function po_create_messages_js () { return po_create_type('messages-js') }
280 function po_create_installer () { return po_create_type('installer') }
281 function po_create_installer_marc21 () { return po_create_type('installer-MARC21') }
282 function po_create_installer_unimarc () { return po_create_type('installer-UNIMARC') }
284 function po_update_type (type) {
285 const msgmerge_opts = '--backup=off --quiet --sort-output --update';
286 const cmd = `msgmerge ${msgmerge_opts} <%= file.path %> misc/translator/Koha-${type}.pot`;
287 const languages = getLanguages();
288 const globs = languages.map(language => `misc/translator/po/${language}-${type}.po`);
291 .pipe(exec(cmd, { continueOnError: true }))
292 .pipe(exec.reporter({ err: false, stdout: false }))
295 function po_update_marc_marc21 () { return po_update_type('marc-MARC21') }
296 function po_update_marc_unimarc () { return po_update_type('marc-UNIMARC') }
297 function po_update_staff () { return po_update_type('staff-prog') }
298 function po_update_opac () { return po_update_type('opac-bootstrap') }
299 function po_update_pref () { return po_update_type('pref') }
300 function po_update_messages () { return po_update_type('messages') }
301 function po_update_messages_js () { return po_update_type('messages-js') }
302 function po_update_installer () { return po_update_type('installer') }
303 function po_update_installer_marc21 () { return po_update_type('installer-MARC21') }
304 function po_update_installer_unimarc () { return po_update_type('installer-UNIMARC') }
307 * Gulp plugin that executes xgettext-like command `cmd` on all files given as
308 * input, and then outputs the result as a POT file named `filename`.
309 * `cmd` should accept -o and -f options
311 function xgettext (cmd, filename) {
312 const filenames = [];
314 function transform (file, encoding, callback) {
315 filenames.push(path.relative(file.cwd, file.path));
319 function flush (callback) {
320 fs.mkdtemp(path.join(os.tmpdir(), 'koha-'), (err, folder) => {
321 const outputFilename = path.join(folder, filename);
322 const filesFilename = path.join(folder, 'files');
323 fs.writeFile(filesFilename, filenames.join(os.EOL), err => {
324 if (err) return callback(err);
326 const command = `${cmd} -o ${outputFilename} -f ${filesFilename}`;
327 child_process.exec(command, err => {
328 if (err) return callback(err);
330 fs.readFile(outputFilename, (err, data) => {
331 if (err) return callback(err);
333 const file = new Vinyl();
334 file.path = path.join(file.base, filename);
335 file.contents = data;
336 callback(null, file);
343 return through2.obj(transform, flush);
347 * Return languages selected for PO-related tasks
349 * This can be either languages given on command-line with --lang option, or
350 * all the languages found in misc/translator/po otherwise
352 function getLanguages () {
353 if (Array.isArray(args.lang)) {
361 const filenames = fs.readdirSync('misc/translator/po')
362 .filter(filename => filename.endsWith('.po'))
363 .filter(filename => !filename.startsWith('.'))
365 const re = new RegExp('-(' + poTypes.join('|') + ')\.po$');
366 languages = filenames.map(filename => filename.replace(re, ''))
368 return Array.from(new Set(languages));
371 exports.build = build;
374 exports['po:create'] = parallel(...poTypes.map(type => series(poTasks[type].extract, poTasks[type].create)));
375 exports['po:update'] = parallel(...poTypes.map(type => series(poTasks[type].extract, poTasks[type].update)));
376 exports['po:extract'] = parallel(...poTypes.map(type => poTasks[type].extract));
378 exports.default = function () {
379 watch(css_base + "/src/**/*.scss", series('css'));