130 lines
3.1 KiB
PHP
130 lines
3.1 KiB
PHP
<?php
|
|||
|
|
/**
|
||
|
|
* Verify that every translation call carries the plugin's text domain.
|
||
|
|
*
|
||
|
|
* A mistyped or missing domain does not break anything visibly — the string
|
||
|
|
* simply never gets translated, and nobody notices until the shop is asked to
|
||
|
|
* run in another language.
|
||
|
|
*
|
||
|
|
* Tokenised rather than grepped, so a domain-looking string inside a sentence
|
||
|
|
* cannot pass for the real thing.
|
||
|
|
*/
|
||
|
|
|
||
|
|
// Copied between plugins, so there is no sensible default domain: a wrong one
|
||
|
|
// would report "all good" while checking for somebody else's text domain.
|
||
|
|
if ( empty( $argv[1] ) ) {
|
||
|
|
fwrite( STDERR, "Usage: php ci/check-textdomain.php <text-domain> [root]\n" );
|
||
|
|
exit( 1 );
|
||
|
|
}
|
||
|
|
|
||
|
|
$domain = $argv[1];
|
||
|
|
$root = $argv[2] ?? '.';
|
||
|
|
$functions = array(
|
||
|
|
'__',
|
||
|
|
'_e',
|
||
|
|
'_x',
|
||
|
|
'_ex',
|
||
|
|
'_n',
|
||
|
|
'_nx',
|
||
|
|
'_n_noop',
|
||
|
|
'_nx_noop',
|
||
|
|
'esc_html__',
|
||
|
|
'esc_html_e',
|
||
|
|
'esc_html_x',
|
||
|
|
'esc_attr__',
|
||
|
|
'esc_attr_e',
|
||
|
|
'esc_attr_x',
|
||
|
|
);
|
||
|
|
|
||
|
|
$problems = 0;
|
||
|
|
|
||
|
|
$iterator = new RecursiveIteratorIterator(
|
||
|
|
new RecursiveCallbackFilterIterator(
|
||
|
|
new RecursiveDirectoryIterator( $root, FilesystemIterator::SKIP_DOTS ),
|
||
|
|
static function ( $file ) {
|
||
|
|
$name = $file->getFilename();
|
||
|
|
return ! in_array( $name, array( '.git', 'node_modules', 'vendor', 'build' ), true );
|
||
|
|
}
|
||
|
|
)
|
||
|
|
);
|
||
|
|
|
||
|
|
foreach ( $iterator as $file ) {
|
||
|
|
if ( 'php' !== strtolower( $file->getExtension() ) ) {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
$path = $file->getPathname();
|
||
|
|
$tokens = token_get_all( file_get_contents( $path ) );
|
||
|
|
$count = count( $tokens );
|
||
|
|
|
||
|
|
for ( $i = 0; $i < $count; $i++ ) {
|
||
|
|
$token = $tokens[ $i ];
|
||
|
|
|
||
|
|
if ( ! is_array( $token ) || T_STRING !== $token[0] || ! in_array( $token[1], $functions, true ) ) {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Skip method calls and definitions: ->__(), function __().
|
||
|
|
$prev = $i - 1;
|
||
|
|
while ( $prev >= 0 && is_array( $tokens[ $prev ] ) && T_WHITESPACE === $tokens[ $prev ][0] ) {
|
||
|
|
--$prev;
|
||
|
|
}
|
||
|
|
if ( $prev >= 0 && is_array( $tokens[ $prev ] ) && in_array( $tokens[ $prev ][0], array( T_OBJECT_OPERATOR, T_DOUBLE_COLON, T_FUNCTION ), true ) ) {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Find the opening parenthesis.
|
||
|
|
$j = $i + 1;
|
||
|
|
while ( $j < $count && is_array( $tokens[ $j ] ) && T_WHITESPACE === $tokens[ $j ][0] ) {
|
||
|
|
++$j;
|
||
|
|
}
|
||
|
|
if ( $j >= $count || '(' !== $tokens[ $j ] ) {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Collect every literal string passed at the top level of this call.
|
||
|
|
$depth = 0;
|
||
|
|
$strings = array();
|
||
|
|
|
||
|
|
for ( $k = $j; $k < $count; $k++ ) {
|
||
|
|
$t = $tokens[ $k ];
|
||
|
|
|
||
|
|
if ( '(' === $t ) {
|
||
|
|
++$depth;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
if ( ')' === $t ) {
|
||
|
|
--$depth;
|
||
|
|
if ( 0 === $depth ) {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
if ( is_array( $t ) && T_CONSTANT_ENCAPSED_STRING === $t[0] ) {
|
||
|
|
$strings[] = trim( $t[1], "'\"" );
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if ( ! in_array( $domain, $strings, true ) ) {
|
||
|
|
printf(
|
||
|
|
"::error file=%s,line=%d::The %s() call is missing the text domain '%s'.\n",
|
||
|
|
ltrim( str_replace( $root, '', $path ), '/' ),
|
||
|
|
$token[2],
|
||
|
|
$token[1],
|
||
|
|
$domain
|
||
|
|
);
|
||
|
|
++$problems;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if ( $problems > 0 ) {
|
||
|
|
printf( "\n%d translation calls are missing the text domain.\n", $problems );
|
||
|
|
exit( 1 );
|
||
|
|
}
|
||
|
|
|
||
|
|
echo "Every translation call carries the text domain '{$domain}'.\n";
|
||
|
|
exit( 0 );
|