Skip to content

perf(wpml): opt-in guard for ACFML's front-end ACF field-definition translation #131

Description

@parisek

Problem

ACFML translates ACF field definitions — labels, instructions, placeholders,
prepend/append, choices, message — on every request that loads a field group.
ACFML\Strings\FieldHooks implements IWPML_Frontend_Action, so it registers on
the front end with no is_admin() guard, and hooks acf/load_field. Every field
then reaches Config::getForField():

Fns::filter( Relation::propEq( 'namespace', 'field' ), self::DATA )

An unmemoised linear scan over a static array, run through WPML's functional
library, allocating a closure per element. On a plain page view none of those
strings is rendered, so the whole walk is wasted.

Measured (sloneek, 117 field groups / 972 top-level fields)

time
baseline 1.09–1.13 s
group-level translation off 1.04–1.14 s (no change)
field-level translation off 0.29 s

A PHP-FPM slowlog profile over 3973 slow requests put 71 % of deepest-frame
samples inside wpml/fp + wpml/collect.

Staging, same box, alternating the file to hold load constant:

round without with
1 9.70 s 4.17 s
2 8.97 s 4.28 s
3 10.58 s 3.99 s

Rendered HTML is byte-identical once per-render uniqueId() values are
normalised.

Why this is not #107

#107 was closed unmerged, on
the reasoning that a shared workaround would outlive the bug it worked around.
That reasoning was right there and does not carry here:

#107 (acf/load_reference leak) this
who pays one caller outside WPML's own plugins every front-end render
fixed upstream yes, acfml 3.0-b.1 no
workaround lifetime would outlive the bug bug has no end date

Verified rather than assumed: on proficiohub (acfml 3.0-b.1, sitepress
5.0.0-b.1, wpml-string-translation 5.0.0-beta.1), Strings/FieldHooks.php,
Config.php, Translator.php, Traversable/Entity.php,
Traversable/FieldGroup.php, Transformer/Translate.php and Package.php are
byte-identical to 2.2.4, and the bundled wpml/fp library has identical MD5s.
The same comparison does pick up 3.0-b.1's one change — the remove_filter
hook-name fix from #107 — so it is not a blind diff.

Proposal

A StarterBase opt-in property, default off, following $site_icon_tags
and its siblings:

/**
 * @var bool Skip ACFML's translation of ACF field definitions on plain
 * front-end views. Admin, REST, AJAX and WP-CLI keep it.
 */
protected $acfml_skip_frontend_field_translation = false;

Default off is not timidity. Switching it on is wrong for any site that renders
ACF field definitions to visitors:

  • a theme calling acf_form() — placeholders, instructions and labels become
    visitor-facing
  • a template printing a select/radio/checkbox choice label rather than
    its value

Neither is detectable from the kit, so the consuming site has to assert it. The
sloneek filter asserts exactly that in its own comment.

Retiring it later is a one-line default flip once ACFML memoises Config::getFor().

Prior art in this repo

Downstream implementation and full evidence: portadesign/sloneek#103.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions