Skip to content

Drop escaping of translation strings (esc_html__/esc_html_e/etc.) — trust w.org translations #20

@dd32

Description

@dd32

Policy

WordPress.org-supplied translations are trusted. The translation string itself does not need to be escaped — only data interpolated into it.

What this means in practice

  • Replace esc_html__() / esc_html_e() / esc_attr__() / esc_attr_e() with __() / _e() / _x() / _ex() for the static, translator-provided portion of strings.
  • Continue to escape interpolated values: variables, IDs, URLs, user content, etc. should still go through esc_html(), esc_attr(), esc_url(), esc_js(), etc. before being placed into output.
  • In printf() / sprintf() patterns, escape the substituted values, not the format string.

Before

wp_die(
    esc_html__( 'This invite link is invalid or has been revoked.', 'multi-author-posts' ),
    esc_html__( 'Invalid Invite', 'multi-author-posts' ),
    array( 'response' => 403 )
);

After

wp_die(
    __( 'This invite link is invalid or has been revoked.', 'multi-author-posts' ),
    __( 'Invalid Invite', 'multi-author-posts' ),
    array( 'response' => 403 )
);

Rationale

  • WordPress.org Polyglots translations go through review and are trusted not to inject markup.
  • Double-escaping the static string adds noise and can corrupt characters (entities, smart quotes) inside the translated copy.
  • The actual XSS risk is in interpolated data, which we keep escaping.

Scope

includes/class-map-invite.php has the two esc_html__() calls inside the wp_die() invalid-invite branch. No other files use the escaping wrappers today.

Plugin Check currently flags the un-escaped form as WordPress.Security.EscapeOutput.UnsafePrintingFunction / OutputNotEscaped; those codes are downgraded to warnings in .github/workflows/plugin-check.yml per this policy.

Out of scope

  • Strings concatenated with untrusted markup that must be escaped as a whole — keep wrapping the whole composite through wp_kses_* if needed.
  • HTML attribute contexts where the translation is the entire attribute value — case-by-case; escaping the variable being inserted is usually enough.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions