Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 65x 65x 27x 19x 19x 27x 27x 61x 24x 7x 7x 24x 24x 14x 14x 65x 65x 65x 8x 65x 8x 19x 19x 18x 18x 19x 8x 65x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 8x 8x 8x 8x 8x 8x 8x 8x 8x 8x 36x 8x 8x 7x 7x 7x 36x 36x 8x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 15x 15x 15x 6x 6x 6x 6x 6x 6x 6x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 5x 4x 4x 4x 4x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 5x 5x 5x 2x 2x 2x 3x 3x 3x 5x 6x 6x 6x 6x 6x 6x 6x | /**
* Library for the `emit-widget-strings` bin: one `_x()` per translatable
* `widget.json` value, so gettext extraction sees the strings the widget
* registry translates at hydration. Rationale and usage: README,
* "widget.json strings".
*/
const fs = require( 'fs' );
const path = require( 'path' );
/**
* Walk a metadata value in lockstep with its i18n schema, the way core's
* `translate_settings_using_i18n_schema()` does, reporting every translatable
* string with the context the schema assigns it.
*
* @param {*} schema - Schema node: a context string, a one-element
* list, or a map of keys (`*` matching any key).
* @param {*} value - The metadata node under that schema.
* @param {Function} onString - Receives `( msgid, context )`.
*/
function walkSchema( schema, value, onString ) {
if ( typeof schema === 'string' ) {
if ( typeof value === 'string' && value !== '' ) {
onString( value, schema );
}
return;
}
if ( Array.isArray( schema ) ) {
if ( Array.isArray( value ) && schema.length > 0 ) {
value.forEach( item => walkSchema( schema[ 0 ], item, onString ) );
}
return;
}
if (
schema &&
typeof schema === 'object' &&
value &&
typeof value === 'object' &&
! Array.isArray( value )
) {
for ( const [ key, item ] of Object.entries( value ) ) {
const sub = Object.hasOwn( schema, key ) ? schema[ key ] : schema[ '*' ];
if ( sub !== undefined ) {
walkSchema( sub, item, onString );
}
}
}
}
/**
* Collect the translatable strings of every `<widgetsDir>/<dir>/widget.json`.
*
* @param {string} widgetsDir - Directory holding one folder per widget.
* @param {object} schema - The widget i18n schema.
* @param {string} domain - Text domain for widgets that declare none.
* @return {{ entries: Array<{ msgid: string, context: string, domain: string }>, widgets: number }} The unique (msgid, context, domain) triples in widget order, and the number of manifests read.
*/
function collectWidgetStrings( widgetsDir, schema, domain ) {
const entries = [];
const seen = new Set();
let widgets = 0;
const dirs = fs.existsSync( widgetsDir ) ? fs.readdirSync( widgetsDir ).sort() : [];
for ( const dir of dirs ) {
const file = path.join( widgetsDir, dir, 'widget.json' );
if ( ! fs.existsSync( file ) ) {
continue;
}
const widget = JSON.parse( fs.readFileSync( file, 'utf8' ) );
widgets++;
const textdomain =
typeof widget.textdomain === 'string' && widget.textdomain !== ''
? widget.textdomain
: domain;
for ( const [ field, fieldSchema ] of Object.entries( schema ) ) {
walkSchema( fieldSchema, widget[ field ], ( msgid, context ) => {
const key = JSON.stringify( [ msgid, context, textdomain ] );
if ( ! seen.has( key ) ) {
seen.add( key );
entries.push( { msgid, context, domain: textdomain } );
}
} );
}
}
return { entries, widgets };
}
/**
* A PHP single-quoted string literal.
*
* @param {string} value - The string.
* @return {string} The literal.
*/
function toPhpString( value ) {
return `'${ value.replace( /\\/g, '\\\\' ).replace( /'/g, "\\'" ) }'`;
}
/**
* Render the stub file.
*
* @param {Array<{ msgid: string, context: string, domain: string }>} entries - The triples to emit.
* @return {string} PHP source.
*/
function formatWidgetStringsPhp( entries ) {
return [
'<?php',
'/**',
' * Gettext reference stub for the widget.json metadata of the widgets in this',
' * package: one call per translatable value, with the context the widget',
' * registry looks up at hydration. Generated by emit-widget-strings; never loaded.',
' */',
'',
...entries.map(
( { msgid, context, domain } ) =>
`_x( ${ toPhpString( msgid ) }, ${ toPhpString( context ) }, ${ toPhpString( domain ) } );`
),
'',
].join( '\n' );
}
/**
* Write the stub for a package, or remove a stale one when there is nothing to emit.
*
* @param {object} options - Options.
* @param {string} options.widgetsDir - Directory holding one folder per widget.
* @param {object} options.schema - The widget i18n schema.
* @param {string} options.domain - Text domain for widgets that declare none.
* @param {string} options.outFile - Path of the stub to write.
* @return {{ entries: number, widgets: number, written: boolean }} What happened.
*/
function emitWidgetStrings( { widgetsDir, schema, domain, outFile } ) {
const { entries, widgets } = collectWidgetStrings( widgetsDir, schema, domain );
if ( entries.length === 0 ) {
fs.rmSync( outFile, { force: true } );
return { entries: 0, widgets, written: false };
}
fs.mkdirSync( path.dirname( outFile ), { recursive: true } );
fs.writeFileSync( outFile, formatWidgetStringsPhp( entries ) );
return { entries: entries.length, widgets, written: true };
}
module.exports = {
walkSchema,
collectWidgetStrings,
formatWidgetStringsPhp,
emitWidgetStrings,
};
|