-
-
Notifications
You must be signed in to change notification settings - Fork 493
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Ensure translatable strings, and string domains are not dynamic #136
Conversation
@shadyvb May I ask why you would disallow dynamic string domains ? I realize that the .pot file can be generated in the WP repo if you use absolute string domains, but there are lots of other tools available to generate .pot files which work without issue with dynamic string domains and the actual translation of strings works fine with dynamic string domains. Also from a programming point of view it's actually good practice as it makes the code more flexible and easier to maintain (no duplicate information). May be a warning would be more appropriate ? Preferably a warning which can be turned off ? |
7 => 1, | ||
9 => 1, | ||
); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should line 5 be included too ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, because __( "hell\$varo", 'domain' );
does not contain a variable. It is escaped.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment was incorrectly saying it was bad, however. It is actually good.
|
||
__( $var, 'domain' ); // Bad, shouldn't use variables | ||
|
||
__( 'string', SOMETHING ); // Bad, shouldn't use CONSTANTS |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Few more unit tests:
__( 'string' . $var, 'domain' ); // Bad, shouldn't use variable for string
__( $var . 'string', 'domain' ); // Bad, shouldn't use variable for string
__( SOMETHING, 'domain' ); // Bad, shouldn't use constant for string
__( 'string' . SOMETHING, 'domain' ); // Bad, shouldn't use constant for string
__( SOMETHING . 'string', 'domain' ); // Bad, shouldn't use variable for string
__( 'string', $domain ); // Bad, shouldn't use variable for domain
__( 'string', 'my' . $domain ); // Bad, shouldn't use variable for domain
__( 'string', $domain . 'domain' ); // Bad, shouldn't use variable for domain
__( 'string', 'domain' ); // Good
_x( 'string', 'context', 'domain' ); // Good
_x( 'string', $var, 'domain' ); // Bad, shouldn't use variable for context
_x( 'string', 'context' . $var, 'domain' ); // Bad, shouldn't use variable for context
_x( 'string', $var . 'context', 'domain' ); // Bad, shouldn't use variable for context
_x( 'string', SOMETHING, 'domain' ); // Bad, shouldn't use constant for context
_x( 'string', SOMETHING . 'context', 'domain' ); // Bad, shouldn't use constant for context
_x( 'string', 'context' . SOMETHING, 'domain' ); // Bad, shouldn't use constant for context
@jrfnl: See https://markjaquith.wordpress.com/2011/10/06/translating-wordpress-plugins-and-themes-dont-get-clever/ |
@westonruter See my original comment in which I already respond to that article. Mark presumes in his article that people don't know how to generate .pot files outside of using the tooling in the WP repo admin. [Edit] Oh and just be sure that I'm making myself clear - I'm not talking about the string to be translated, only about the text-domain. |
At this moment in time, even the WP i18n tools don't actually limit the strings collected by the text domain i.e. the final argument (string, variable, constant, something else) is not actually read. This is why it also works for core i18n strings which obviously don't have that arg. The precaution here is trying to make future developments which may consider the text domain, easier. |
Thanks for that addition @GaryJones. Sounds to me like all the more reason to differentiate between the messages about the translatable string and messages about the domain, to make messages about the domain a warning and to make sure they have a separate error code, so people can turn them off without turning messages about the translatable strings off. |
if ( $tokens[$i]['code'] === T_DOUBLE_QUOTED_STRING ) { | ||
$string = $tokens[$i]['content']; | ||
if ( preg_match( '#\$#', $string ) > 0 ) { | ||
$phpcsFile->addError( 'Translatable strings should not contain variables, found ' . $tokens[$i]['content'], $i ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Per @jrfnl's note on this PR:
Sounds to me like all the more reason to differentiate between the messages about the translatable string and messages about the domain, to make messages about the domain a warning and to make sure they have a separate error code, so people can turn them off without turning messages about the translatable strings off.
This can be change to a warning, and the third argument can he supplied to the method to provide the warning with an identifier which can be used to target the warning in a custom ruleset.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As a side question, shouldn't all our errors and warnings have identifiers so folks can target them as they desire?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As a side question, shouldn't all our errors and warnings have identifiers so folks can target them as they desire?
@GaryJones yes, absolutely.
I checked with @ocean90, and he says if it is hosted on WordPress.org, the string literal text domain must match the theme/plugin slug. So…
|
|
||
public $i18n_functions = array( | ||
'translate', | ||
'translate_with_gettext_context', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
translate() and translate_with_gettext_context() are low-API functions, they shouldn't be used by plugins IMO.
Since #50 is about "proper usage of translation functions", should this include a warning when |
|
Just an idea: Looking at the added tests - what about an optional extra check to verify that a |
@jrfnl I like that. Actually, I think we can accomplish this via a |
@@ -33,6 +33,7 @@ | |||
<rule ref="WordPress.WP.PreparedSQL"/> | |||
<rule ref="WordPress.Variables.GlobalVariables"/> | |||
<rule ref="WordPress.PHP.StrictComparisons" /> | |||
<rule ref="WordPress.WP.I18n" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this go into WordPress-Extra
or WordPress-Core
? I'm just concerned about use with actual core, and how a lot of warnings will start appearing since no text domain is supplied.
Ready for review. |
* @param array $tokens | ||
* @return bool | ||
*/ | ||
protected function check_literal_string_text_tokens( $phpcs_file, $arg_name = '$text', $stack_ptr, $tokens = array() ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's an unfortunate amount of duplication between check_literal_string_text_tokens
, check_literal_string_context_tokens
, and check_string_domain_tokens
. However, an immediate refactoring wasn't apparent which would allow custom messages as well as error codes for each argument type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's an unfortunate amount of duplication between check_literal_string_text_tokens, check_literal_string_context_tokens, and check_string_domain_tokens. However, an immediate refactoring wasn't apparent which would allow custom messages as well as error codes for each argument type.
Aren't the messages and error codes identical other than the arg names though? ("translatable text", "context", "text domain") I think it could be refactored to a single method that would just accept those strings and build the error messages and codes from them?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, some are errors and some are warnings.
It could be refactored to try to abstract these out more by passing in params to common methods if you think it is worthwhile.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[ci skip]
@JDGrimes would you be able to reviewmerge? |
*/ | ||
class WordPress_Sniffs_WP_I18nSniff extends WordPress_Sniff { | ||
|
||
public $i18n_functions = array( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't it be better to flip this array and have the function names as keys (this is standard in most other sniffs)? Then you can use isset()
instead of in_array()
. And perhaps more importantly, you could let the nature of each function be specified by the values, rather than hardcoding this below. So you could have:
array(
'__' => 'simple',
'_x' => 'context',
'_n' => 'plural',
'_nx' => 'plural_context',
)
ect.
Because right now if someone ever wanted to sub-class this and add their own custom translation functions (bad idea, I know), they'd have to duplicate all of the code rather than just add them to this list, because how each function is handled has to be hard-coded below.
Anyway, I just prefer isset()
to in_array()
. 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great points! See 9ebd183.
The text domain can be a variable even for the WordPress makepot or on WordPress.org. The only condition for wordpress.org is that the text domain must match the theme/plugin slug. I have tested it. Anyone who has says differently has not tested it. What is best practice is another discussion. |
@grappler Nobody is saying that it doesn't work… See also #136 (comment). Not sure why you think that it's just a "best practice" but it's definitely a WordPress coding standard which is enforced by several handbook pages, for example https://developer.wordpress.org/plugins/internationalization/how-to-internationalize-your-plugin/. |
Quoting from the post https://markjaquith.wordpress.com/2011/10/06/translating-wordpress-plugins-and-themes-dont-get-clever/ Yes, I can accept it is WordPress coding standard. 😄 |
Fixes #50.