Files
AsterionWP2026/tools/av-generate-solutions.php
j.foucher d2badf49d4 feat(bricks+wpml): full programmatic page generation + native WPML CTE integration
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>
2026-05-14 19:40:28 +02:00

379 lines
35 KiB
PHP

<?php
/**
* Plugin Name: Asterion — Generate solution pages (one-shot)
* Description: /?av_generate_solutions=1 reads content-source/solutions-data.php
* and creates/updates : hub /solutions/ + 4 child pages (my-proserve,
* proserve-flex, proserve-academy, customization). Idempotent.
*/
defined('ABSPATH') || exit;
require_once __DIR__ . '/_av-bricks-shared.php';
/* -----------------------------------------------------------------
Register Solutions-specific FR strings into the shared dict.
Brand names (MY PROSERVE, PROSERVE FLEX, PROSERVE ACADEMY, PROSERVE+,
PROSERVE™, ASTERION VR™) are kept verbatim.
----------------------------------------------------------------- */
add_filter('av_fr_translation_dict', function ($dict) {
return array_merge($dict, av_solutions_fr_dict());
});
function av_solutions_fr_dict() {
return [
/* ============================ HUB ============================ */
'Solutions' => 'Solutions',
'PROSERVE platform' => 'Plateforme PROSERVE',
"Four tiers. One platform. Train where you are." => "Quatre paliers. Une plateforme. Entraînez-vous là où vous êtes.",
"From the solo officer in a 9 m\u{B2} corner to a 16-trainee free-roam academy : PROSERVE\u{2122} scales without changing software. Same physics. Same AI. Same instructor logic. Pick the configuration that fits your context — upgrade or extend later."
=> "De l'agent seul dans 9 m\u{B2} à une académie en libre déambulation pour 16 stagiaires : PROSERVE\u{2122} évolue sans changer de logiciel. Mêmes physiques. Même IA. Même logique instructeur. Choisissez la configuration adaptée à votre contexte — étendez ou faites évoluer plus tard.",
"Pick the right configuration for your unit." => "Choisissez la configuration adaptée à votre unité.",
/* -------- Card titles + bodies (hub grid) -------- */
'MY PROSERVE' => 'MY PROSERVE',
'PROSERVE FLEX' => 'PROSERVE FLEX',
'PROSERVE ACADEMY' => 'PROSERVE ACADEMY',
'PROSERVE+' => 'PROSERVE+',
'Solo training' => 'Entraînement solo',
'Team training · 1 to 5' => 'Entraînement en équipe · 1 à 5',
'Unit training · 1 to 16' => 'Entraînement d\'unité · 1 à 16',
'Customization & options' => 'Personnalisation & options',
'Your first range, in a mini-case.'
=> 'Votre premier stand, dans un mini-coffre.',
'Your team, in the same scenario, on any flat surface.'
=> 'Votre équipe, dans le même scénario, sur n\'importe quelle surface plane.',
'The full training centre, in your facility.'
=> 'Le centre de formation complet, dans vos locaux.',
"When the catalog doesn't fit your mission."
=> "Quand le catalogue ne correspond pas à votre mission.",
// Card body concatenations from hub builder (eyebrow — tagline)
"Solo training \u{2014} Your first range, in a mini-case."
=> "Entraînement solo \u{2014} Votre premier stand, dans un mini-coffre.",
"Team training \u{B7} 1 to 5 \u{2014} Your team, in the same scenario, on any flat surface."
=> "Entraînement en équipe \u{B7} 1 à 5 \u{2014} Votre équipe, dans le même scénario, sur n'importe quelle surface plane.",
"Unit training \u{B7} 1 to 16 \u{2014} The full training centre, in your facility."
=> "Entraînement d'unité \u{B7} 1 à 16 \u{2014} Le centre de formation complet, dans vos locaux.",
"Customization & options \u{2014} When the catalog doesn't fit your mission."
=> "Personnalisation & options \u{2014} Quand le catalogue ne correspond pas à votre mission.",
/* ====================== MY PROSERVE ====================== */
"French regulation requires three live-fire sessions and 90 cartridges per officer per year. Reality: 10 to 15. MY PROSERVE closes that gap, every shift, in 9 square meters."
=> "La réglementation française exige trois séances de tir réel et 90 cartouches par agent et par an. La réalité : 10 à 15. MY PROSERVE comble cet écart, à chaque service, en 9 mètres carrés.",
'Why solo training matters now' => "Pourquoi l'entraînement solo compte maintenant",
'The training deficit is operational risk.'
=> "Le déficit d'entraînement est un risque opérationnel.",
"Two facts shape this product. First, regulation sets a floor that almost no force consistently meets. Second, every officer issued a firearm — including municipal police, gendarmerie reservists, and protective security agents — needs more repetition than range schedules can deliver. MY PROSERVE answers both: it puts a personal range in 3 to 9 m\u{B2} of free floor space, available 24/7, with the same physics as the live one."
=> "Deux faits structurent ce produit. D'abord, la réglementation fixe un plancher que presque aucune force ne respecte vraiment. Ensuite, chaque agent porteur d'arme — y compris les polices municipales, les réservistes de la gendarmerie et les agents de sécurité — a besoin de plus de répétition que le planning des stands ne peut offrir. MY PROSERVE répond aux deux : un stand personnel dans 3 à 9 m\u{B2} de surface libre, disponible 24h/24, avec la même physique que le tir réel.",
'Key benefits' => 'Bénéfices clés',
'What you get with one mini-case.' => 'Ce que vous obtenez avec un seul mini-coffre.',
'Ultra-portable' => 'Ultra-portable',
'Fits in a single mini-case sized for routine transport. Weighs less than 20 kg. Sets up in under five minutes.'
=> "Tient dans un seul mini-coffre dimensionné pour le transport quotidien. Pèse moins de 20 kg. Installation en moins de cinq minutes.",
'Available around the clock' => 'Disponible 24h/24',
'No range scheduling, no firearms instructor required for routine drills. Train at the start of a shift, between two patrols, or before deployment.'
=> "Pas de réservation de stand, pas d'instructeur de tir requis pour les exercices courants. Entraînez-vous au début d'un service, entre deux patrouilles ou avant une mission.",
'Multi-site' => 'Multi-sites',
'Move it from station to station. Equip a region with a small fleet — your officers train where they are stationed.'
=> "Déplacez-le d'un poste à l'autre. Équipez une région avec une petite flotte — vos agents s'entraînent là où ils sont affectés.",
'Economical' => 'Économique',
"Ammunition costs collapse. A CO2 refill costs about \u{20AC}0.25 and powers 30 to 40 shots. A gas refill costs about \u{20AC}8 and powers 600 to 800 shots. Break-even versus live ammunition arrives in months, not years."
=> "Les coûts de munitions s'effondrent. Une recharge CO2 coûte environ 0,25 \u{20AC} et permet 30 à 40 tirs. Une recharge gaz coûte environ 8 \u{20AC} et permet 600 à 800 tirs. Le retour sur investissement face aux munitions réelles se compte en mois, pas en années.",
'Real-recoil firearm' => 'Arme à recul réel',
'A gas-blowback or CO2 replica fitted with our Universal Weapon Digitizer. Recoil is genuine. Shot detection is dual (sound + recoil) to avoid false positives.'
=> "Une réplique gas-blowback ou CO2 équipée de notre Universal Weapon Digitizer. Le recul est authentique. La détection du tir est double (son + recul) pour éviter les faux positifs.",
'Personal AAR' => 'AAR personnel',
'Every officer has a personal account and dashboard. Track precision, reaction time, missed shots, target identification accuracy over time. Shareable with the supervising instructor.'
=> "Chaque agent dispose d'un compte personnel et d'un tableau de bord. Suivez précision, temps de réaction, tirs manqués et identification des cibles dans le temps. Partageable avec l'instructeur référent.",
"What's in the mini-case" => 'Le contenu du mini-coffre',
'Inside the case.' => 'Dans le coffre.',
'VR rendering laptop (Intel Core i9-13900HX · NVIDIA RTX 4080 · 16 GB DDR5 · Wi-Fi 6E)'
=> 'Portable de rendu VR (Intel Core i9-13900HX · NVIDIA RTX 4080 · 16 Go DDR5 · Wi-Fi 6E)',
"5K wireless headset (per-eye 2448\u{D7}2448 \u{B7} 120\u{B0} FOV \u{B7} Snapdragon XR2)"
=> "Casque sans fil 5K (2448\u{D7}2448 par \u{153}il \u{B7} 120\u{B0} FOV \u{B7} Snapdragon XR2)",
'Spare battery + charging dock' => 'Batterie de rechange + station de charge',
'Wi-Fi 6E router (tri-band, AXE11000)' => 'Routeur Wi-Fi 6E (tri-bande, AXE11000)',
'One real-recoil replica (handgun OR rifle, your choice from the catalog)'
=> 'Une réplique à recul réel (pistolet OU fusil, au choix dans le catalogue)',
'Universal Weapon Digitizer (UWeD)' => 'Universal Weapon Digitizer (UWeD)',
'Gas / CO2 starter pack' => 'Pack de démarrage gaz / CO2',
"Custom-foam waterproof transport case (~20 kg fully loaded)"
=> "Coffre de transport étanche avec mousse sur mesure (~20 kg en charge)",
'Suitable for' => 'Adapté à',
'Designed for the officer, not the academy.' => "Conçu pour l'agent, pas l'académie.",
'Police municipale and protective security agents — comply with continuous training obligations without depending on a national range network.'
=> "Police municipale et agents de sécurité — respectez les obligations de formation continue sans dépendre d'un réseau national de stands.",
'Reservists and gendarmes — train at home base between mobilizations.'
=> "Réservistes et gendarmes — entraînez-vous à votre base entre deux mobilisations.",
'Firearm-licensed officers in transport, energy, or critical infrastructure security.'
=> "Agents porteurs d'arme dans la sécurité des transports, de l'énergie ou des infrastructures critiques.",
'Trainee officers preparing for live-fire qualifications.'
=> 'Élèves préparant leurs qualifications de tir réel.',
'Volume pricing' => 'Tarif volume',
'Volume pricing for regional rollouts.' => 'Tarif volume pour les déploiements régionaux.',
"Equip a department, a region, or a national programme. Indicative tiers: from \u{20AC}9,000 per unit at single-piece purchase, dropping to \u{20AC}6,000 per unit at 50+ units. Quote on request."
=> "Équipez un département, une région ou un programme national. Tarifs indicatifs : à partir de 9 000 \u{20AC} l'unité à l'achat unitaire, descendant à 6 000 \u{20AC} l'unité au-delà de 50 unités. Devis sur demande.",
'When to size up.' => "Quand passer à la taille supérieure.",
'MY PROSERVE is the right choice for solo, repetitive marksmanship and decision drills. If you need cooperative team training (multiple trainees in the same scenario, real-time tactical coordination), step up to PROSERVE FLEX. If you run a full curriculum for 8 to 16 trainees at once, PROSERVE ACADEMY is the right fit. The software is the same — only the format scales.'
=> "MY PROSERVE est le bon choix pour le tir et les drills décisionnels solo et répétés. Si vous avez besoin d'entraînement coopératif (plusieurs stagiaires dans le même scénario, coordination tactique en temps réel), montez à PROSERVE FLEX. Si vous opérez un cursus complet pour 8 à 16 stagiaires en simultané, PROSERVE ACADEMY est adapté. Le logiciel est le même — seul le format évolue.",
'Compare with PROSERVE FLEX' => 'Comparer avec PROSERVE FLEX',
'Compare with PROSERVE ACADEMY' => 'Comparer avec PROSERVE ACADEMY',
/* ====================== PROSERVE FLEX ====================== */
'The portable team kit. One case per operator, one instructor PC, full multi-user cooperation. Deploy on a station floor, in a school gym, in a tent. The whole unit trains together — and reviews together.'
=> "Le kit équipe portable. Un coffre par opérateur, un PC instructeur, coopération multi-utilisateurs complète. Déployez-le sur le sol d'un poste, dans un gymnase, sous une tente. Toute l'unité s'entraîne ensemble — et débriefe ensemble.",
'Why team training in VR' => "Pourquoi l'entraînement en équipe en VR",
'Tactical performance is collective. Train it that way.'
=> "La performance tactique est collective. Entraînez-la comme telle.",
'Solo drills build muscle memory. They do not build cohesion. PROSERVE FLEX puts up to five operators in the same scenario, seeing each other as 3D avatars in roles defined by the mission — point, breacher, security, K9 handler, sniper. Communication, coordination, command-of-fire decisions: everything an instructor can rate, the system records.'
=> "Les exercices solo construisent la mémoire musculaire. Pas la cohésion. PROSERVE FLEX réunit jusqu'à cinq opérateurs dans le même scénario, se voyant comme des avatars 3D dans des rôles définis par la mission — pointe, breacher, sécurité, maître-chien, tireur d'élite. Communication, coordination, décisions de tir : tout ce qu'un instructeur peut noter, le système l'enregistre.",
'Why FLEX wins on mobility.' => 'Pourquoi FLEX gagne sur la mobilité.',
'Versatile' => 'Polyvalent',
'Solo, two-by-two, or in teams of up to five. Same hardware, different scenario. The kit adapts to the schedule, not the other way around.'
=> "Solo, par binôme ou en équipe jusqu'à cinq. Même matériel, scénarios différents. Le kit s'adapte au planning, pas l'inverse.",
'Mobile' => 'Mobile',
"One case per operator — fully wireless. No fixed install, no cable management, no specialized facility. Set up on any flat indoor surface from 9 m\u{B2} (solo) to 100 m\u{B2} (full team)."
=> "Un coffre par opérateur — entièrement sans fil. Pas d'installation fixe, pas de câbles, pas de local spécifique. Déployez-vous sur toute surface plane intérieure, de 9 m\u{B2} (solo) à 100 m\u{B2} (équipe complète).",
'Fast' => 'Rapide',
'Solo configuration ready in under five minutes. Full team configuration ready in under twenty. The instructor PC handles calibration on the fly.'
=> "Configuration solo prête en moins de cinq minutes. Configuration équipe complète prête en moins de vingt. Le PC instructeur gère la calibration à la volée.",
'Immersive' => 'Immersif',
'Real-recoil replicas, ballistic physics powered by Unreal Engine 5, autonomous AI civilians and adversaries with vision, hearing, and personality (cautious, courageous, aggressive, panicked). No two scenarios play out the same way.'
=> "Répliques à recul réel, physique balistique par Unreal Engine 5, civils et adversaires IA autonomes dotés de vision, ouïe et personnalité (prudent, courageux, agressif, paniqué). Aucun scénario ne se joue deux fois de la même façon.",
'Instructor-led' => 'Piloté par l\'instructeur',
"A dedicated instructor PC follows the action from any angle, manipulates the scenario in real time, displays each trainee's field of view and shot trace. Statistics roll up automatically."
=> "Un PC instructeur dédié suit l'action sous tous les angles, manipule le scénario en temps réel, affiche le champ de vision et la trace de tir de chaque stagiaire. Les statistiques remontent automatiquement.",
'AAR-ready' => 'AAR-ready',
"Replay every second of the session in top-down tactical view, free camera, or any trainee's first-person view. Pause, slow down, compare. End every drill with the data, not just the impression."
=> "Rejouez chaque seconde de la session en vue tactique zénithale, caméra libre ou première personne d'un stagiaire. Pause, ralenti, comparaison. Terminez chaque drill avec les données, pas seulement l'impression.",
'Inside the kit' => 'Le contenu du kit',
"What's in the FLEX deployment." => 'Le contenu du déploiement FLEX.',
'One trainee case per operator (laptop, headset, batteries, weapon, UWeD)'
=> 'Un coffre stagiaire par opérateur (laptop, casque, batteries, arme, UWeD)',
'One instructor PC + display' => 'Un PC instructeur + écran',
'Charging station + spare batteries' => 'Station de charge + batteries de rechange',
'Optional: haptic vest, wrist tracker, holsters, additional weapon profiles (PROSERVE+)'
=> 'Optionnel : gilet haptique, tracker de poignet, étuis, profils d\'armes additionnels (PROSERVE+)',
'Designed for tactical teams.' => 'Conçu pour les équipes tactiques.',
'Special weapons and tactics units (RAID, BRI, GIGN equivalents)'
=> 'Unités d\'intervention spéciales (RAID, BRI, équivalents GIGN)',
'Mobile gendarmerie squadrons' => 'Escadrons de gendarmerie mobile',
'National police rapid-response teams' => 'Équipes d\'intervention rapide de la police nationale',
'K9 handler training pairs' => 'Binômes maître-chien',
'Embarked teams (counter-piracy, executive protection)'
=> 'Équipes embarquées (contre-piraterie, protection rapprochée)',
'Sample scenarios' => 'Scénarios types',
'Sample team scenarios.' => 'Scénarios types en équipe.',
'Tactical progression in a residential apartment' => 'Progression tactique dans un appartement',
'Hostage rescue on an office floor' => 'Libération d\'otages dans un étage de bureaux',
'Vehicle stop with armed driver' => 'Contrôle d\'un véhicule avec conducteur armé',
'Active shooter in a supermarket' => 'Tireur actif dans un supermarché',
'Crowd containment with isolated aggressor' => 'Maintien de foule avec agresseur isolé',
'Tactical entry on a parking deck' => 'Entrée tactique sur un parking en élévation',
/* ====================== PROSERVE ACADEMY ====================== */
'For training centres, national academies, and large operational units. Up to sixteen trainees in the same free-roam scenario. Up to 900 m² of indoor space. Permanent, calibrated, supervised by a dedicated instructor cockpit.'
=> "Pour les centres de formation, les écoles nationales et les grandes unités opérationnelles. Jusqu'à seize stagiaires dans le même scénario en libre déambulation. Jusqu'à 900 m\u{B2} en intérieur. Installation permanente, calibrée, supervisée par un cockpit instructeur dédié.",
'When ACADEMY is the right fit' => 'Quand ACADEMY est adapté',
'When you need scale, not portability.' => 'Quand vous avez besoin d\'échelle, pas de portabilité.',
"ACADEMY is the right configuration when you train cohorts of 8 to 16 trainees in the same scenario, when training is the unit's daily activity, and when you have a dedicated facility you can configure once. The hardware lives in a rack. The instructor sits in a control booth. The trainees walk in, calibrate, and start."
=> "ACADEMY est la bonne configuration quand vous formez des cohortes de 8 à 16 stagiaires dans le même scénario, quand l'entraînement est l'activité quotidienne de l'unité et que vous disposez d'un local dédié configurable une fois pour toutes. Le matériel vit en baie. L'instructeur est en cabine de contrôle. Les stagiaires entrent, se calibrent et commencent.",
'What ACADEMY brings to a curriculum.' => "Ce qu'ACADEMY apporte à un cursus.",
'Up to 16 trainees, same scenario' => "Jusqu'à 16 stagiaires, même scénario",
'All trainees see each other as full-body 3D avatars whose appearance reflects their role — assault, security, breach, civilian. Communication is voice-natural and proximity-based.'
=> "Tous les stagiaires se voient en avatars 3D corps entier dont l'apparence reflète leur rôle — assaut, sécurité, brèche, civil. La communication est vocale naturelle et basée sur la proximité.",
'Modular ergonomics' => 'Ergonomie modulaire',
'Configure for one trainee or sixteen. Self-configuring software. Less than five minutes from session-end to next-session-start.'
=> "Configuration pour un ou seize stagiaires. Logiciel auto-configurable. Moins de cinq minutes entre la fin d'une session et le début de la suivante.",
'Multi-mode training' => 'Entraînement multi-modes',
'Procedural learning. Motor-skill acquisition. Repetitive drill (skill-and-drill). Deliberate fatigue training. Whatever your pedagogical model, the platform supports it.'
=> "Apprentissage procédural. Acquisition motrice. Drill répétitif (skill-and-drill). Entraînement délibéré en fatigue. Quel que soit votre modèle pédagogique, la plateforme le supporte.",
'Instructor cockpit, dedicated control room' => 'Cockpit instructeur, salle de contrôle dédiée',
"Real-time 360\u{B0} video monitoring, multi-angle tactical view, shot-trace overlay, individual statistics, scenario manipulation on the fly."
=> "Supervision vidéo 360\u{B0} en temps réel, vue tactique multi-angles, overlay des traces de tir, statistiques individuelles, manipulation du scénario à la volée.",
'Customizable scenarios' => 'Scénarios personnalisables',
"Add bespoke scenarios produced by our French studio. Replicate your local training environments, your unit's uniforms, your specific weapons profiles. Production lead time: 1 to 3 months per environment."
=> "Ajoutez des scénarios sur mesure produits dans notre studio français. Reproduisez vos environnements locaux, les uniformes de votre unité, vos profils d'armes spécifiques. Délai de production : 1 à 3 mois par environnement.",
'Permanent compute infrastructure' => 'Infrastructure de calcul permanente',
'Server rack with VR rendering machines, instructor workstation, dedicated Wi-Fi 6E network. Engineered for daily operation, redundancy, and remote support.'
=> "Baie serveur avec machines de rendu VR, poste instructeur, réseau Wi-Fi 6E dédié. Conçu pour l'opération quotidienne, la redondance et le support à distance.",
'Facility requirements' => 'Exigences du local',
'What you need on site.' => 'Ce qu\'il vous faut sur site.',
"Indoor floor footprint: from 100 m\u{B2} to 900 m\u{B2} depending on the scenarios you train (we recommend 10\u{D7}10 m for full-team dynamic CQB)"
=> "Emprise au sol : de 100 m\u{B2} à 900 m\u{B2} selon les scénarios entraînés (nous recommandons 10\u{D7}10 m pour le CQB dynamique en équipe complète)",
'Even, non-flickering lighting (no direct sunlight)' => 'Éclairage homogène et stable (pas de lumière directe du soleil)',
'Decorated walls (avoid uniformly colored surfaces — posters or markings improve tracking)'
=> "Murs habillés (éviter les surfaces uniformes — affiches ou marquages améliorent le tracking)",
"Dedicated control room (minimum 6 m\u{B2}) for instructor cockpit"
=> "Salle de contrôle dédiée (minimum 6 m\u{B2}) pour le cockpit instructeur",
"No specific HVAC requirements (unlike live-fire ranges \u{2014} no smoke, no lead, no decontamination)"
=> "Aucune exigence CVC spécifique (contrairement aux stands de tir réel \u{2014} pas de fumée, pas de plomb, pas de décontamination)",
'Designed for academies.' => 'Conçu pour les académies.',
'Police academies (national and regional)' => 'Écoles de police (nationales et régionales)',
'Gendarmerie schools' => 'Écoles de gendarmerie',
'Military training centres' => 'Centres de formation militaire',
'Firefighter training schools' => 'Écoles de sapeurs-pompiers',
'Joint inter-services training centres' => 'Centres interarmées de formation',
'Distributors and integrators looking to operate a turnkey training-as-a-service offer'
=> "Distributeurs et intégrateurs souhaitant opérer une offre training-as-a-service clé en main",
/* ====================== PROSERVE+ ====================== */
'Your environments. Your weapons. Your uniforms. Your doctrine. Our engineers, your specifications. PROSERVE+ extends every PROSERVE configuration with bespoke hardware, environments, and software, designed and produced in our French studio.'
=> "Vos environnements. Vos armes. Vos uniformes. Votre doctrine. Nos ingénieurs, vos spécifications. PROSERVE+ étend chaque configuration PROSERVE avec du matériel, des environnements et des logiciels sur mesure, conçus et produits dans notre studio français.",
'Hardware add-ons' => 'Extensions matérielles',
'Hardware extensions.' => 'Extensions matérielles.',
'Haptic vest' => 'Gilet haptique',
'Full-torso wireless haptic vest. 32 ERM motors. Renders the impact location of incoming fire and explosive proximity. Bluetooth BLE, 13.5 hours autonomy.'
=> "Gilet haptique sans fil torso complet. 32 moteurs ERM. Restitue la localisation des impacts entrants et la proximité d'explosion. Bluetooth BLE, 13,5 heures d'autonomie.",
'Wrist trackers' => 'Trackers de poignet',
'Six degree-of-freedom wrist tracking for precise hand placement, weapon retention, and grappling drills.'
=> "Tracking de poignet à six degrés de liberté pour le placement précis des mains, la rétention de l'arme et les exercices de close-combat.",
'Custom replicas' => 'Répliques sur mesure',
'Sourcing and tracker integration for replicas that go beyond the standard catalog: country-specific service weapons, less-lethal options (electroshock, OC spray, beanbag launchers), shoulder-fired support weapons, and field-specific equipment.'
=> "Sourcing et intégration de trackers pour des répliques qui dépassent le catalogue standard : armes de service spécifiques à un pays, options non-létales (électrochoc, gaz OC, lanceurs de sacs lestés), armes d'appui à l'épaule et équipements terrain spécifiques.",
'Environments and scenarios' => 'Environnements et scénarios',
'Bespoke environments and scenarios.' => 'Environnements et scénarios sur mesure.',
'All environment types are possible. We model and animate in our French studio, with our team of 3D designers and senior engineers. Lead time: from one to three months per environment, depending on complexity. One environment typically generates five to ten distinct scenarios.'
=> "Tous les types d'environnements sont possibles. Modélisation et animation dans notre studio français, avec notre équipe de designers 3D et d'ingénieurs seniors. Délai : de un à trois mois par environnement, selon la complexité. Un environnement génère typiquement cinq à dix scénarios distincts.",
'Specific city blocks (urban CQB on actual local geometry)' => 'Quartiers urbains spécifiques (CQB urbain sur géométrie locale réelle)',
'Public transit infrastructure (specific airports, stations, metros)'
=> 'Infrastructures de transport public (aéroports, gares, métros spécifiques)',
'Industrial sites (oil & gas, chemical, energy)' => 'Sites industriels (oil & gas, chimie, énergie)',
'Critical infrastructure (data centres, power plants, water treatment)'
=> 'Infrastructures critiques (data centres, centrales, traitement des eaux)',
'Government and embassy buildings' => 'Bâtiments gouvernementaux et ambassades',
'Maritime (boats, naval vessels, oil rigs)' => 'Maritime (navires, bâtiments militaires, plateformes pétrolières)',
'Aviation (commercial airliners, airfields)' => 'Aviation (avions de ligne, aérodromes)',
'Custom characters' => 'Personnages sur mesure',
'Characters in your colors.' => 'Des personnages aux couleurs de votre unité.',
'Virtual characters dressed in the uniforms of your forces, equipped with the gear of your unit. Hostile actors based on threat profiles relevant to your area of operations. Civilian behaviors localized to the cultural context of the scenario.'
=> "Personnages virtuels en uniforme de vos forces, équipés du matériel de votre unité. Acteurs hostiles fondés sur des profils de menace pertinents pour votre zone d'opération. Comportements civils localisés au contexte culturel du scénario.",
'Process' => 'Processus',
'How a PROSERVE+ project runs.' => 'Comment se déroule un projet PROSERVE+.',
'1. Discovery' => '1. Cadrage',
'We interview your operators and instructors to capture the operational reality and the pedagogical goal.'
=> "Nous interviewons vos opérateurs et instructeurs pour capter la réalité opérationnelle et l'objectif pédagogique.",
'2. Specification' => '2. Spécification',
'Joint document defining the environment, scenarios, characters, weapons, and instructor controls.'
=> "Document conjoint définissant l'environnement, les scénarios, les personnages, les armes et les contrôles instructeur.",
'3. Production' => '3. Production',
'Modeling, animation, AI behavior tuning in our French studio. Weekly review milestones.'
=> "Modélisation, animation, réglage des comportements IA dans notre studio français. Jalons de revue hebdomadaires.",
'4. Integration' => '4. Intégration',
'Software update deploys the new content to your existing PROSERVE installation.'
=> "Une mise à jour logicielle déploie le nouveau contenu sur votre installation PROSERVE existante.",
'5. Validation' => '5. Validation',
'On-site instructor training and acceptance review.'
=> "Formation des instructeurs sur site et revue de réception.",
];
}
/* -----------------------------------------------------------------
URL trigger — fires on template_redirect (NOT init) so Bricks's
element controls are loaded (happens on 'wp' hook). Without loaded
controls, Bricks's wpml_page_builder_register_strings handler skips
every element silently and NO strings reach icl_strings → ATE empty.
----------------------------------------------------------------- */
add_action('template_redirect', function () {
if (empty($_GET['av_generate_solutions'])) return;
if (! in_array($_SERVER['SERVER_NAME'] ?? '', ['localhost', '127.0.0.1'], true)) wp_die('localhost only');
$data_file = 'C:\\ASTERION\\GIT\\WP2026\\content-source\\solutions-data.php';
if (! file_exists($data_file)) wp_die("data file not found: $data_file");
$solutions = include $data_file;
if (! is_array($solutions)) wp_die('solutions data not array');
$parent_id = av_ensure_hub_page('solutions', 'Solutions');
$report = ["Rebuilding /solutions/ hub + child detail pages..."];
// Hub
$hub_elements = av_build_solutions_hub_elements($solutions);
$hub_write = av_write_bricks_page_elements($parent_id, $hub_elements);
$report[] = " HUB → page #$parent_id (rebuilt) — " . count($hub_elements)
. " elements (json={$hub_write['json_len']}, stored={$hub_write['stored_len']})";
// Detail pages
foreach ($solutions as $slug => $d) {
$page = av_upsert_detail_page($slug, 'solutions', $d);
$report[] = " $slug → page #{$page['id']} ({$page['action']}) — {$page['element_count']} elements (json={$page['json_len']}, stored={$page['stored_len']})";
}
header('Content-Type: text/plain; charset=utf-8');
echo implode("\n", $report);
exit;
});
/* -----------------------------------------------------------------
Solutions hub : hero + 4-card grid (tier comparison) + closer CTA
----------------------------------------------------------------- */
function av_build_solutions_hub_elements($solutions) {
$els = [];
// HERO
$els = array_merge($els, av_build_hero('shub', [
'eyebrow' => 'PROSERVE platform',
'tagline' => "Four tiers. One platform. Train where you are.",
'hero_lead' => "From the solo officer in a 9 m\u{B2} corner to a 16-trainee free-roam academy : PROSERVE\u{2122} scales without changing software. Same physics. Same AI. Same instructor logic. Pick the configuration that fits your context — upgrade or extend later.",
'ctas' => [
['label' => 'Book a demo', 'href' => '/request-demo/', 'variant' => 'primary'],
['label' => 'Request a T&E Kit', 'href' => '/request-te-kit/', 'variant' => 'secondary'],
],
]));
// 4-card grid — one card per tier
$cards_section = [
'eyebrow' => '',
'title' => 'Pick the right configuration for your unit.',
'items' => [],
];
foreach ($solutions as $slug => $d) {
$cards_section['items'][] = [
'title' => $d['title'],
'body' => ($d['eyebrow'] ?? '') . " \u{2014} " . ($d['tagline'] ?? ''),
];
}
$els = array_merge($els, av_build_cards_section('shubgrid', 0, $cards_section, 'var(--color-bg-subtle)'));
// Append "Learn more →" link in each card (post-build patch)
$i = 0;
foreach ($solutions as $slug => $d) {
$kid = "kshubgridc0x{$i}";
$lid = "lnshubgridc0x{$i}";
$els[] = av_text_link($lid, $kid, "Learn more \u{2192}", "/solutions/$slug/");
foreach ($els as &$el) {
if (($el['id'] ?? '') === $kid) {
$el['children'][] = $lid;
}
}
unset($el);
$i++;
}
// CLOSER CTA
$els = array_merge($els, av_build_closer_cta('shub'));
return $els;
}