Sessions 3-4 deliverables. Establishes the Asterion content pipeline :
EN pages generated programmatically from content-source/*-data.php, FR
translations rebuilt natively by Bricks's WPML integration from EN structure
+ WPML strings (no direct FR _bricks_page_content_2 writes).
## tools/_av-bricks-shared.php (the library)
Shared helpers + element atoms (eyebrow/heading/text/button/text-link) +
section builders (hero/prose/list/cards/cta/upsell/closer) + page write
pipeline that handles : write EN content via $wpdb (bypass Bricks's filter),
force Bricks::Elements::load_elements() so controls are populated, fire
wpml_page_builder_register_strings, push dict FR into icl_string_translations
(skip user CTE edits, auto-prefix /fr on internal URLs), trigger Bricks
native FR rebuild via wpml_page_builder_string_translated, and ensure a CTE
job exists (icl_translate_job.editor='wpml' + revision=NULL + populate
icl_translate rows with base64+gzip-encoded EN/FR strings).
## tools/av-generate-{industries,solutions}.php
Thin wrappers : URL trigger (template_redirect, not init — Bricks elements
load on 'wp' hook), add_filter('av_fr_translation_dict', ...) to register
FR strings, content-type-specific hub builder. ~150 FR strings per type.
## tools/av-translate-{industries,solutions}-fr.php
One-shot seeds for the initial FR linked posts via WPML trid mapping.
## tools/av-wpml-* operational mu-plugins
- set-cte : switch doc_translation_method to ICL_TM_TMETHOD_EDITOR (=1)
- create-jobs : reset CTE jobs (status=10 + needs_update=1 + editor=wpml +
revision=NULL). Integrated into pipeline via av_ensure_wpml_cte_job().
- fix-status / repair : align FR posts to correct trid + cleanup orphans.
- push-translations : push dict into icl_string_translations.
- plugins-check / debug / dump-strings / icl-dump / rescan : diagnostics.
## tools/av-fix-link-types.php
One-shot DB migration : convert legacy link.type='internal'+url to 'external'
across all _bricks_page_*_2 meta values (JSON and PHP-serialized). Bricks's
set_link_attributes() only renders href= for type='external' — older session-3
templates lost their href silently.
## Other tools/ mu-plugins
- av-bricks-rest-bridge : pivot — exposes Bricks CPTs over REST, dual-format
read filter (JSON for MCP / array for renderer), auto-mirror content→header/
footer for templates.
- av-apply-bricks-styles, av-cleanup-orphans, av-create-wp-menu, av-debug-meta,
av-dump-bricks, av-fix-template-meta : misc operational scripts.
## Bricks Config/
JSON snapshots of bricks_theme_styles, bricks_color_palette,
bricks_global_settings for diffable inspection.
## .gitignore
Add .mcp.json — holds per-developer WP App Password for the Bricks MCP server.
## wp-content/themes/asterion-bricks/assets/css/utilities.css
Note that all responsive overrides are now emitted natively by the page
generator via Bricks per-breakpoint settings, no custom CSS needed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
123 lines
5.0 KiB
PHP
123 lines
5.0 KiB
PHP
<?php
|
|
/**
|
|
* Plugin Name: Asterion — Push dict translations into WPML's icl_strings (so ATE shows them)
|
|
* Description:
|
|
* /?av_wpml_strings_dump=1 → dump registered Bricks strings + their FR translation state
|
|
* /?av_wpml_strings_push=1 → for each EN string in icl_strings (Bricks context), if our
|
|
* av_fr_translation_dictionary() has a match, write the FR
|
|
* translation via icl_add_string_translation() with status=10.
|
|
*
|
|
* Goal : ATE shows our programmatic translations pre-filled when the linguist opens a page.
|
|
* Editing in ATE then updates icl_string_translations → WPML rebuilds the FR Bricks JSON.
|
|
*/
|
|
defined('ABSPATH') || exit;
|
|
|
|
require_once __DIR__ . '/_av-bricks-shared.php';
|
|
|
|
add_action('init', function () {
|
|
if (! in_array($_SERVER['SERVER_NAME'] ?? '', ['localhost', '127.0.0.1'], true)) return;
|
|
|
|
if (! empty($_GET['av_wpml_strings_dump'])) av_wpml_strings_dump();
|
|
if (! empty($_GET['av_wpml_strings_push'])) av_wpml_strings_push();
|
|
});
|
|
|
|
function av_wpml_strings_dump() {
|
|
global $wpdb;
|
|
header('Content-Type: text/plain; charset=utf-8');
|
|
|
|
// 1) Count strings per context
|
|
$rows = $wpdb->get_results(
|
|
"SELECT context, COUNT(*) AS n FROM {$wpdb->prefix}icl_strings GROUP BY context ORDER BY n DESC"
|
|
);
|
|
echo "Strings registered (icl_strings) by context :\n";
|
|
foreach ($rows as $r) echo sprintf(" %-40s %d strings\n", $r->context, $r->n);
|
|
|
|
// 1b) Sample strings whose VALUE matches our content (find which context Bricks uses for page content)
|
|
echo "\nSearching for our content strings :\n";
|
|
foreach (['PROSERVE', 'Police training', 'Built for those', 'mini-case', 'Industries', 'Solutions'] as $needle) {
|
|
$found = $wpdb->get_results($wpdb->prepare(
|
|
"SELECT context, name, LEFT(value, 70) AS preview FROM {$wpdb->prefix}icl_strings WHERE value LIKE %s LIMIT 3",
|
|
'%' . $wpdb->esc_like($needle) . '%'
|
|
));
|
|
echo " needle '$needle' : " . count($found) . " match(es)\n";
|
|
foreach ($found as $f) echo " context='{$f->context}' name='{$f->name}' value='{$f->preview}'\n";
|
|
}
|
|
|
|
// 2) Sample a few Bricks strings + their FR (context is just 'bricks', not per-page)
|
|
echo "\nSample Bricks strings (10 of 156) :\n";
|
|
$strings = $wpdb->get_results(
|
|
"SELECT id, name, LEFT(value, 80) AS preview, status FROM {$wpdb->prefix}icl_strings WHERE context='bricks' LIMIT 10"
|
|
);
|
|
foreach ($strings as $s) {
|
|
echo " id={$s->id} name={$s->name} status={$s->status}\n EN: {$s->preview}\n";
|
|
$fr = $wpdb->get_var($wpdb->prepare(
|
|
"SELECT LEFT(value, 80) FROM {$wpdb->prefix}icl_string_translations WHERE string_id=%d AND language='fr' LIMIT 1",
|
|
$s->id
|
|
));
|
|
echo " FR: " . ($fr !== null ? $fr : '(no translation)') . "\n";
|
|
}
|
|
|
|
// 3) Count translations by language and status
|
|
$tr_rows = $wpdb->get_results(
|
|
"SELECT language, status, COUNT(*) AS n
|
|
FROM {$wpdb->prefix}icl_string_translations GROUP BY language, status"
|
|
);
|
|
echo "\nString translations summary :\n";
|
|
foreach ($tr_rows as $r) echo " lang={$r->language} status={$r->status} : {$r->n}\n";
|
|
|
|
exit;
|
|
}
|
|
|
|
function av_wpml_strings_push() {
|
|
global $wpdb;
|
|
header('Content-Type: text/plain; charset=utf-8');
|
|
|
|
if (! function_exists('icl_add_string_translation')) {
|
|
echo "FAIL : icl_add_string_translation() not available\n";
|
|
exit;
|
|
}
|
|
|
|
// Trigger the dict to load (with all filter contributions from generators)
|
|
$dict = av_fr_translation_dictionary();
|
|
echo "Loaded FR dictionary : " . count($dict) . " EN→FR mappings\n\n";
|
|
|
|
// All Bricks page-content strings (per-page contexts : bricks-{POST_ID})
|
|
$strings = $wpdb->get_results(
|
|
"SELECT id, value, context FROM {$wpdb->prefix}icl_strings WHERE context LIKE 'bricks-%'"
|
|
);
|
|
echo "Found " . count($strings) . " EN strings registered for Bricks pages\n\n";
|
|
|
|
$matched = 0;
|
|
$unmatched = 0;
|
|
$written = 0;
|
|
foreach ($strings as $s) {
|
|
if (! isset($dict[$s->value])) {
|
|
$unmatched++;
|
|
continue;
|
|
}
|
|
$matched++;
|
|
$fr = $dict[$s->value];
|
|
// ICL_TM_COMPLETE = 10
|
|
$result = icl_add_string_translation((int) $s->id, 'fr', $fr, 10);
|
|
if ($result) $written++;
|
|
}
|
|
|
|
echo "Matched in dict : $matched\n";
|
|
echo "Written to icl_string_translations : $written\n";
|
|
echo "Unmatched (no FR in dict, will stay EN in ATE) : $unmatched\n";
|
|
|
|
if ($unmatched > 0 && $unmatched < 100) {
|
|
echo "\nSample unmatched EN strings (extend the dict to translate these) :\n";
|
|
$shown = 0;
|
|
foreach ($strings as $s) {
|
|
if (! isset($dict[$s->value]) && $shown < 15) {
|
|
echo " - " . substr($s->value, 0, 120) . (strlen($s->value) > 120 ? '...' : '') . "\n";
|
|
$shown++;
|
|
}
|
|
}
|
|
}
|
|
|
|
echo "\nDONE. Refresh ATE — FR strings should be pre-filled.\n";
|
|
exit;
|
|
}
|