Problem/Motivation

Now, when we try to pass a constant to the t() function, the code sniffer shows the warning:
Only string literals should be passed to t() where possible

But, as for me, using a constant is also ok, it's the same as a string literal.
Using constants is very convenient when we have several places to insert the same text: with constants, we can just put it once in a constant, and reuse it in all needed places, especially in different classes.

So, maybe we can find some way to allow this?

The current workaround to dismiss the warning is to use // phpcs:ignore or @codingStandardsIgnoreStart / @codingStandardsIgnoreEnd tricks around the t() call.

Steps to reproduce

1. Get the code like this:

define("ALIAS_RULES_DESCRIPTION", "Alias should start with /");
$description = $this->t(ALIAS_RULES_DESCRIPTION);

2. Run the code sniffer and see the warning.

Proposed resolution

Try to detect if there is a constant passed, and if so - dismiss the warning.

Remaining tasks

User interface changes

API changes

Data model changes

Issue fork coder-3326197

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

Murz created an issue. See original summary.

murz’s picture

Currently we have this code, that do this check, in the file coder_sniffer/Drupal/Sniffs/Semantics/FunctionTSniff.php:

        if ($tokens[$argument['start']]['code'] !== T_CONSTANT_ENCAPSED_STRING) {
            // Not a translatable string literal.
            $warning = 'Only string literals should be passed to t() where possible';
            $phpcsFile->addWarning($warning, $argument['start'], 'NotLiteralString');
            return;
        }

Any ideas on how we can add here the detection of constants?

murz’s picture

Status: Active » Needs review

I've found a way via allowing also T_STRING argument type, via this change:

        if (
          $tokens[$argument['start']]['code'] !== T_CONSTANT_ENCAPSED_STRING
          && $tokens[$argument['start']]['code'] !== T_STRING
        ) {

It works well, and if I'm trying to pass a string with a inline variable like "foo $bar" - the argument type is T_DOUBLE_QUOTED_STRING, so it still works well for such case.

Could anyone check if this is a suitable fix, or it can also allow some "bad things" to pass?

klausi’s picture

Status: Needs review » Needs work
Issue tags: +Needs tests

I think we can do this - although it is still not ideal to have translatable strings in constants in Drupal. Then you have the same problem again that translation extraction tools cannot find it in source code. I'm not sure if those extraction tools are still widely used, so I'm fine with relaxing this check.

Can you file a pull request against https://github.com/pfrenssen/coder and add a test case?

murz’s picture

Here is my PR in GitHub tracker: https://github.com/pfrenssen/coder/pull/182

murz’s picture

Status: Needs work » Needs review

I've fixed all remarks by comments in the PR https://github.com/pfrenssen/coder/pull/182 - please review.

murz’s picture

  • Murz authored 1c4a0921 on 8.3.x
    feat(FunctionT): Allow passing constants to t() (#3326197 by Murz)
    
    

  • klausi committed 88055e40 on 8.3.x
    style(FunctionT): Fix coding standard spacing in test (#3326197)
    
klausi’s picture

Status: Needs review » Fixed
Issue tags: -Needs Review Queue Initiative

Merged, thanks!

Also pushed a small follow-up to fix spacing in the test.

penyaskito’s picture

The main reason coder complained about constansts in t() was because potx cannot parse those.

potx is still relied by lots of people, and has no other solution solving that problem space in Drupal AFAIK.

localize.drupal.org relies heavily on potx for core and contrib modules translation, so I'd expect to have this rule still in coder.

If it's really gonna be removed, because of that impact, I'd hope it wouldn't happen in a minor release, and that's there some communication around it aside of release notes.

klausi’s picture

Status: Fixed » Active

Thanks for checking in here! Hm, there is certainly a tradeoff here. In private projects it is fine if you use constants in t() when you don't use the potx extraction tools. When you use the same string multiple times a constant is also super helpful so that you don't have to maintain the same string in multiple places.

But maybe then private projects should simply disable this sniff if they want to use constants. I'm open to revert this change. Do we see public contrib projects often use constants where Coder should throw an error? Let me know if you have some examples!

Release policy: relaxing or hardening a sniff is perfectly fine in a minor/patch release of Coder (we are not fully following Semver regarding minor/patch releases yet for historic reasons). I would never make a major release because of tweaking a sniff.

Leaving this as active for now for more feedback.

penyaskito’s picture

Sadly we can't (or I don't know how) search on drupalci reports to find how often this happens.

But if I search "Only string literals should be passed to t() where possible" on d.o issues I find several issues yet from 2022 or 2023.
Most of them are from patches/code from new contributors, but also found some instances from experienced contributors (not linking because there's no need of public pointing anyone).

But I agree with reverting this. Specially since it's a warning and afaik we can ignore it with // @ignore if you really need it.

murz’s picture

Yeah, the potx issue seems an important reason to revert this, didn't know about this. Thanks for the investigation!

But maybe there are chances to improve/fix this on potx side? If no - seems yes, we should revert this.

But for me - using constants for repeatable strings is a very convenient tool, because in many contrib modules, and even in Core I see a lot of repeatable strings, and sometimes they should be the same but differ just because of paraphrasing or some punctuation, as result we have several almost equal strings in translation queue instead of one. So, the constants approach for translation strings should minimize such cases.

penyaskito’s picture

@Murz: If you need

$var = 'string';
// Do whatever with t($var);

then better use

$var = t('string');
// Do whatever with $var;

This limits you to use constants in classes which I'm guessing that's what you are looking for.

Potx situation won't change. For being able to parse strings in variables on t(), you'd need to execute the code (as this strings might have operations, concatenations, etc on them) and aside of the complexity of that, that would mean a security risk on localize.drupal.org.

murz’s picture

@penyaskito, I need something like this:

class ModuleController {
  public const PATH_VALIDATION_RULES = "Only numbers and characters are allowed in the string";
}

class ModuleSettingsForm {
...
   $form_state->setErrorByName('field', $this->t(ModuleController::PATH_VALIDATION_RULES));
...
}

class EntityEditForm {
...
   $form_state->setErrorByName('field', $this->t(ModuleController::PATH_VALIDATION_RULES));
...
}

class ModuleViewHelpPage {
...
  $output .= $this->t("Rules for the path parameter: @rules", [
     '@rules' => $this->t(ModuleController::PATH_VALIDATION_RULES),
  ]);
...
}

So, I want to have the message text only in one single place, not the 3+ duplicates of it, where it can be typed differently like this:

Only numbers and characters are allowed in the string.
Only numbers and characters are allowed in the string
Only characters and numbers are allowed in the string
Only numbers and characters symbols are allowed in the string

in result, giving 4 almost the same strings in the translation queue.

So, the approach of defining the message text in a const variable to reuse in many places looks very convenient.

And replacing it with something like this:

class ModuleController {
  function getPathValidationRulesMessage()
    return $this->t("Only numbers and characters are allowed in the string");
  }
}

looks much worse! At least, because it will require always passing the ModuleController as a dependency to the each class where we need this string.

murz’s picture

And about potx - maybe we can extend it via catching constant values for translating, that are described via specific annotation tags like this:

class SomeClass {
  /** 
   * @const Path validation rules description
   * @translatable
   */    
  public const PATH_VALIDATION_RULES = "Only numbers and characters are allowed in the string";
}

We already have something similar in plugin annotations:

/**
 * Provides an action that can save any entity.
 *
 * @Action(
 *   id = "entity:save_action",
 *   action_label = @Translation("Save"),
 *   deriver = "Drupal\Core\Action\Plugin\Action\Derivative\EntityChangedActionDeriver",
 * )
 */

So, this approach will not require executing the code to get strings for translation, just a simple parsing should be enough.

What do you think about this approach?

penyaskito’s picture

You can always use the "D7 features" approach:

class ModuleController {
  public const PATH_VALIDATION_RULES = "Only numbers and characters are allowed in the string";

  public __ctor() {
     t('Only numbers and characters are allowed in the string');
  }

}

class ModuleSettingsForm {

...
...
   // @ignore rule
   $form_state->setErrorByName('field', $this->t(ModuleController::PATH_VALIDATION_RULES));
...
}

You have an unused t() somewhere to ensure potx finds it, and then ignore the rest.

For new formats and parsers, better to discuss in potx queue itself.

klausi’s picture

OK, since Murz also agrees that we can revert this I think we can go ahead with the revert before the next release. As the potx parsing is much harder to fix people should make an exception in their phpcs.xml config if they would like to use constants as Murz proposed.

  • klausi authored a0b76c6c on 8.3.x
    Revert "feat(FunctionT): Allow passing constants to t() (#3326197 by...
klausi’s picture

Status: Active » Fixed

Revert pushed! Thanks both of you for your thoughtful comments.

klausi’s picture

Forgot credits for penyaskito, added now.

klausi’s picture

Title: Allow passing constants to t() translate function to prevent 'Only string literals should be passed to t() where possible' CS warning » REVERTED: Allow passing constants to t() translate function to prevent 'Only string literals should be passed to t() where possible' CS warning

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

murz’s picture

To move on I created a feature request in the potx module: #3392795: Extract translatable strings from constants with @translation tag