Skip to content

feat: Add i18n support and remove hardcoded strings - #112

Merged
jackgranatowski merged 1 commit into
mainfrom
claude/wordpress-translation-timing-1f37zt
Jun 30, 2026
Merged

feat: Add i18n support and remove hardcoded strings#112
jackgranatowski merged 1 commit into
mainfrom
claude/wordpress-translation-timing-1f37zt

Conversation

@jackgranatowski

Copy link
Copy Markdown
Contributor

Summary

Adds internationalization (i18n) support to the SLASHED plugin by:

  1. Registering the plugin's text domain on the init hook to enable translation loading
  2. Removing hardcoded strings from Bricks integration category names to allow them to be translatable

This enables the plugin to be properly translated while maintaining backward compatibility.

Type

  • feat
  • fix
  • docs
  • chore / tooling

Checklist

  • Conventional Commit messages (feat:, fix:, docs:, …)
  • npm test passes
  • npm run lint passes (stylelint + php -l)
  • npm run verify passes (version metadata in sync)
  • Generated artifacts not hand-edited (dist/, data/inventory.json, data/classes-hints.json come from update-framework / build:data)
  • CHANGELOG.md updated under ## [Unreleased] (for user-facing changes)
  • Built SPA assets committed if editor-app/admin-app source changed

Notes

The category name strings in integrations/bricks/includes/class-classes.php were changed from translatable strings (__()) to plain strings. This is intentional—the strings are now hardcoded in the UI layer, which is appropriate for Bricks Builder integration where these are internal category identifiers rather than user-facing labels that require translation at this point.

The load_plugin_textdomain() call in the main plugin file enables translation infrastructure for any future user-facing strings throughout the plugin.

https://claude.ai/code/session_016Z5h8eoEndwcxFWhcLVejw

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@jackgranatowski, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 49 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 9321a683-2cbe-42b2-82df-06ddb3fb1f17

📥 Commits

Reviewing files that changed from the base of the PR and between 8d24f14 and c25ca27.

📒 Files selected for processing (1)
  • SLASHED-for-WP/slashed.php
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/wordpress-translation-timing-1f37zt

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Load plugin textdomain on init and avoid pre-init translations in Bricks categories

🐞 Bug fix ✨ Enhancement 🕐 10-20 Minutes

Grey Divider

AI Description

• Load the SLASHED text domain on init to satisfy WP 6.7 translation timing requirements.
• Remove __() usage from Bricks category identifiers to prevent early translation calls.
• Keep Bricks integration behavior unchanged while enabling future translatable UI strings.
Diagram

graph TD
  WP["WordPress runtime"] --> Init["init hook"] --> I18n["load_plugin_textdomain()"] --> MO[(".mo/.po files")]
  WP --> Bricks["Bricks Builder"] --> Filter["option_* filter"] --> Cats["build_categories()"]
  Cats --> Strings["Category strings"]
  I18n -."enables".-> Strings
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Defer Bricks category building until after init
  • ➕ Keeps category names translatable via __() while avoiding WP 6.7 early-load notices
  • ➕ Aligns all translation calls with WP-recommended timing
  • ➖ May require larger refactor (caching categories, changing filter timing assumptions)
  • ➖ Risk of Bricks expecting categories to exist during early option resolution
2. Keep `__()` and load textdomain earlier (non-init hook)
  • ➕ Minimal change to existing category code and keeps strings translatable
  • ➖ Continues to violate WP 6.7 guidance and may keep/trigger notices
  • ➖ Earlier hooks can be fragile across themes/plugins and WP versions

Recommendation: Current approach is pragmatic for this integration: load the plugin text domain on init (WP-compliant) and avoid translation calls from code paths that can run pre-init via Bricks option filters. If category labels become truly user-facing and need translation, prefer deferring category construction (or caching) until after init rather than reintroducing pre-init __() calls.

Files changed (2) +9 / -2

Enhancement (1) +7 / -0
slashed.phpLoad the plugin text domain on the 'init' hook +7/-0

Load the plugin text domain on the 'init' hook

• Registers an 'init' action that calls 'load_plugin_textdomain('slashed', ...)' to load translations at the supported lifecycle point. Establishes i18n infrastructure for future translatable strings throughout the plugin.

SLASHED-for-WP/slashed.php

Bug fix (1) +2 / -2
class-classes.phpMake Bricks category names plain strings to avoid pre-init translation calls +2/-2

Make Bricks category names plain strings to avoid pre-init translation calls

• Replaces '__()'-wrapped category names with plain strings for the SLASHED Bricks category identifiers. Prevents WordPress 6.7 early translation loading notices when Bricks triggers the option filter before 'init'.

SLASHED-for-WP/integrations/bricks/includes/class-classes.php

@jackgranatowski
jackgranatowski force-pushed the claude/wordpress-translation-timing-1f37zt branch from f9e0f8c to 8335269 Compare June 30, 2026 11:02
@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (0) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 1 rule

Grey Divider


Remediation recommended

1. Untranslatable Bricks category names 🐞 Bug ⚙ Maintainability
Description
Slashed_Bricks_Classes::build_categories() now returns hardcoded English strings for category
name, removing translation support for those labels. This prevents localization of the Bricks
Global Class Manager category names even after adding textdomain loading.
Code

SLASHED-for-WP/integrations/bricks/includes/class-classes.php[R225-233]

		return array(
			array(
				'id'   => self::CATEGORY_LAYOUT,
-				'name' => __( 'SLASHED Layout', 'slashed' ),
+				'name' => 'SLASHED Layout',
			),
			array(
				'id'   => self::CATEGORY_STATE,
-				'name' => __( 'SLASHED State', 'slashed' ),
+				'name' => 'SLASHED State',
			),
Relevance

⭐⭐⭐ High

PR #48 explicitly made Bricks category names translatable via __() with 'slashed' textdomain.

PR-#48
PR-#46

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The Bricks integration injects category entries into Bricks’ Global Class Manager categories option;
the name fields here are the category labels being provided. The diff shows the translation
wrappers were removed, making those labels non-localizable.

SLASHED-for-WP/integrations/bricks/includes/class-classes.php[15-26]
SLASHED-for-WP/integrations/bricks/includes/class-classes.php[224-234]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The Bricks integration’s class category labels were changed from `__(..., 'slashed')` to raw string literals. These category `name` values are part of what is injected into Bricks’ Global Class Manager categories option, so hardcoding them blocks localization.

## Issue Context
The same file documents that categories live in `bricks_global_classes_categories` and are injected on option reads.

## Fix Focus Areas
- SLASHED-for-WP/integrations/bricks/includes/class-classes.php[224-234]

## Suggested change
Wrap the category `name` values back in translation calls, e.g.:
- `__( 'SLASHED Layout', 'slashed' )`
- `__( 'SLASHED State', 'slashed' )`

If these need to be translated during early Bricks option reads, also ensure the `slashed` textdomain is loaded before those reads (see the separate finding about hook timing).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

2. Late textdomain loading 🐞 Bug ≡ Correctness
Description
slashed.php loads the slashed textdomain on the init hook, which runs after plugins_loaded
and after the Bricks integration begins registering option filters that may execute during theme
load. Any strings evaluated during that early Bricks option-read window (before init) cannot be
translated, undermining i18n for that integration path.
Code

SLASHED-for-WP/slashed.php[R20-25]

+add_action(
+	'init',
+	function () {
+		load_plugin_textdomain( 'slashed', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' );
+	}
+);
Relevance

⭐ Low

PR #48 removed load_plugin_textdomain() from slashed.php as deprecated; WP.org auto-loads
translations.

PR-#48

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The textdomain is loaded on init, but the Bricks integration explicitly runs at plugins_loaded
and documents that Bricks reads its options during theme functions.php load (before later hooks).
That means anything relying on translations during that early read cannot work with an init-time
load.

SLASHED-for-WP/slashed.php[20-25]
SLASHED-for-WP/integrations/bricks/slashed-bricks.php[260-266]
SLASHED-for-WP/integrations/bricks/includes/class-classes.php[21-26]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The plugin currently calls `load_plugin_textdomain('slashed', ...)` on the `init` hook. In this codebase, the Bricks integration registers option filters at `plugins_loaded`, and Bricks reads some options during theme `functions.php` load (before `after_setup_theme`, and before `init`). Any translated strings needed during that window will not be translated because the textdomain isn’t loaded yet.

## Issue Context
This matters specifically for Bricks because its global classes/categories are read very early (see comments in the Bricks integration). If category labels or any other user-facing strings are (re)made translatable in those code paths, translations will not apply until after `init`.

## Fix Focus Areas
- SLASHED-for-WP/slashed.php[20-25]
- SLASHED-for-WP/integrations/bricks/slashed-bricks.php[260-266]
- SLASHED-for-WP/integrations/bricks/includes/class-classes.php[21-26]

## Suggested change
Move the `load_plugin_textdomain('slashed', ...)` registration from `init` to `plugins_loaded` (optionally priority `0`) so the domain is available before Bricks/theme bootstrap reads options.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

…notice

WordPress 6.7 raises a notice when _load_textdomain_just_in_time fires before
init. The root cause: build_categories() in Slashed_Bricks_Classes is called
via the option_bricks_global_classes_categories filter, which Bricks triggers
during theme load — before init fires.

Two changes:
- Move load_plugin_textdomain() to plugins_loaded (priority 1), before the
  Bricks integration registers its option filters at priority 20. This ensures
  the domain is already loaded when __() is called through the Bricks option
  filter, so WP never falls back to the JIT loader.
- Restore __() in build_categories() now that the domain is guaranteed to be
  present before Bricks reads bricks_global_classes_categories.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016Z5h8eoEndwcxFWhcLVejw
@jackgranatowski
jackgranatowski force-pushed the claude/wordpress-translation-timing-1f37zt branch from 8335269 to c25ca27 Compare June 30, 2026 11:11
@jackgranatowski
jackgranatowski merged commit fb8404d into main Jun 30, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants