Make WordPress Core

Opened 5 weeks ago

Last modified 2 weeks ago

#65797 new defect (bug)

Pages printed by wp_die() miss the lang attribute and a main heading

Reported by: afercia Owned by:
Priority: normal Milestone: 7.2
Component: Administration Version:
Severity: normal Keywords: has-patch has-unit-tests
Cc: Focuses: accessibility

Description

Noticed while inspecting the You are running WordPress without JavaScript and CSS files... page, which appears while developing and CSS/JS aren't build yet. See screenshot.

Applies to other pages printed via wp_die() as well.

The default handler for wp_die() is _default_wp_die_handler() which prints a HTML document where the <html> element misses the lang attribute. It only prints the dir attribute, when available. However some errors happen early in the bootstrap process and I don't think the language attributes can be retrieved at that point. I guess the lang attribute should be conditionally hardcoded to lang="en-US".

There's no main H1 heading on the page.

All pages should always have a main H1 heading to inform all users what the page is about. This isn't just for accessibility, it's best usability practice anyways.

I'd like to suggest to explore adding a heading arg to the args of wp_die() and _default_wp_die_handler(). When set, the heading should be printed out before the message.

For the specific You are running WordPress without JavaScript and CSS files... error page, in src/index.php, there is a call to:

wp_die( $die, __( 'WordPress &rsaquo; Error' ) );

where the variable $die contains the error message and one more string is passed that is used for the document <title> tag, rendered as WordPress › Error.

The third optional parameter is $args, where we could pass a new heading arg.

Attachments (2)

Screenshot 2026-08-02 at 15.15.29.png (189.9 KB ) - added by afercia 5 weeks ago.
Screenshot 2026-08-03 at 16.52.00.png (74.1 KB ) - added by afercia 5 weeks ago.

Download all attachments as: .zip

Change History (7)

This ticket was mentioned in PR #12817 on WordPress/wordpress-develop by @khokansardar.


5 weeks ago
#1

  • Keywords has-patch has-unit-tests added

Pages printed by the default wp_die() handler are missing a lang attribute when the error occurs before the site's language is available, and have no main heading describing what the page is about.

What the problem was:

  • _default_wp_die_handler() only reaches get_language_attributes() when language_attributes() and is_rtl() are defined. Errors raised early in the bootstrap process (for example the "You are running WordPress without JavaScript and CSS files" page) run before general-template.php is loaded, so the document renders as <html dir='ltr'> with no lang attribute.
  • The rendered page has no H1, so neither assistive technology users nor sighted users are told what the page is about.

What the fix does:

  • Falls back to lang="en-US" when the site's language cannot be determined.
  • Adds a heading argument to wp_die() and _default_wp_die_handler(), printed as an <h1> above the message when set.
  • Passes the existing "WordPress › Error" string as the heading in src/index.php.

Approach and why:

  • The lang fallback applies only when language_attributes()/is_rtl() are undefined. When WordPress is loaded the output is byte-for-byte unchanged, so a site in a non-English locale is never mislabelled as en-US.
  • The heading is opt-in. Defaulting it to $title would add a second H1 to the many wp_die() calls whose message already contains its own heading.
  • No new translatable string is introduced; the page reuses the string already passed as the document title.
  • The heading is not escaped, matching how $message and $title are handled in the same function. Escaping helpers are not guaranteed to exist on the bootstrap paths this handler serves.

Trac ticket: https://core.trac.wordpress.org/ticket/65797

## Use of AI Tools
AI assistance: Yes
Tool(s): Claude Code
Model(s): Claude Opus 5
Used for: Ticket analysis, tests and generate PR details. All changes were reviewed and validated by me

This ticket was mentioned in Slack in #accessibility by joedolson. View the logs.


5 weeks ago

#3 @joedolson
5 weeks ago

  • Milestone Awaiting Review7.2

@audrasjb commented on PR #12817:


2 weeks ago
#4

Instead of falling back to lang="en-US" when the site's language cannot be determined, I think this ticket is a great opportunity to see how we could make the site language available earlier in WP's boostrap process. Because declaring English for a fully translated page is an issue in itself.

@khokansardar commented on PR #12817:


2 weeks ago
#5

Instead of falling back to lang="en-US" when the site's language cannot be determined, I think this ticket is a great opportunity to see how we could make the site language available earlier in WP's boostrap process. Because declaring English for a fully translated page is an issue in itself.

Good call — the premise behind the en-US fallback doesn't hold.

Every early path into _default_wp_die_handler() calls wp_load_translations_early() first, so the default textdomain is already loaded and __( 'html_lang_attribute' ) — the same string get_bloginfo( 'language' ) reads — gives the right tag with no DB access. What's missing that early is get_language_attributes() (general-template.php loads at wp-settings.php:216) and get_locale()/determine_locale() (both hit get_option() → fatal wp_cache_get()). That's what the old condition was really detecting.

Clearest case is wp_maintenance() — a translated page shown to every visitor during updates:

de_DE  <html dir='ltr' lang='de-DE'>  Jetzt WordPress aktualisieren
ja_JP  <html dir='ltr' lang='ja'>     今すぐ WordPress を更新
none   <html dir='ltr' lang='en-US'>  Update WordPress now

en-US now only shows up when no translations are loaded, i.e. when the page really is English. Updated in the PR.

Note: See TracTickets for help on using tickets.