Skip to content

Commit 0d50ef7

Browse files
authored
Merge branch 'main' into copilot/fix-1570774-111592377-5ffacbc1-5e8a-4e4c-b0c9-3ffb5c94276f
2 parents e21bc5a + 76c354a commit 0d50ef7

6 files changed

Lines changed: 903 additions & 4 deletions

File tree

.github/workflows/copilot-setup-steps.yml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,7 @@ jobs:
2121

2222
- name: Check existence of composer.json file
2323
id: check_composer_file
24-
uses: andstor/file-existence-action@076e0072799f4942c8bc574a82233e1e4d13e9d6 # v3
25-
with:
26-
files: "composer.json"
24+
run: echo "files_exists=$(test -f composer.json && echo true || echo false)" >> "$GITHUB_OUTPUT"
2725

2826
- name: Set up PHP environment
2927
if: steps.check_composer_file.outputs.files_exists == 'true'
@@ -38,7 +36,7 @@ jobs:
3836

3937
- name: Install Composer dependencies & cache dependencies
4038
if: steps.check_composer_file.outputs.files_exists == 'true'
41-
uses: ramsey/composer-install@3cf229dc2919194e9e36783941438d17239e8520 # v3
39+
uses: ramsey/composer-install@a35c6ebd3d08125aaf8852dff361e686a1a67947 # v3
4240
env:
4341
COMPOSER_ROOT_VERSION: dev-${{ github.event.repository.default_branch }}
4442
with:

README.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,81 @@ wp i18n
2626

2727

2828

29+
### wp i18n audit
30+
31+
Audit strings in a project.
32+
33+
~~~
34+
wp i18n audit <source> [--slug=<slug>] [--domain=<domain>] [--ignore-domain] [--include=<paths>] [--exclude=<paths>] [--skip-js] [--skip-php] [--skip-blade] [--skip-block-json] [--skip-theme-json] [--format=<format>]
35+
~~~
36+
37+
Scans PHP, Blade-PHP and JavaScript files for translatable strings to find possible mistakes.
38+
39+
**OPTIONS**
40+
41+
<source>
42+
Directory to scan for string extraction.
43+
44+
[--slug=<slug>]
45+
Plugin or theme slug. Defaults to the source directory's basename.
46+
47+
[--domain=<domain>]
48+
Text domain to look for in the source code, unless the `--ignore-domain` option is used.
49+
By default, the "Text Domain" header of the plugin or theme is used.
50+
If none is provided, it falls back to the project slug.
51+
52+
[--ignore-domain]
53+
Ignore the text domain completely and extract strings with any text domain.
54+
55+
[--include=<paths>]
56+
Comma-separated list of files and paths that should be used for string extraction.
57+
If provided, only these files and folders will be taken into account.
58+
59+
[--exclude=<paths>]
60+
Comma-separated list of files and paths that should be ignored for string extraction.
61+
For example, `--exclude=.github,myfile.php` would ignore any strings found within `myfile.php` or the `.github`
62+
folder. Simple glob patterns can be used, i.e. `--exclude=foo-*.php` excludes any PHP file with the `foo-`
63+
prefix. Leading and trailing slashes are ignored, i.e. `/my/directory/` is the same as `my/directory`. The
64+
following files and folders are always excluded: node_modules, .git, .svn, .CVS, .hg, vendor, *.min.js, test, tests.
65+
66+
[--skip-js]
67+
Skips JavaScript string extraction.
68+
69+
[--skip-php]
70+
Skips PHP string extraction.
71+
72+
[--skip-blade]
73+
Skips Blade-PHP string extraction.
74+
75+
[--skip-block-json]
76+
Skips string extraction from block.json files.
77+
78+
[--skip-theme-json]
79+
Skips string extraction from theme.json files.
80+
81+
[--format=<format>]
82+
Output format for the audit results.
83+
---
84+
default: plaintext
85+
options:
86+
- plaintext
87+
- json
88+
- github-actions
89+
---
90+
91+
**EXAMPLES**
92+
93+
# Audit a plugin for possible translation issues.
94+
$ wp i18n audit wp-content/plugins/hello-world
95+
96+
# Audit a plugin and output results as JSON.
97+
$ wp i18n audit wp-content/plugins/hello-world --format=json
98+
99+
# Audit a plugin with GitHub Actions annotations format.
100+
$ wp i18n audit wp-content/plugins/hello-world --format=github-actions
101+
102+
103+
29104
### wp i18n make-pot
30105

31106
Create a POT file for a WordPress project.

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
"bundled": true,
4242
"commands": [
4343
"i18n",
44+
"i18n audit",
4445
"i18n make-pot",
4546
"i18n make-json",
4647
"i18n make-mo",

features/audit.feature

Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
Feature: Audit strings in a WordPress project
2+
3+
Background:
4+
Given a WP install
5+
6+
Scenario: Audits a plugin for translation issues
7+
Given an empty foo-plugin directory
8+
And a foo-plugin/foo-plugin.php file:
9+
"""
10+
<?php
11+
/**
12+
* Plugin Name: Foo Plugin
13+
* Text Domain: foo-plugin
14+
*/
15+
16+
__( 'Hello %s', 'foo-plugin' );
17+
"""
18+
19+
When I try `wp i18n audit foo-plugin`
20+
Then STDERR should contain:
21+
"""
22+
Warning: foo-plugin.php:7: The string "Hello %s" contains placeholders but has no "translators:" comment to clarify their meaning.
23+
"""
24+
And STDERR should contain:
25+
"""
26+
Warning: Found 1 issue.
27+
"""
28+
And the return code should be 0
29+
30+
Scenario: Audits a plugin and finds no issues
31+
Given an empty foo-plugin directory
32+
And a foo-plugin/foo-plugin.php file:
33+
"""
34+
<?php
35+
/**
36+
* Plugin Name: Foo Plugin
37+
* Text Domain: foo-plugin
38+
*/
39+
40+
__( 'Hello World', 'foo-plugin' );
41+
"""
42+
43+
When I run `wp i18n audit foo-plugin`
44+
Then STDOUT should contain:
45+
"""
46+
Success: No issues found.
47+
"""
48+
And STDERR should be empty
49+
50+
Scenario: Outputs audit results as JSON
51+
Given an empty foo-plugin directory
52+
And a foo-plugin/foo-plugin.php file:
53+
"""
54+
<?php
55+
/**
56+
* Plugin Name: Foo Plugin
57+
* Text Domain: foo-plugin
58+
*/
59+
60+
__( 'Hello %s', 'foo-plugin' );
61+
"""
62+
63+
When I run `wp i18n audit foo-plugin --format=json`
64+
Then STDOUT should contain:
65+
"""
66+
"file": "foo-plugin.php"
67+
"""
68+
And STDOUT should contain:
69+
"""
70+
"line": 7
71+
"""
72+
And STDOUT should contain:
73+
"""
74+
"message": "The string \"Hello %s\" contains placeholders but has no \"translators:\" comment to clarify their meaning."
75+
"""
76+
And STDOUT should contain:
77+
"""
78+
"code": "missing-translator-comment"
79+
"""
80+
81+
Scenario: Outputs audit results in GitHub Actions format
82+
Given an empty foo-plugin directory
83+
And a foo-plugin/foo-plugin.php file:
84+
"""
85+
<?php
86+
/**
87+
* Plugin Name: Foo Plugin
88+
* Text Domain: foo-plugin
89+
*/
90+
91+
__( 'Hello %s', 'foo-plugin' );
92+
"""
93+
94+
When I run `wp i18n audit foo-plugin --format=github-actions`
95+
Then STDOUT should contain:
96+
"""
97+
::warning file=foo-plugin.php,line=7::The string "Hello %s" contains placeholders but has no "translators:" comment to clarify their meaning.
98+
"""
99+
100+
Scenario: Detects multiple unordered placeholders
101+
Given an empty foo-plugin directory
102+
And a foo-plugin/foo-plugin.php file:
103+
"""
104+
<?php
105+
/**
106+
* Plugin Name: Foo Plugin
107+
* Text Domain: foo-plugin
108+
*/
109+
110+
__( 'Hello %s %s', 'foo-plugin' );
111+
"""
112+
113+
When I try `wp i18n audit foo-plugin`
114+
Then STDERR should contain:
115+
"""
116+
Warning: foo-plugin.php:7: Multiple placeholders should be ordered.
117+
"""
118+
119+
Scenario: Detects strings without translatable content
120+
Given an empty foo-plugin directory
121+
And a foo-plugin/foo-plugin.php file:
122+
"""
123+
<?php
124+
/**
125+
* Plugin Name: Foo Plugin
126+
* Text Domain: foo-plugin
127+
*/
128+
129+
__( '%s', 'foo-plugin' );
130+
"""
131+
132+
When I try `wp i18n audit foo-plugin`
133+
Then STDERR should contain:
134+
"""
135+
Warning: foo-plugin.php:7: Found string without translatable content.
136+
"""
137+
138+
Scenario: Detects multiple translator comments
139+
Given an empty foo-plugin directory
140+
And a foo-plugin/foo-plugin.php file:
141+
"""
142+
<?php
143+
/**
144+
* Plugin Name: Foo Plugin
145+
* Text Domain: foo-plugin
146+
*/
147+
148+
/* translators: Comment 1 */
149+
__( 'Hello World', 'foo-plugin' );
150+
151+
/* translators: Comment 2 */
152+
__( 'Hello World', 'foo-plugin' );
153+
"""
154+
155+
When I try `wp i18n audit foo-plugin`
156+
Then STDERR should contain:
157+
"""
158+
Warning: foo-plugin.php:
159+
"""
160+
And STDERR should contain:
161+
"""
162+
different translator comments
163+
"""
164+
165+
Scenario: Detects missing singular placeholder
166+
Given an empty foo-plugin directory
167+
And a foo-plugin/foo-plugin.php file:
168+
"""
169+
<?php
170+
/**
171+
* Plugin Name: Foo Plugin
172+
* Text Domain: foo-plugin
173+
*/
174+
175+
_n( 'One comment', '%s Comments', $count, 'foo-plugin' );
176+
"""
177+
178+
When I try `wp i18n audit foo-plugin`
179+
Then STDERR should contain:
180+
"""
181+
Warning: foo-plugin.php:7: Missing singular placeholder, needed for some languages.
182+
"""
183+
184+
Scenario: Detects mismatched placeholders in plural strings
185+
Given an empty foo-plugin directory
186+
And a foo-plugin/foo-plugin.php file:
187+
"""
188+
<?php
189+
/**
190+
* Plugin Name: Foo Plugin
191+
* Text Domain: foo-plugin
192+
*/
193+
194+
_n( '%s Comment', '%d Comments', $count, 'foo-plugin' );
195+
"""
196+
197+
When I try `wp i18n audit foo-plugin`
198+
Then STDERR should contain:
199+
"""
200+
Warning: foo-plugin.php:7: Mismatched placeholders for singular and plural string.
201+
"""
202+
203+
Scenario: Respects --ignore-domain flag
204+
Given an empty foo-plugin directory
205+
And a foo-plugin/foo-plugin.php file:
206+
"""
207+
<?php
208+
/**
209+
* Plugin Name: Foo Plugin
210+
* Text Domain: foo-plugin
211+
*/
212+
213+
__( 'Hello %s', 'different-domain' );
214+
"""
215+
216+
When I try `wp i18n audit foo-plugin --ignore-domain`
217+
Then STDERR should contain:
218+
"""
219+
Warning: foo-plugin.php:7: The string "Hello %s" contains placeholders but has no "translators:" comment to clarify their meaning.
220+
"""
221+
222+
Scenario: Respects --skip-php flag
223+
Given an empty foo-plugin directory
224+
And a foo-plugin/foo-plugin.php file:
225+
"""
226+
<?php
227+
/**
228+
* Plugin Name: Foo Plugin
229+
* Text Domain: foo-plugin
230+
*/
231+
232+
__( 'Hello %s', 'foo-plugin' );
233+
"""
234+
235+
When I run `wp i18n audit foo-plugin --skip-php`
236+
Then STDOUT should contain:
237+
"""
238+
Success: No issues found.
239+
"""
240+
241+
Scenario: Shows file before warning message in plaintext format
242+
Given an empty foo-plugin directory
243+
And a foo-plugin/foo-plugin.php file:
244+
"""
245+
<?php
246+
/**
247+
* Plugin Name: Foo Plugin
248+
* Text Domain: foo-plugin
249+
*/
250+
251+
__( 'Hello %s', 'foo-plugin' );
252+
"""
253+
254+
When I try `wp i18n audit foo-plugin --format=plaintext`
255+
Then STDERR should contain:
256+
"""
257+
Warning: foo-plugin.php:7: The string "Hello %s" contains placeholders but has no "translators:" comment to clarify their meaning.
258+
"""

i18n-command.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,15 @@
3333
WP_CLI::add_command( 'i18n make-php', '\WP_CLI\I18n\MakePhpCommand' );
3434

3535
WP_CLI::add_command( 'i18n update-po', '\WP_CLI\I18n\UpdatePoCommand' );
36+
37+
WP_CLI::add_command(
38+
'i18n audit',
39+
'\WP_CLI\I18n\AuditCommand',
40+
array(
41+
'before_invoke' => static function () {
42+
if ( ! function_exists( 'mb_ereg' ) ) {
43+
WP_CLI::error( 'The mbstring extension is required for string extraction to work reliably.' );
44+
}
45+
},
46+
)
47+
);

0 commit comments

Comments
 (0)