From d3bb32eb97e9e44baddf6f4dc9024d84fafbf80f Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 13 Jul 2026 17:17:53 -0400 Subject: [PATCH] Enhance multi-page AJAX handling by enforcing context for translations in `ajax_inscr_gestion` and `ajax_promoteur_hub`. Introduce new language keys for improved user feedback and error messages. Increment version code to 4.72.806 to reflect these changes. --- .cursor/rules/ms1-documentation.mdc | 20 ++ ajax_inscr_gestion.php | 13 +- ajax_inscr_gestion_qr.php | 8 +- ajax_promoteur_hub.php | 10 +- inc_tableau_eve_acces.php | 6 +- js/v2/inscr-gestion.js | 65 +++-- js/v2/promoteur-hub.js | 16 +- mobile.php | 50 ++-- php/inc_fx_eve_acces_promoteur.php | 96 +++---- php/inc_fx_inscriptions_gestion.php | 101 +++++-- php/inc_fx_promoteur_hub.php | 173 ++++++++---- php/inc_settings.php | 2 +- php/inc_v2_assets.php | 44 +++- sql/MSIN-4401-eve-acces-promoteur-info.sql | 99 +++++++ sql/MSIN-4401-v2-info-complet.sql | 290 +++++++++++++++++++++ 15 files changed, 777 insertions(+), 216 deletions(-) create mode 100644 sql/MSIN-4401-eve-acces-promoteur-info.sql create mode 100644 sql/MSIN-4401-v2-info-complet.sql diff --git a/.cursor/rules/ms1-documentation.mdc b/.cursor/rules/ms1-documentation.mdc index 499d65f..ed92e45 100644 --- a/.cursor/rules/ms1-documentation.mdc +++ b/.cursor/rules/ms1-documentation.mdc @@ -12,6 +12,26 @@ alwaysApply: true - Référencer le ticket : `// MSIN-4405` ou bloc PHPDoc `* MSIN-4405 — …`. - Langue : **français** pour les commentaires métier (comme le reste du projet), sauf si le fichier est entièrement en anglais. +## Libellés UI (table `info`) + +- Tout texte affiché à l’utilisateur (FR/EN) passe par **Info** (`afficheTexte` / clés `info_clef`), **jamais** codé en dur dans le PHP/JS. +- Nouvelle clé = script SQL `sql/MSIN-xxxx-….sql` (INSERT dans `info`, fr + en), exécutable en pilote/prod. +- Les fallbacks PHP FR/EN ne remplacent pas ce script : ce sont un filet de secours uniquement. + +## AJAX multi-pages (Info) + +Tout endpoint AJAX qui lit/écrit des libellés Info et peut être appelé depuis plusieurs pages (compte, hub, `/mobile`) doit **forcer le contexte `compte.php`** avant les includes utiles : + +```php +define('MS1_…_AJAX', true); // optionnel, pattern existant +$_SERVER['PHP_SELF'] = '/compte.php'; +$_SERVER['SCRIPT_NAME'] = '/compte.php'; +$vPage = 'compte.php'; +$vtexte_page = obtenirTextepage($vPage, $strLangue, 2); +``` + +Objectif : éviter les clés créées sous `info_prg=ajax_….php` et les libellés manquants selon la page appelante. Références : `ajax_promoteur_hub.php`, `ajax_bib_range.php`, `ajax_inscr_gestion.php`. + ## Scripts SQL En-tête obligatoire en tête de fichier : diff --git a/ajax_inscr_gestion.php b/ajax_inscr_gestion.php index 31f5244..ef098dc 100644 --- a/ajax_inscr_gestion.php +++ b/ajax_inscr_gestion.php @@ -5,6 +5,13 @@ */ session_start(); +// MSIN-4401 — Traductions toujours au nom de compte.php (jamais ajax_inscr_gestion.php). +// Evite les doublons info_prg=ajax_*.php et les libelles manquants selon la page appelante +// (hub, compte, /mobile). Meme pattern que ajax_promoteur_hub / ajax_bib_range. +define('MS1_INSCR_GESTION_AJAX', true); +$_SERVER['PHP_SELF'] = '/compte.php'; +$_SERVER['SCRIPT_NAME'] = '/compte.php'; + header('Content-Type: application/json; charset=utf-8'); // Filet de securite : transforme tout fatal (include manquant, fonction @@ -36,10 +43,14 @@ require_once 'php/inc_fx_eve_acces.php'; $intComId = intval($_SESSION['com_id'] ?? 0); $strAction = trim((string)($_REQUEST['a'] ?? '')); $strLangue = trim((string)($_REQUEST['lang'] ?? 'fr')); -if ($strLangue === '') { +if ($strLangue === '' || !in_array($strLangue, array('fr', 'en'), true)) { $strLangue = 'fr'; } +$vPage = 'compte.php'; +$vtexte_page = obtenirTextepage($vPage, $strLangue, 2); +fxInscrGestionInitTextes($strLangue); + if ($intComId <= 0) { $GLOBALS['__inscr_gestion_done'] = true; // MSIN-4401 — code session : le client recharge pour afficher le login. diff --git a/ajax_inscr_gestion_qr.php b/ajax_inscr_gestion_qr.php index c7cf6da..7681073 100644 --- a/ajax_inscr_gestion_qr.php +++ b/ajax_inscr_gestion_qr.php @@ -2,18 +2,24 @@ session_start(); +// MSIN-4401 — Traductions toujours au nom de compte.php (jamais ajax_inscr_gestion_qr.php). +define('MS1_INSCR_GESTION_AJAX', true); +$_SERVER['PHP_SELF'] = '/compte.php'; +$_SERVER['SCRIPT_NAME'] = '/compte.php'; + require_once 'php/inc_fonctions.php'; require_once 'php/inc_fx_inscriptions_gestion.php'; global $vDomaine; $strLangue = 'fr'; -if (!empty($_REQUEST['lang'])) { +if (!empty($_REQUEST['lang']) && in_array($_REQUEST['lang'], array('fr', 'en'), true)) { $strLangue = $_REQUEST['lang']; } $vPage = 'compte.php'; $vtexte_page = obtenirTextepage($vPage, $strLangue, 2); +fxInscrGestionInitTextes($strLangue); header('Content-Type: application/json; charset=utf-8'); diff --git a/ajax_promoteur_hub.php b/ajax_promoteur_hub.php index 525ff2d..12829ab 100644 --- a/ajax_promoteur_hub.php +++ b/ajax_promoteur_hub.php @@ -26,7 +26,7 @@ if (empty($_SESSION['com_id'])) { echo json_encode(array( 'ok' => false, 'code' => 'session', - 'msg' => 'Session expirée', + 'msg' => fxPromoteurHubTexte('promoteur_hub_ajax_session', 0), )); exit; } @@ -34,18 +34,18 @@ if (empty($_SESSION['com_id'])) { $intComId = intval($_SESSION['com_id']); if (!in_array($strAction, array('event_stats', 'event_links'), true) || $intEveId <= 0) { - echo json_encode(array('ok' => false, 'msg' => 'Requête invalide')); + echo json_encode(array('ok' => false, 'msg' => fxPromoteurHubTexte('promoteur_hub_ajax_invalid', 0))); exit; } if (!fxPromoteurHubComHasEvent($intComId, $intEveId)) { - echo json_encode(array('ok' => false, 'msg' => 'Accès refusé')); + echo json_encode(array('ok' => false, 'msg' => fxPromoteurHubTexte('promoteur_hub_ajax_denied', 0))); exit; } if ($strAction === 'event_stats') { if (!fxPromoteurHubCanViewDashboard($intComId, $intEveId)) { - echo json_encode(array('ok' => false, 'msg' => 'Accès refusé')); + echo json_encode(array('ok' => false, 'msg' => fxPromoteurHubTexte('promoteur_hub_ajax_denied', 0))); exit; } @@ -61,7 +61,7 @@ if ($strAction === 'event_stats') { $arrItem = fxPromoteurHubGetEventItemForLinks($intComId, $intEveId); if ($arrItem === null) { - echo json_encode(array('ok' => false, 'msg' => 'Événement introuvable')); + echo json_encode(array('ok' => false, 'msg' => fxPromoteurHubTexte('promoteur_hub_ajax_not_found', 0))); exit; } diff --git a/inc_tableau_eve_acces.php b/inc_tableau_eve_acces.php index b997b10..799fb13 100644 --- a/inc_tableau_eve_acces.php +++ b/inc_tableau_eve_acces.php @@ -34,11 +34,9 @@ if ($intEveId <= 0 || !fxEveAccesCanTeamInvite($intActor, $intEveId)) { // Headers déjà envoyés (include après layout) : message + lien, pas de header Location. echo '
'; - echo fxEveAccesPromoteurEsc( - $strLangue === 'en' ? 'Access denied.' : 'Accès refusé.' - ); + echo fxEveAccesPromoteurEsc(fxEveAccesPromoteurT('eve_acces_promoteur_access_denied', $strLangue)); echo ' '; - echo fxEveAccesPromoteurEsc($strLangue === 'en' ? 'Back' : 'Retour'); + echo fxEveAccesPromoteurEsc(fxEveAccesPromoteurT('eve_acces_promoteur_btn_back', $strLangue)); echo '
'; return; } diff --git a/js/v2/inscr-gestion.js b/js/v2/inscr-gestion.js index 571966b..ad021bc 100644 --- a/js/v2/inscr-gestion.js +++ b/js/v2/inscr-gestion.js @@ -6,6 +6,11 @@ 'use strict'; var cfg = window.MS1_V2_INSCR_GESTION || {}; + var i18n = cfg.i18n || {}; + + function t(key) { + return (i18n[key] && String(i18n[key])) || ''; + } // MSIN-4401 — Session expiree en AJAX : recharger → ecran de login (pas un message mort). function fxInscrRedirectIfSessionExpired(result) { @@ -189,10 +194,10 @@ $panel.attr('data-loaded', '1'); focusRefundPanel(); } else { - $panel.html('
\u26a0\ufe0f ' + ((result && result.message) || 'Erreur de chargement.') + '
'); + $panel.html('
\u26a0\ufe0f ' + ((result && result.message) || t('errLoad')) + '
'); } }, 'json').fail(function () { - $panel.html('
\u26a0\ufe0f Erreur reseau.
'); + $panel.html('
\u26a0\ufe0f ' + t('errNetwork') + '
'); }); }); @@ -230,10 +235,10 @@ $panel.html(result.html); $panel.attr('data-loaded', '1'); } else { - $panel.html('
\u26a0\ufe0f ' + ((result && result.message) || 'Erreur de chargement.') + '
'); + $panel.html('
\u26a0\ufe0f ' + ((result && result.message) || t('errLoad')) + '
'); } }, 'json').fail(function () { - $panel.html('
\u26a0\ufe0f Erreur reseau.
'); + $panel.html('
\u26a0\ufe0f ' + t('errNetwork') + '
'); }); }); @@ -266,17 +271,17 @@ Ms1Loader.hide(); } if (json && json.success) { - showMsg('\u2705 ' + (json.message || 'Remboursement effectue.'), true); + showMsg('\u2705 ' + (json.message || t('refundOk')), true); onRefundSuccess(form, json); } else { - showMsg('\u26a0\ufe0f ' + ((json && json.message) || 'Erreur remboursement.'), false); + showMsg('\u26a0\ufe0f ' + ((json && json.message) || t('errRefund')), false); } }) .catch(function () { if (window.Ms1Loader) { Ms1Loader.hide(); } - showMsg('\u26a0\ufe0f Erreur reseau pendant le remboursement.', false); + showMsg('\u26a0\ufe0f ' + t('errNetwork'), false); }); }); })(); @@ -733,23 +738,18 @@ } function bibOcrErrorMessage(result) { - var blnEn = (document.documentElement.lang === 'en'); var code = result && result.message ? String(result.message) : ''; if (code === 'ocr_not_configured') { - return blnEn - ? 'OCR not configured (Vision API key missing on server).' - : 'OCR non configuré (clé Vision absente sur le serveur).'; + return t('bibOcrNotConfigured'); } if (code === 'vision_api' || code === 'vision_http' || code === 'vision_curl') { var detail = (result && result.detail) ? String(result.detail) : ''; - return (blnEn ? 'Vision error: ' : 'Erreur Vision : ') + (detail || code); + return t('bibOcrVisionPrefix') + (detail || code); } if (code === 'image_invalid') { - return blnEn ? 'Invalid image capture.' : 'Image invalide.'; + return t('bibOcrImageInvalid'); } - var base = blnEn - ? 'Unreadable number. Frame a single number.' - : 'Numéro illisible. Cadrez un seul numéro.'; + var base = t('bibOcrUnreadable'); if (result && result.seen) { base += ' (' + result.seen + ')'; } else if (code && code !== 'no_number') { @@ -802,16 +802,16 @@ // Renvoie la valeur de l'attribut si elle est traduite, sinon le texte // integre (bilingue) — evite d'afficher une clef brute a l'usager. - function scanMsg(panel, attr, frText, enText) { + function scanMsg(panel, attr) { var value = panel ? panel.getAttribute(attr) : ''; if (value && !isRawI18nKey(value)) { return value; } - return (document.documentElement.lang === 'en') ? enText : frText; + return ''; } BibScanSession.prototype.showReady = function () { - this.setStatus(scanMsg(this.panel, 'data-msg-ready', 'Cadrez le numéro, puis touchez l\'image', 'Frame the number, then tap the image'), false); + this.setStatus(scanMsg(this.panel, 'data-msg-ready'), false); this.setCaptureReady(true); if (this.captureLabel) { this.captureLabel.hidden = false; @@ -838,11 +838,11 @@ return; } if (!self.video) { - self.setStatus(scanMsg(self.panel, 'data-msg-loading', 'Initialisation (1re fois)...', 'Initializing (first time)...'), true); + self.setStatus(scanMsg(self.panel, 'data-msg-loading'), true); return; } if (!cfg.bibOcrCloud && !self.workerReady) { - self.setStatus(scanMsg(self.panel, 'data-msg-loading', 'Initialisation (1re fois)...', 'Initializing (first time)...'), true); + self.setStatus(scanMsg(self.panel, 'data-msg-loading'), true); return; } self.runOcr(); @@ -917,7 +917,7 @@ input.classList.add('inscr-gestion-bib--proposed'); var blnSearch = this.isSearchContext(); if (blnSearch) { - var tplSearch = scanMsg(this.panel, 'data-msg-found-search', 'Numéro %s détecté — recherche...', 'Number %s detected — searching...'); + var tplSearch = scanMsg(this.panel, 'data-msg-found-search'); this.setStatus(tplSearch.replace('%s', strNum), true); this.stop(); var form = this.block.closest('form.inscr-gestion-form'); @@ -927,7 +927,7 @@ return; } input.focus(); - var tpl = scanMsg(this.panel, 'data-msg-found', 'Numéro %s détecté — validez avec OK.', 'Number %s detected — press OK.'); + var tpl = scanMsg(this.panel, 'data-msg-found'); this.setStatus(tpl.replace('%s', strNum), false); this.stop(); }; @@ -945,7 +945,7 @@ if (self.captureLabel) { self.captureLabel.hidden = true; } - self.setStatus(scanMsg(self.panel, 'data-msg-scanning', 'Lecture en cours...', 'Reading...'), true); + self.setStatus(scanMsg(self.panel, 'data-msg-scanning'), true); // MSIN-4401 — Chemin produit : photo → Google Vision (serveur). Plus fiable que Tesseract mobile. if (cfg.bibOcrCloud) { @@ -983,12 +983,7 @@ self.setStatus(bibOcrErrorMessage(result), false); // Laisse l'erreur visible (pas de flash) — disparait au prochain tap / ready. }).catch(function () { - self.setStatus( - (document.documentElement.lang === 'en') - ? 'Network / capture error.' - : 'Erreur réseau ou capture.', - false - ); + self.setStatus(t('bibOcrNetwork'), false); }).then(function () { self.ocrBusy = false; self.setCaptureReady(true); @@ -1014,7 +1009,7 @@ return; } var seen = String(rawText || '').replace(/\D/g, ''); - var base = scanMsg(self.panel, 'data-msg-invalid', 'Numéro illisible. Cadrez un seul numéro.', 'Unreadable number. Frame a single number.'); + var base = scanMsg(self.panel, 'data-msg-invalid'); if (seen) { base += ' (' + seen + ')'; } @@ -1025,7 +1020,7 @@ self.captureLabel.hidden = false; } }).catch(function () { - self.setStatus(scanMsg(self.panel, 'data-msg-invalid', 'Numéro illisible. Cadrez un seul numéro.', 'Unreadable number. Frame a single number.'), false); + self.setStatus(scanMsg(self.panel, 'data-msg-invalid'), false); self.ocrBusy = false; self.setCaptureReady(true); if (self.captureLabel) { @@ -1049,7 +1044,7 @@ BibScanSession.prototype.start = function () { var self = this; if (!self.panel || !navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) { - self.setStatus(scanMsg(self.panel, 'data-msg-camera', 'Impossible d\'accéder à la caméra.', 'Unable to access the camera.'), false); + self.setStatus(scanMsg(self.panel, 'data-msg-camera'), false); return; } if (activeScan && activeScan !== self) { @@ -1068,7 +1063,7 @@ self.scrollCaptureIntoView(); } }, 120); - self.setStatus(scanMsg(self.panel, 'data-msg-loading', 'Initialisation (1re fois)...', 'Initializing (first time)...'), true); + self.setStatus(scanMsg(self.panel, 'data-msg-loading'), true); var cameraPromise = navigator.mediaDevices.getUserMedia({ video: { @@ -1106,7 +1101,7 @@ // Apres affichage video : recentrer (layout final). self.scrollCaptureIntoView(); }).catch(function () { - self.setStatus(scanMsg(self.panel, 'data-msg-camera', 'Impossible d\'accéder à la caméra.', 'Unable to access the camera.'), false); + self.setStatus(scanMsg(self.panel, 'data-msg-camera'), false); self.stop(); }); }; diff --git a/js/v2/promoteur-hub.js b/js/v2/promoteur-hub.js index eed4e24..3e002f7 100644 --- a/js/v2/promoteur-hub.js +++ b/js/v2/promoteur-hub.js @@ -13,13 +13,15 @@ } var strAjaxUrl = cfg.ajaxUrl || ''; - var strWaitMsg = cfg.preloaderWait || 'Chargement…'; + var strWaitMsg = cfg.preloaderWait || ''; + var i18n = cfg.i18n || {}; + var strAjaxError = i18n.ajaxError || ''; var intHubLoaderPending = 0; // MSIN-4401 — Session expiree : recharger la page → formulaire de login. - // Pas de message "Non connecte" qui laisse l'usager bloque. + // Detection par code serveur (pas egalite de message FR). function fxHubRedirectIfSessionExpired(r) { - if (r && (r.code === 'session' || r.msg === 'Non connecté' || r.msg === 'Session expirée')) { + if (r && r.code === 'session') { window.location.reload(); return true; } @@ -68,7 +70,7 @@ return; } if (!r || !r.ok) { - $collapse.find('.promoteur-hub-stats-placeholder').text(r && r.msg ? r.msg : 'Erreur'); + $collapse.find('.promoteur-hub-stats-placeholder').text(r && r.msg ? r.msg : strAjaxError); return; } $collapse.find('.promoteur-hub-stats-placeholder').replaceWith(r.html || ''); @@ -78,7 +80,7 @@ $collapse.data('hub-loaded', 1); }) .fail(function () { - $collapse.find('.promoteur-hub-stats-placeholder').text('Erreur chargement'); + $collapse.find('.promoteur-hub-stats-placeholder').text(strAjaxError); }) .always(function () { fxHubHideLoader(); @@ -99,14 +101,14 @@ return; } if (!r || !r.ok) { - $collapse.find('.promoteur-hub-links-placeholder').text(r && r.msg ? r.msg : 'Erreur'); + $collapse.find('.promoteur-hub-links-placeholder').text(r && r.msg ? r.msg : strAjaxError); return; } $collapse.find('.promoteur-hub-links-placeholder').replaceWith(r.html || ''); $collapse.data('hub-links-loaded', 1); }) .fail(function () { - $collapse.find('.promoteur-hub-links-placeholder').text('Erreur chargement'); + $collapse.find('.promoteur-hub-links-placeholder').text(strAjaxError); }) .always(function () { fxHubHideLoader(); diff --git a/mobile.php b/mobile.php index ecaa675..5ce9e66 100644 --- a/mobile.php +++ b/mobile.php @@ -37,7 +37,7 @@ if (!isset($_SESSION['com_id'])) { $_SESSION['redirect_after_login'] = $_SERVER['REQUEST_URI']; $_SESSION['affiche_pas_connexion'] = 'true'; - $strMetaTitle = ($strLangue === 'fr') ? 'Inscriptions mobile' : 'Mobile registrations'; + $strMetaTitle = fxInscrGestionT('inscr_gestion_mobile_title'); $strMetaDescription = ''; $strMetaKeywords = ''; $blnBoutonRetour = false; @@ -108,40 +108,42 @@ if ($intInviteCom > 0 && $intEveForInvite > 0 . '&return=' . rawurlencode($strReturnPath); $strStayUrl = $vDomaine . ($strLangue === 'en' ? '/account' : '/compte'); - $strMetaTitle = ($strLangue === 'fr') ? 'Inscriptions mobile' : 'Mobile registrations'; + $strMetaTitle = fxInscrGestionT('inscr_gestion_mobile_title'); $strMetaDescription = ''; $strMetaKeywords = ''; $blnBoutonRetour = false; $footer_script = false; + $strMailPart = ($strTargetMail !== '') + ? ' (' . htmlspecialchars($strTargetMail, ENT_QUOTES, 'UTF-8') . ')' + : ''; + $strSignedAs = str_replace( + '%name%', + '' . htmlspecialchars($strSessionLabel, ENT_QUOTES, 'UTF-8') . '', + fxInscrGestionT('mobile_invite_conflict_signed_as') + ); + $strInviteFor = str_replace( + array('%name%', '%mail_part%'), + array( + '' . htmlspecialchars($strTargetLabel, ENT_QUOTES, 'UTF-8') . '', + $strMailPart, + ), + fxInscrGestionT('mobile_invite_conflict_for') + ); + require_once('inc_header.php'); ?>
@@ -172,7 +174,7 @@ if ($intEveId === 0 && count($tabEveIds) === 1) { exit; } -$strMetaTitle = ($strLangue === 'fr') ? 'Inscriptions mobile' : 'Mobile registrations'; +$strMetaTitle = fxInscrGestionT('inscr_gestion_mobile_title'); $strMetaDescription = ''; $strMetaKeywords = ''; $blnBoutonRetour = false; @@ -198,9 +200,7 @@ require_once('inc_header.php'); if ($intEveId === 0) { if (count($tabEveIds) === 0) { echo ''; } else { fxInscrGestionRenderEventPicker($tabEveIds, $strLangue, fxInscrGestionBaseUrl($strLangue, true)); diff --git a/php/inc_fx_eve_acces_promoteur.php b/php/inc_fx_eve_acces_promoteur.php index 02376f5..173525f 100644 --- a/php/inc_fx_eve_acces_promoteur.php +++ b/php/inc_fx_eve_acces_promoteur.php @@ -48,9 +48,17 @@ function fxEveAccesPromoteurT($strClef, $strLangue = 'fr') 'fr' => '0 = sans expiration. Utilisé surtout pour les nouveaux accès.', 'en' => '0 = no expiration. Mainly for new access grants.', ), + 'eve_acces_promoteur_invite_courriel_help' => array( + 'fr' => 'Saisissez d\'abord le courriel : on vérifie s\'il existe déjà un compte MS1 avant de demander le nom.', + 'en' => 'Enter the email first: we check if an MS1 account already exists before asking for a name.', + ), 'eve_acces_promoteur_btn_invite' => array('fr' => 'Assigner l\'accès', 'en' => 'Grant access'), 'eve_acces_promoteur_btn_resend' => array('fr' => 'Renvoyer le mail', 'en' => 'Resend email'), 'eve_acces_promoteur_btn_revoke' => array('fr' => 'Retirer', 'en' => 'Revoke'), + 'eve_acces_promoteur_confirm_revoke' => array( + 'fr' => 'Retirer cet accès ?', + 'en' => 'Revoke this access?', + ), 'eve_acces_promoteur_no_kits' => array( 'fr' => 'Aucun kit délégable disponible. Demandez à un super admin d\'activer « délégable » sur un kit.', 'en' => 'No delegable kit available. Ask a super admin to mark a kit as delegable.', @@ -100,6 +108,29 @@ function fxEveAccesPromoteurT($strClef, $strLangue = 'fr') 'fr' => 'Courriel invalide.', 'en' => 'Invalid email.', ), + 'eve_acces_promoteur_access_denied' => array('fr' => 'Accès refusé.', 'en' => 'Access denied.'), + 'eve_acces_promoteur_btn_back' => array('fr' => 'Retour', 'en' => 'Back'), + 'eve_acces_promoteur_mail_new_subject' => array( + 'fr' => 'Accès promoteur — créez votre mot de passe', + 'en' => 'Promoter access — set your password', + ), + 'eve_acces_promoteur_mail_new_body' => array( + 'fr' => 'Bonjour %prenom%,

Un accès vous a été accordé pour %eve_nom% (kit : %kit%).

Un compte a été créé avec le courriel %login%.
Cliquez ci-dessous pour choisir votre mot de passe.

Ensuite, connectez-vous ici pour travailler sur le terrain :
%mobile_url%', + 'en' => 'Hello %prenom%,

You were granted access to %eve_nom% (kit: %kit%).

An account was created with %login%.
Click below to set your password.

Then sign in here for field use:
%mobile_url%', + ), + 'eve_acces_promoteur_mail_new_cta' => array('fr' => 'Choisir mon mot de passe', 'en' => 'Set my password'), + 'eve_acces_promoteur_mail_existing_subject' => array( + 'fr' => 'Nouvel accès — %eve_nom%', + 'en' => 'New access — %eve_nom%', + ), + 'eve_acces_promoteur_mail_existing_body' => array( + 'fr' => 'Bonjour %prenom%,

Un accès vous a été accordé pour %eve_nom% (kit : %kit%).

Ouvrez le lien ci-dessous et connectez-vous avec le compte %login% (si un autre compte est déjà ouvert, vous serez invité à vous déconnecter).', + 'en' => 'Hello %prenom%,

You were granted access to %eve_nom% (kit: %kit%).

Open the link below and sign in as %login% (if another account is already open, you will be asked to sign out).', + ), + 'eve_acces_promoteur_mail_existing_cta' => array( + 'fr' => 'Ouvrir l\'accès (mobile)', + 'en' => 'Open access (mobile)', + ), ); $strLang = ($strLangue === 'en') ? 'en' : 'fr'; if (isset($arrFallback[$strClef][$strLang])) { @@ -294,53 +325,26 @@ function fxEveAccesPromoteurSendInviteMail($arrCompte, $arrEve, $arrRole, $strLa } $strMobileUrl = fxEveAccesPromoteurAppendInviteParams($strMobileUrl, $intComId, $intEveId); + // Placeholders Info (contenu HTML géré dans table info). + $arrMailVars = array( + '%prenom%' => htmlspecialchars($strPrenom, ENT_QUOTES, 'UTF-8'), + '%eve_nom%' => htmlspecialchars($strEveNom, ENT_QUOTES, 'UTF-8'), + '%kit%' => htmlspecialchars($strKit, ENT_QUOTES, 'UTF-8'), + '%login%' => htmlspecialchars((string)$strLogin, ENT_QUOTES, 'UTF-8'), + '%mobile_url%' => htmlspecialchars($strMobileUrl, ENT_QUOTES, 'UTF-8'), + ); + if ($blnNewAccount && $strResetToken !== '') { - if ($strLangue === 'fr') { - $subject = 'Accès promoteur — créez votre mot de passe'; - $message = 'Bonjour ' . $strPrenom . ',

' - . 'Un accès vous a été accordé pour ' . htmlspecialchars($strEveNom, ENT_QUOTES, 'UTF-8') . '' - . ' (kit : ' . htmlspecialchars($strKit, ENT_QUOTES, 'UTF-8') . ').

' - . 'Un compte a été créé avec le courriel ' . htmlspecialchars($strLogin, ENT_QUOTES, 'UTF-8') . '.
' - . 'Cliquez ci-dessous pour choisir votre mot de passe.

' - . 'Ensuite, connectez-vous ici pour travailler sur le terrain :
' - . '' - . htmlspecialchars($strMobileUrl, ENT_QUOTES, 'UTF-8') . ''; - $attachment_label = 'Choisir mon mot de passe'; - } else { - $subject = 'Promoter access — set your password'; - $message = 'Hello ' . $strPrenom . ',

' - . 'You were granted access to ' . htmlspecialchars($strEveNom, ENT_QUOTES, 'UTF-8') . '' - . ' (kit: ' . htmlspecialchars($strKit, ENT_QUOTES, 'UTF-8') . ').

' - . 'An account was created with ' . htmlspecialchars($strLogin, ENT_QUOTES, 'UTF-8') . '.
' - . 'Click below to set your password.

' - . 'Then sign in here for field use:
' - . '' - . htmlspecialchars($strMobileUrl, ENT_QUOTES, 'UTF-8') . ''; - $attachment_label = 'Set my password'; - } + $subject = strtr(fxEveAccesPromoteurT('eve_acces_promoteur_mail_new_subject', $strLangue), $arrMailVars); + $message = strtr(fxEveAccesPromoteurT('eve_acces_promoteur_mail_new_body', $strLangue), $arrMailVars); + $attachment_label = fxEveAccesPromoteurT('eve_acces_promoteur_mail_new_cta', $strLangue); $attachment_url = $vDomaine . $strComptePath . '/reset?t=' . urlencode($strResetToken) . '&m=' . urlencode($arrCompte['com_courriel']) . '&id=' . intval($arrCompte['com_id']); } else { - if ($strLangue === 'fr') { - $subject = 'Nouvel accès — ' . $strEveNom; - $message = 'Bonjour ' . $strPrenom . ',

' - . 'Un accès vous a été accordé pour ' . htmlspecialchars($strEveNom, ENT_QUOTES, 'UTF-8') . '' - . ' (kit : ' . htmlspecialchars($strKit, ENT_QUOTES, 'UTF-8') . ').

' - . 'Ouvrez le lien ci-dessous et connectez-vous avec le compte ' - . '' . htmlspecialchars($strLogin, ENT_QUOTES, 'UTF-8') . ' ' - . '(si un autre compte est déjà ouvert, vous serez invité à vous déconnecter).'; - $attachment_label = 'Ouvrir l\'accès (mobile)'; - } else { - $subject = 'New access — ' . $strEveNom; - $message = 'Hello ' . $strPrenom . ',

' - . 'You were granted access to ' . htmlspecialchars($strEveNom, ENT_QUOTES, 'UTF-8') . '' - . ' (kit: ' . htmlspecialchars($strKit, ENT_QUOTES, 'UTF-8') . ').

' - . 'Open the link below and sign in as ' - . '' . htmlspecialchars($strLogin, ENT_QUOTES, 'UTF-8') . ' ' - . '(if another account is already open, you will be asked to sign out).'; - $attachment_label = 'Open access (mobile)'; - } + $subject = strtr(fxEveAccesPromoteurT('eve_acces_promoteur_mail_existing_subject', $strLangue), $arrMailVars); + $message = strtr(fxEveAccesPromoteurT('eve_acces_promoteur_mail_existing_body', $strLangue), $arrMailVars); + $attachment_label = fxEveAccesPromoteurT('eve_acces_promoteur_mail_existing_cta', $strLangue); $attachment_url = $strMobileUrl; } @@ -739,11 +743,7 @@ function fxEveAccesPromoteurRenderPage($intEveId, $strLangue = 'fr', $strFlash = echo '
'; echo '
'; echo '

' - . fxEveAccesPromoteurEsc( - $strLangue === 'en' - ? 'Enter the email first: we check if an MS1 account already exists before asking for a name.' - : 'Saisissez d\'abord le courriel : on vérifie s\'il existe déjà un compte MS1 avant de demander le nom.' - ) + . fxEveAccesPromoteurEsc(fxEveAccesPromoteurT('eve_acces_promoteur_invite_courriel_help', $strLangue)) . '

'; echo ''; fxEveAccesPromoteurRenderInviteScript(); @@ -825,7 +825,7 @@ function fxEveAccesPromoteurRenderPage($intEveId, $strLangue = 'fr', $strFlash = echo ''; echo '
'; echo ''; echo ''; diff --git a/php/inc_fx_inscriptions_gestion.php b/php/inc_fx_inscriptions_gestion.php index 5c36985..d32bf7e 100644 --- a/php/inc_fx_inscriptions_gestion.php +++ b/php/inc_fx_inscriptions_gestion.php @@ -71,6 +71,11 @@ function fxInscrGestionCanDo($intComId, $intEveId, $strActionPerm) { return false; } + // MSIN-4401 — kit propriétaire / total (grants_all) = toutes les actions, sans cocher chaque droit. + if (fxEveAccesComEventHasGrantsAllKit(intval($intComId), intval($intEveId))) { + return true; + } + if (fxEveAccesHasPermission(intval($intComId), intval($intEveId), $strActionPerm)) { return true; } @@ -400,6 +405,53 @@ function fxInscrGestionFallbackTexte($strClef, $strLangue) { 'inscr_gestion_rech_statut' => $blnFr ? 'Statut de course' : 'Race status', 'inscr_gestion_rech_statut_all' => $blnFr ? 'Tous les statuts' : 'All statuses', 'inscr_gestion_rech_statut_n' => $blnFr ? '%d sélectionnés' : '%d selected', + 'inscr_gestion_mobile_title' => $blnFr ? 'Inscriptions mobile' : 'Mobile registrations', + 'inscr_gestion_login_user' => $blnFr ? 'Nom d\'utilisateur' : 'Username', + 'inscr_gestion_login_pass' => $blnFr ? 'Mot de passe' : 'Password', + 'inscr_gestion_login_btn' => $blnFr ? 'Connexion' : 'Sign in', + 'inscr_gestion_login_intro' => $blnFr + ? 'Connectez-vous pour accéder aux inscriptions mobile.' + : 'Sign in to access mobile registrations.', + 'inscr_gestion_login_err_user' => $blnFr + ? 'Veuillez entrer votre nom d\'usager' + : 'Please enter your username', + 'inscr_gestion_login_err_pass' => $blnFr + ? 'Veuillez entrer votre mot de passe' + : 'Please enter your password', + 'inscr_gestion_choose_event' => $blnFr ? 'Choisir un événement' : 'Choose an event', + 'inscr_gestion_back_events' => $blnFr ? 'Événements' : 'Events', + 'tableau_promoteur_menueinscription_v2' => $blnFr ? 'Inscriptions V2' : 'Registrations V2', + 'mobile_no_acces_v2' => $blnFr + ? 'Aucun accès inscriptions mobile (v2) n\'est actif pour ce compte.' + : 'No active mobile registration access (v2) for this account.', + 'mobile_invite_conflict_signed_as' => $blnFr + ? 'Vous êtes actuellement connecté en tant que %name%.' + : 'You are currently signed in as %name%.', + 'mobile_invite_conflict_for' => $blnFr + ? 'Cette invitation concerne le compte %name%%mail_part%. Déconnectez-vous d\'abord, puis reconnectez-vous avec ce compte.' + : 'This invitation is for %name%%mail_part%. Sign out first, then sign in with that account.', + 'mobile_invite_conflict_btn_logout' => $blnFr + ? 'Se déconnecter et continuer' + : 'Sign out and continue', + 'mobile_invite_conflict_btn_stay' => $blnFr + ? 'Rester sur mon compte actuel' + : 'Stay on my current account', + 'inscr_gestion_bib_scan_found_search' => $blnFr + ? 'Numéro %s détecté — recherche...' + : 'Number %s detected — searching...', + 'inscr_gestion_bib_ocr_not_configured' => $blnFr + ? 'OCR non configuré (clé Vision absente sur le serveur).' + : 'OCR not configured (Vision API key missing on server).', + 'inscr_gestion_bib_ocr_vision_prefix' => $blnFr ? 'Erreur Vision : ' : 'Vision error: ', + 'inscr_gestion_bib_ocr_image_invalid' => $blnFr ? 'Image invalide.' : 'Invalid image capture.', + 'inscr_gestion_bib_ocr_network' => $blnFr ? 'Erreur réseau ou capture.' : 'Network or capture error.', + 'inscr_gestion_err_load' => $blnFr ? 'Erreur de chargement.' : 'Loading error.', + 'inscr_gestion_err_network' => $blnFr ? 'Erreur réseau.' : 'Network error.', + 'inscr_gestion_err_refund' => $blnFr ? 'Erreur remboursement.' : 'Refund error.', + 'inscr_gestion_refund_ok' => $blnFr ? 'Remboursement effectué.' : 'Refund completed.', + 'inscr_gestion_refund_cancel_hint' => $blnFr + ? 'Votre transaction a été remboursée. Si vous voulez aussi l\'annuler dans le système d\'inscription :' + : 'Your transaction has been refunded. If you also want to cancel it in the registration system:', ); } @@ -476,37 +528,33 @@ function fxInscrGestionLoginActionUrl($strLangue) { function fxInscrGestionRenderLoginPage($strLangue) { $strActionUrl = fxInscrGestionLoginActionUrl($strLangue); - $strLblUser = ($strLangue === 'fr') ? 'Nom d\'utilisateur' : 'Username'; - $strLblPass = ($strLangue === 'fr') ? 'Mot de passe' : 'Password'; - $strBtn = ($strLangue === 'fr') ? 'Connexion' : 'Sign in'; - $strIntro = ($strLangue === 'fr') - ? 'Connectez-vous pour accéder aux inscriptions mobile.' - : 'Sign in to access mobile registrations.'; echo '
'; } -/** Libellé menu promoteur V2 (même texte que l'ancien + suffixe V2). */ +/** Libellé menu promoteur V2 (Info). */ function fxInscrGestionPromoteurMenuLabel() { - return afficheTexte('tableau_promoteur_menueinscription', 0) . ' V2'; + return fxInscrGestionT('tableau_promoteur_menueinscription_v2'); } function fxInscrGestionListBackLabel($strLangue) { if (fxInscrGestionIsEntry()) { - return ($strLangue === 'fr') ? 'Événements' : 'Events'; + return fxInscrGestionT('inscr_gestion_back_events'); } return fxInscrGestionT('promoteur_back'); @@ -1437,7 +1485,7 @@ function fxInscrGestionResolveQrScan($strRaw, $intEveId, $strLangue) { function fxInscrGestionRenderEventPicker($tabEveIds, $strLangue, $strBaseUrl) { global $objDatabase; - $strTitle = ($strLangue === 'fr') ? 'Choisir un événement' : 'Choose an event'; + $strTitle = fxInscrGestionT('inscr_gestion_choose_event'); echo '
'; fxInscrGestionRenderPageHead($strTitle); @@ -1629,23 +1677,24 @@ function fxInscrGestionVisionReadBibNumber($strImageBase64) { function fxInscrGestionRenderBibScanBlock($strBibInputId, $strLangue) { echo '
'; + echo ' data-msg-scanning="' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_bib_scan_scanning')) . '"'; + echo ' data-msg-loading="' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_bib_scan_loading')) . '"'; + echo ' data-msg-ready="' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_bib_scan_ready')) . '"'; + echo ' data-msg-found="' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_bib_scan_found')) . '"'; + echo ' data-msg-found-search="' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_bib_scan_found_search')) . '"'; + echo ' data-msg-invalid="' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_bib_scan_invalid')) . '"'; + echo ' data-msg-camera="' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_qr_camera_error')) . '">'; echo '

' - . fxInscrGestionEsc(fxInscrGestionFallbackTexte('inscr_gestion_bib_scan_hint', $strLangue)) . '

'; + . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_bib_scan_hint')) . '

'; echo '
'; echo '
'; echo '
'; // Zone-cible centrale (eclaircie) : aligne le repere visuel sur la region // reellement lue par l'OCR (captureCenterFrame : ~55% largeur x 30% hauteur). echo ''; - // Pastille courte (fallback integre) — evite un long texte info qui masque le viseur. + // Pastille courte — texte Info (inscr_gestion_bib_scan_tap). echo '' - . fxInscrGestionEsc(fxInscrGestionFallbackTexte('inscr_gestion_bib_scan_tap', $strLangue)) . ''; + . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_bib_scan_tap')) . ''; echo '
'; echo '
'; } diff --git a/php/inc_fx_promoteur_hub.php b/php/inc_fx_promoteur_hub.php index 5981c28..e0d0dbe 100644 --- a/php/inc_fx_promoteur_hub.php +++ b/php/inc_fx_promoteur_hub.php @@ -463,9 +463,14 @@ function fxPromoteurHubStaticFallback($clef, $strLangParam = '') $strLangParam = !empty($strLangue) ? $strLangue : 'fr'; } $lng = ($strLangParam === 'en') ? 'en' : 'fr'; + // MSIN-4401 — filet si Info absente (jamais la source de vérité). $tab = array( 'promoteur_hub_section_gestion' => array('fr' => 'Gestion', 'en' => 'Management'), 'promoteur_hub_link_dashboard' => array('fr' => 'Tableau de bord V2', 'en' => 'V2 dashboard'), + 'promoteur_hub_no_features' => array( + 'fr' => 'Aucune fonctionnalité disponible avec vos droits actuels.', + 'en' => 'No features available with your current permissions.', + ), 'promoteur_hub_menu' => array('fr' => 'Tableau de bord V2', 'en' => 'V2 dashboard'), 'tableau_promoteur_menurapports' => array('fr' => 'Rapports', 'en' => 'Reports'), 'tableau_promoteur_menudossard' => array('fr' => 'Dossards', 'en' => 'Bibs'), @@ -474,6 +479,48 @@ function fxPromoteurHubStaticFallback($clef, $strLangParam = '') 'tableau_promoteur_menueinscription' => array('fr' => 'Inscriptions', 'en' => 'Registrations'), 'tableau_promoteur_rabais' => array('fr' => 'Rabais', 'en' => 'Discounts'), 'tableau_promoteur_envoie_email_questions' => array('fr' => 'Envoi courriel questions', 'en' => 'Send question emails'), + 'promoteur_hub_load_menu' => array('fr' => 'Chargement du menu…', 'en' => 'Loading menu…'), + 'promoteur_hub_load_dashboard' => array('fr' => 'Chargement du tableau de bord…', 'en' => 'Loading dashboard…'), + 'promoteur_hub_epreuves_title' => array('fr' => 'Épreuves en vente', 'en' => 'Events for sale'), + 'promoteur_hub_produits_title' => array('fr' => 'Produits en vente', 'en' => 'Products for sale'), + 'promoteur_hub_bibs_sold' => array('fr' => 'dossards vendus', 'en' => 'bibs sold'), + 'promoteur_hub_restant' => array('fr' => 'restant', 'en' => 'left'), + 'promoteur_hub_unlimited' => array('fr' => 'illimité', 'en' => 'unlimited'), + 'promoteur_hub_prix_courant' => array('fr' => 'Prix courant', 'en' => 'Current price'), + 'promoteur_hub_prochain_prix' => array('fr' => 'Prochain prix', 'en' => 'Next price'), + 'promoteur_hub_des' => array('fr' => 'dès', 'en' => 'on'), + 'promoteur_hub_dossards_remis' => array('fr' => 'Dossards remis', 'en' => 'Bibs handed out'), + 'promoteur_hub_liste_active' => array('fr' => 'liste active', 'en' => 'list active'), + 'promoteur_hub_waiting_n' => array('fr' => 'en attente', 'en' => 'waiting'), + 'promoteur_hub_waiting_list' => array('fr' => 'Liste d\'attente', 'en' => 'Waiting list'), + 'promoteur_hub_disponible' => array('fr' => 'disponible(s)', 'en' => 'available'), + 'promoteur_hub_vendus_title' => array('fr' => 'Vendus', 'en' => 'Sold'), + 'promoteur_hub_vendus_lbl' => array('fr' => 'vendu(s)', 'en' => 'sold'), + 'promoteur_hub_paniers_title' => array('fr' => 'Dans des paniers incomplets', 'en' => 'In incomplete carts'), + 'promoteur_hub_paniers_lbl' => array('fr' => 'panier(s)', 'en' => 'cart(s)'), + 'promoteur_hub_menu_features' => array('fr' => 'Menu des fonctionnalités', 'en' => 'Features menu'), + 'promoteur_hub_flash_inscrits' => array('fr' => 'inscrits', 'en' => 'registered'), + 'promoteur_hub_flash_rempli' => array('fr' => 'rempli', 'en' => 'filled'), + 'promoteur_hub_flash_places' => array('fr' => 'places restantes', 'en' => 'spots left'), + 'promoteur_hub_flash_revenus' => array('fr' => 'revenus est.', 'en' => 'est. revenue'), + 'promoteur_hub_badge_completes' => array('fr' => 'complète(s)', 'en' => 'sold out'), + 'promoteur_hub_badge_presque' => array('fr' => 'presque pleine(s)', 'en' => 'almost full'), + 'promoteur_hub_badge_attente' => array('fr' => 'en attente', 'en' => 'waiting'), + 'promoteur_hub_badge_limite' => array('fr' => 'limite', 'en' => 'deadline'), + 'promoteur_hub_ajax_session' => array('fr' => 'Session expirée', 'en' => 'Session expired'), + 'promoteur_hub_ajax_invalid' => array('fr' => 'Requête invalide', 'en' => 'Invalid request'), + 'promoteur_hub_ajax_denied' => array('fr' => 'Accès refusé', 'en' => 'Access denied'), + 'promoteur_hub_ajax_not_found' => array('fr' => 'Événement introuvable', 'en' => 'Event not found'), + 'promoteur_hub_ajax_error' => array('fr' => 'Erreur', 'en' => 'Error'), + 'promoteur_hub_rapport_chrono' => array('fr' => 'chrono + questions', 'en' => 'chrono + questions'), + 'promoteur_hub_rapport_finances' => array('fr' => 'Transactions-Finances', 'en' => 'Transactions-Finances'), + 'promoteur_hub_rapport_finances_all' => array('fr' => 'Transactions-Finances-Tout', 'en' => 'Transactions-Finances-All'), + 'promoteur_hub_rapport_finances_adh' => array('fr' => 'Transactions-Finances-Adhésions Seul', 'en' => 'Transactions-Finances-Memberships only'), + 'promoteur_hub_rapport_finances_eve' => array('fr' => 'Transactions-Finances-Événements Seul', 'en' => 'Transactions-Finances-Events only'), + 'promoteur_hub_rapport_adhesions' => array('fr' => 'adhésions actives', 'en' => 'active memberships'), + 'promoteur_hub_rapport_adhesions_annuelles' => array('fr' => 'liste d\'adhésions annuelles', 'en' => 'annual memberships list'), + 'promoteur_hub_rapport_finances_capitaines' => array('fr' => 'Transactions-Finances (capitaines seulement)', 'en' => 'Transactions-Finances (captains only)'), + 'promoteur_hub_rapport_paiements' => array('fr' => 'Paiements', 'en' => 'Payments'), ); return $tab[$clef][$lng] ?? ''; @@ -555,38 +602,74 @@ function fxPromoteurHubGetEventLinks($arrItem, $strLangue) // ---- Rapports (niveau 1 V2 : chrono / finances / paiements) ---- if ($intTeId != 9 && $fnCan(array('reports.chrono'))) { - $arrRapports[] = array('label' => 'chrono + questions', 'url' => $strRapport . 'chrono&e=' . $strE . '&lng=' . $strLangue, 'target' => '_blank'); + $arrRapports[] = array( + 'label' => fxPromoteurHubTexte('promoteur_hub_rapport_chrono', 0), + 'url' => $strRapport . 'chrono&e=' . $strE . '&lng=' . $strLangue, + 'target' => '_blank', + ); } if ($fnCan(array('reports.finances', 'reports.finances_membership', 'reports.finances_events'))) { if ($intTeId == 13) { - $arrRapports[] = array('label' => 'Transactions-Finances-Tout', 'url' => $strRapport . 'finances-membership&e=' . $strE . '&lng=' . $strLangue . '&a-ach_maj_from=' . urlencode($strDateTq) . '&btn_search=', 'target' => '_blank'); + $arrRapports[] = array( + 'label' => fxPromoteurHubTexte('promoteur_hub_rapport_finances_all', 0), + 'url' => $strRapport . 'finances-membership&e=' . $strE . '&lng=' . $strLangue . '&a-ach_maj_from=' . urlencode($strDateTq) . '&btn_search=', + 'target' => '_blank', + ); } else { - $arrRapports[] = array('label' => 'Transactions-Finances', 'url' => $strRapport . 'finances&e=' . $strE . '&lng=' . $strLangue, 'target' => '_blank'); + $arrRapports[] = array( + 'label' => fxPromoteurHubTexte('promoteur_hub_rapport_finances', 0), + 'url' => $strRapport . 'finances&e=' . $strE . '&lng=' . $strLangue, + 'target' => '_blank', + ); } } if ($intTeId == 13 && $fnCan(array('reports.finances_membership', 'reports.finances_events', 'reports.adhesions', 'reports.adhesions_sales'))) { if ($fnCan(array('reports.finances_membership'))) { - $arrRapports[] = array('label' => 'Transactions-Finances-Adhésions Seul', 'url' => $strRapport . 'finances-membership-tq&e=' . $strE . '&lng=' . $strLangue . '&a-ach_maj_from=' . urlencode($strDateTq) . '&btn_search=', 'target' => '_blank'); + $arrRapports[] = array( + 'label' => fxPromoteurHubTexte('promoteur_hub_rapport_finances_adh', 0), + 'url' => $strRapport . 'finances-membership-tq&e=' . $strE . '&lng=' . $strLangue . '&a-ach_maj_from=' . urlencode($strDateTq) . '&btn_search=', + 'target' => '_blank', + ); } if ($fnCan(array('reports.finances_events'))) { - $arrRapports[] = array('label' => 'Transactions-Finances-Événements Seul', 'url' => $strRapport . 'finances-membership-autre&e=' . $strE . '&lng=' . $strLangue . '&a-ach_maj_from=' . urlencode($strDateTq) . '&btn_search=', 'target' => '_blank'); + $arrRapports[] = array( + 'label' => fxPromoteurHubTexte('promoteur_hub_rapport_finances_eve', 0), + 'url' => $strRapport . 'finances-membership-autre&e=' . $strE . '&lng=' . $strLangue . '&a-ach_maj_from=' . urlencode($strDateTq) . '&btn_search=', + 'target' => '_blank', + ); } if ($fnCan(array('reports.adhesions'))) { - $arrRapports[] = array('label' => 'adhésions actives', 'url' => $strRapport . 'adhesions&e=' . $strE . '&lng=' . $strLangue, 'target' => '_blank'); + $arrRapports[] = array( + 'label' => fxPromoteurHubTexte('promoteur_hub_rapport_adhesions', 0), + 'url' => $strRapport . 'adhesions&e=' . $strE . '&lng=' . $strLangue, + 'target' => '_blank', + ); } if ($fnCan(array('reports.adhesions_sales'))) { - $arrRapports[] = array('label' => "liste d'adhésions annuelles", 'url' => $strRapport . 'adhesions-ventes&e=' . $strE . '&lng=' . $strLangue, 'target' => '_blank'); + $arrRapports[] = array( + 'label' => fxPromoteurHubTexte('promoteur_hub_rapport_adhesions_annuelles', 0), + 'url' => $strRapport . 'adhesions-ventes&e=' . $strE . '&lng=' . $strLangue, + 'target' => '_blank', + ); } } if (intval($arrItem['rapport_capitaines'] ?? 0) == 1 && $fnCan(array('reports.finances_captains'))) { - $arrRapports[] = array('label' => 'Transactions-Finances (capitaines seulement)', 'url' => $strRapport . 'finances-pnq&e=' . $strE . '&lng=' . $strLangue, 'target' => '_blank'); + $arrRapports[] = array( + 'label' => fxPromoteurHubTexte('promoteur_hub_rapport_finances_capitaines', 0), + 'url' => $strRapport . 'finances-pnq&e=' . $strE . '&lng=' . $strLangue, + 'target' => '_blank', + ); } if ($fnCan(array('reports.payments'))) { - $arrRapports[] = array('label' => ($strLangue === 'fr' ? 'Paiements' : 'Payments'), 'url' => $strRapport . 'remboursements&e=' . $strE . '&lng=' . $strLangue, 'target' => '_blank'); + $arrRapports[] = array( + 'label' => fxPromoteurHubTexte('promoteur_hub_rapport_paiements', 0), + 'url' => $strRapport . 'remboursements&e=' . $strE . '&lng=' . $strLangue, + 'target' => '_blank', + ); } if (($blnLegacyMenus || $blnGrantsAll || $blnSuper || $fnCan(array('reports.custom'))) && function_exists('fxGetMenussuperadm')) { @@ -694,11 +777,8 @@ function fxPromoteurHubRenderEventPanelContent($arrItem, $strLangue) // MSIN-4401 — pas d'en-têtes vides si aucun droit dans la section if (!$blnGestion && !$blnRapports) { - $blnFr = ($strLangue !== 'en'); return '

' - . fxPromoteurHubEsc($blnFr - ? 'Aucune fonctionnalité disponible avec vos droits actuels.' - : 'No features available with your current permissions.') + . fxPromoteurHubEsc(fxPromoteurHubTexte('promoteur_hub_no_features', 0)) . '

'; } @@ -732,8 +812,7 @@ function fxPromoteurHubRenderEventPanelShell($arrItem, $strLangue) { $strPanelId = 'hub-fn-' . intval($arrItem['eve_id']); $intEveId = intval($arrItem['eve_id']); - $blnFr = ($strLangue !== 'en'); - $strLoadMsg = $blnFr ? 'Chargement du menu…' : 'Loading menu…'; + $strLoadMsg = fxPromoteurHubTexte('promoteur_hub_load_menu', 0); global $vDomaine; $strRunnerSrc = $vDomaine . '/images/ms1-runner-loader.gif?v=' . _VERSION_CODE; @@ -1074,13 +1153,8 @@ function fxPromoteurHubRenderFlash($arrStats, $strLangue = 'fr') return ''; } - $blnFr = ($strLangue !== 'en'); $arrFlash = $arrStats['flash']; - $lbl = function ($fr, $en) use ($blnFr) { - return $blnFr ? $fr : $en; - }; - $html = ''; // ---- Bande flash ---- @@ -1088,25 +1162,25 @@ function fxPromoteurHubRenderFlash($arrStats, $strLangue = 'fr') $html .= '
'; $html .= '' . intval($arrFlash['total_vendus']) . ''; - $html .= '' . fxPromoteurHubEsc($lbl('inscrits', 'registered')) . ''; + $html .= '' . fxPromoteurHubEsc(fxPromoteurHubTexte('promoteur_hub_flash_inscrits', 0)) . ''; $html .= '
'; if (intval($arrFlash['total_max']) > 0) { $html .= '
'; $html .= '' . intval($arrFlash['pct_global']) . '%'; - $html .= '' . fxPromoteurHubEsc($lbl('rempli', 'filled')) . ''; + $html .= '' . fxPromoteurHubEsc(fxPromoteurHubTexte('promoteur_hub_flash_rempli', 0)) . ''; $html .= '
'; $html .= '
'; $html .= '' . intval($arrFlash['total_restant']) . ''; - $html .= '' . fxPromoteurHubEsc($lbl('places restantes', 'spots left')) . ''; + $html .= '' . fxPromoteurHubEsc(fxPromoteurHubTexte('promoteur_hub_flash_places', 0)) . ''; $html .= '
'; } if (floatval($arrFlash['revenus']) > 0) { $html .= '
'; $html .= '' . fxPromoteurHubEsc(fxShowPrix($arrFlash['revenus'], $strLangue)) . ''; - $html .= '' . fxPromoteurHubEsc($lbl('revenus est.', 'est. revenue')) . ''; + $html .= '' . fxPromoteurHubEsc(fxPromoteurHubTexte('promoteur_hub_flash_revenus', 0)) . ''; $html .= '
'; } @@ -1116,19 +1190,19 @@ function fxPromoteurHubRenderFlash($arrStats, $strLangue = 'fr') $arrBadges = array(); if (intval($arrFlash['nb_completes']) > 0) { $arrBadges[] = array('cls' => 'phub-badge-danger', 'icon' => 'fa-ban', - 'txt' => intval($arrFlash['nb_completes']) . ' ' . $lbl('complète(s)', 'sold out')); + 'txt' => intval($arrFlash['nb_completes']) . ' ' . fxPromoteurHubTexte('promoteur_hub_badge_completes', 0)); } if (intval($arrFlash['nb_presque']) > 0) { $arrBadges[] = array('cls' => 'phub-badge-warn', 'icon' => 'fa-exclamation-triangle', - 'txt' => intval($arrFlash['nb_presque']) . ' ' . $lbl('presque pleine(s)', 'almost full')); + 'txt' => intval($arrFlash['nb_presque']) . ' ' . fxPromoteurHubTexte('promoteur_hub_badge_presque', 0)); } if (intval($arrFlash['total_attente']) > 0) { $arrBadges[] = array('cls' => 'phub-badge-info', 'icon' => 'fa-hourglass-half', - 'txt' => intval($arrFlash['total_attente']) . ' ' . $lbl('en attente', 'waiting')); + 'txt' => intval($arrFlash['total_attente']) . ' ' . fxPromoteurHubTexte('promoteur_hub_badge_attente', 0)); } if ($arrFlash['prochaine_date_limite'] !== '') { $arrBadges[] = array('cls' => 'phub-badge-soft', 'icon' => 'fa-clock-o', - 'txt' => $lbl('limite', 'deadline') . ' ' . fxShowDate($arrFlash['prochaine_date_limite'], $strLangue, 1)); + 'txt' => fxPromoteurHubTexte('promoteur_hub_badge_limite', 0) . ' ' . fxShowDate($arrFlash['prochaine_date_limite'], $strLangue, 1)); } if (!empty($arrBadges)) { $html .= '
'; @@ -1151,12 +1225,10 @@ function fxPromoteurHubRenderEpreuvesGrid($arrStats, $strLangue = 'fr') return ''; } - $blnFr = ($strLangue !== 'en'); - // ---- Titre de section ---- $html = '
' . '' - . fxPromoteurHubEsc($blnFr ? 'Épreuves en vente' : 'Events for sale') . '
'; + . fxPromoteurHubEsc(fxPromoteurHubTexte('promoteur_hub_epreuves_title', 0)) . ''; // ---- Tuiles par epreuve ---- $html .= '
'; @@ -1182,45 +1254,45 @@ function fxPromoteurHubRenderEpreuvesGrid($arrStats, $strLangue = 'fr') if (!$arrEpr['illimitee']) { $html .= '/ ' . intval($arrEpr['max']) . ''; } - $html .= '' . fxPromoteurHubEsc($blnFr ? 'dossards vendus' : 'bibs sold') . ''; + $html .= '' . fxPromoteurHubEsc(fxPromoteurHubTexte('promoteur_hub_bibs_sold', 0)) . ''; $html .= '
'; // Barre de progression if (!$arrEpr['illimitee'] && intval($arrEpr['max']) > 0) { $html .= '
'; $html .= '
' . intval($arrEpr['pct']) . '% · ' - . intval($arrEpr['restant']) . ' ' . fxPromoteurHubEsc($blnFr ? 'restant' : 'left') . '
'; + . intval($arrEpr['restant']) . ' ' . fxPromoteurHubEsc(fxPromoteurHubTexte('promoteur_hub_restant', 0)) . '
'; } else { $html .= '
' - . fxPromoteurHubEsc($blnFr ? 'illimité' : 'unlimited') . '
'; + . fxPromoteurHubEsc(fxPromoteurHubTexte('promoteur_hub_unlimited', 0)) . '
'; } // Pastilles secondaires $html .= '
'; if ($arrEpr['prix_courant'] !== null) { - $html .= '' + $html .= '' . '' . fxPromoteurHubEsc(fxShowPrix($arrEpr['prix_courant'], $strLangue)) . ''; if ($arrEpr['prix_prochain'] !== null && $arrEpr['prix_date_fin'] !== '' && $arrEpr['prix_date_fin'] >= date('Y-m-d')) { - $html .= '' + $html .= '' . '' . fxPromoteurHubEsc(fxShowPrix($arrEpr['prix_prochain'], $strLangue)) - . ' ' . fxPromoteurHubEsc($blnFr ? 'dès' : 'on') . ' ' + . ' ' . fxPromoteurHubEsc(fxPromoteurHubTexte('promoteur_hub_des', 0)) . ' ' . fxShowDate($arrEpr['prix_date_fin'], $strLangue, 1) . ''; } } if (intval($arrEpr['checkin']) > 0) { - $html .= '' + $html .= '' . '' . intval($arrEpr['checkin']) . ''; } if (intval($arrEpr['attente_nb']) > 0 || $arrEpr['attente_active']) { $strAtt = $arrEpr['attente_active'] && intval($arrEpr['attente_nb']) === 0 - ? ($blnFr ? 'liste active' : 'list active') - : intval($arrEpr['attente_nb']) . ' ' . ($blnFr ? 'en attente' : 'waiting'); - $html .= '' + ? fxPromoteurHubTexte('promoteur_hub_liste_active', 0) + : intval($arrEpr['attente_nb']) . ' ' . fxPromoteurHubTexte('promoteur_hub_waiting_n', 0); + $html .= '' . '' . fxPromoteurHubEsc($strAtt) . ''; } @@ -1335,12 +1407,10 @@ function fxPromoteurHubRenderProduits($arrProduits, $strLangue = 'fr') return ''; } - $blnFr = ($strLangue !== 'en'); - $html = '
'; $html .= '
' . '' - . fxPromoteurHubEsc($blnFr ? 'Produits en vente' : 'Products for sale') . '
'; + . fxPromoteurHubEsc(fxPromoteurHubTexte('promoteur_hub_produits_title', 0)) . ''; $html .= '
'; foreach ($arrProduits as $arrProd) { @@ -1359,17 +1429,17 @@ function fxPromoteurHubRenderProduits($arrProduits, $strLangue = 'fr') $html .= '' . intval($arrProd['restant']) . ''; $html .= '/ ' . intval($arrProd['total']) . ''; } - $html .= '' . fxPromoteurHubEsc($blnFr ? 'disponible(s)' : 'available') . ''; + $html .= '' . fxPromoteurHubEsc(fxPromoteurHubTexte('promoteur_hub_disponible', 0)) . ''; $html .= '
'; $html .= '
'; - $html .= '' + $html .= '' . '' . intval($arrProd['vendus']) . ' ' - . fxPromoteurHubEsc($blnFr ? 'vendu(s)' : 'sold') . ''; + . fxPromoteurHubEsc(fxPromoteurHubTexte('promoteur_hub_vendus_lbl', 0)) . ''; if (intval($arrProd['panier']) > 0) { - $html .= '' + $html .= '' . '' . intval($arrProd['panier']) . ' ' - . fxPromoteurHubEsc($blnFr ? 'panier(s)' : 'cart(s)') . ''; + . fxPromoteurHubEsc(fxPromoteurHubTexte('promoteur_hub_paniers_lbl', 0)) . ''; } $html .= '
'; // .phub-epr-chips @@ -1445,7 +1515,6 @@ function fxPromoteurHubRenderEventCard($arrItem, $strLangue, $blnArchived = fals { $strCardClass = 'card promoteur-hub-card mb-3' . ($blnArchived ? ' promoteur-hub-card-archived' : ''); - $blnFr = ($strLangue !== 'en'); $intEveId = intval($arrItem['eve_id']); $intComId = intval($arrItem['com_id'] ?? ($_SESSION['com_id'] ?? 0)); @@ -1506,7 +1575,7 @@ function fxPromoteurHubRenderEventCard($arrItem, $strLangue, $blnArchived = fals if ($blnFonctions) { $html .= ''; } @@ -1517,7 +1586,7 @@ function fxPromoteurHubRenderEventCard($arrItem, $strLangue, $blnArchived = fals // ---- Collapse : stats chargées au clic (ajax_promoteur_hub.php) ---- if ($blnHasStats) { global $vDomaine; - $strLoadMsg = $blnFr ? 'Chargement du tableau de bord…' : 'Loading dashboard…'; + $strLoadMsg = fxPromoteurHubTexte('promoteur_hub_load_dashboard', 0); $strRunnerSrc = $vDomaine . '/images/ms1-runner-loader.gif?v=' . _VERSION_CODE; $html .= '
'; diff --git a/php/inc_settings.php b/php/inc_settings.php index 2fcda4e..0f8e2b2 100644 --- a/php/inc_settings.php +++ b/php/inc_settings.php @@ -7,7 +7,7 @@ * Constantes * * **************/ -define('_VERSION_CODE', '4.72.804'); +define('_VERSION_CODE', '4.72.806'); define('_DATE_CODE', '2026-07-13'); //MSIN-4290 define('QR_SECRET_KEY', 'ms1_qr_2026_cle_secrete_longue_et_fixe'); diff --git a/php/inc_v2_assets.php b/php/inc_v2_assets.php index a09f589..60812be 100644 --- a/php/inc_v2_assets.php +++ b/php/inc_v2_assets.php @@ -23,13 +23,23 @@ if (!function_exists('fxV2RegisterScript')) { $strLangue = ($strLangue === 'en') ? 'en' : 'fr'; $strWait = afficheTexte('preloader_wait', 0, 0, 1); - if (trim((string)$strWait) === '' || $strWait === 'preloader_wait') { - $strWait = ($strLangue === 'en') ? 'Loading…' : 'Chargement…'; + if (trim((string)$strWait) === '' || $strWait === 'preloader_wait' || $strWait === '*preloader_wait*') { + $strWait = function_exists('fxPromoteurHubTexte') + ? fxPromoteurHubTexte('promoteur_hub_load_dashboard', 0) + : ''; } + // MSIN-4401 — libellés Info injectés (pas de FR/EN en dur dans promoteur-hub.js). + $strAjaxError = function_exists('fxPromoteurHubTexte') + ? fxPromoteurHubTexte('promoteur_hub_ajax_error', 0) + : afficheTexte('promoteur_hub_ajax_error', 0, 0, 1); + return [ 'ajaxUrl' => $vDomaine . '/ajax_promoteur_hub.php', 'preloaderWait' => $strWait, + 'i18n' => [ + 'ajaxError' => $strAjaxError, + ], ]; } @@ -47,17 +57,29 @@ if (!function_exists('fxV2RegisterScript')) { ? (bool)fxInscrGestionBibOcrCloudEnabled() : false, 'preloaderWait' => afficheTexte('preloader_wait', 0, 0, 1), - 'refundCancelHint' => ($strLangue === 'en') - ? 'Your transaction has been refunded. If you also want to cancel it in the registration system:' - : "Votre transaction a été remboursée. Si vous voulez aussi l'annuler dans le système d'inscription :", + 'refundCancelHint' => function_exists('fxInscrGestionT') + ? fxInscrGestionT('inscr_gestion_refund_cancel_hint') + : '', 'loginMessages' => [ - 'txt_login' => ($strLangue === 'fr') - ? "Veuillez entrer votre nom d'usager" - : 'Please enter your username', - 'txt_password' => ($strLangue === 'fr') - ? 'Veuillez entrer votre mot de passe' - : 'Please enter your password', + 'txt_login' => function_exists('fxInscrGestionT') + ? fxInscrGestionT('inscr_gestion_login_err_user') + : '', + 'txt_password' => function_exists('fxInscrGestionT') + ? fxInscrGestionT('inscr_gestion_login_err_pass') + : '', ], + // MSIN-4401 — libellés Info injectés (pas de FR/EN en dur dans le JS). + 'i18n' => function_exists('fxInscrGestionT') ? [ + 'errLoad' => fxInscrGestionT('inscr_gestion_err_load'), + 'errNetwork' => fxInscrGestionT('inscr_gestion_err_network'), + 'errRefund' => fxInscrGestionT('inscr_gestion_err_refund'), + 'refundOk' => fxInscrGestionT('inscr_gestion_refund_ok'), + 'bibOcrNotConfigured' => fxInscrGestionT('inscr_gestion_bib_ocr_not_configured'), + 'bibOcrVisionPrefix' => fxInscrGestionT('inscr_gestion_bib_ocr_vision_prefix'), + 'bibOcrImageInvalid' => fxInscrGestionT('inscr_gestion_bib_ocr_image_invalid'), + 'bibOcrNetwork' => fxInscrGestionT('inscr_gestion_bib_ocr_network'), + 'bibOcrUnreadable' => fxInscrGestionT('inscr_gestion_bib_scan_invalid'), + ] : [], ]; } diff --git a/sql/MSIN-4401-eve-acces-promoteur-info.sql b/sql/MSIN-4401-eve-acces-promoteur-info.sql new file mode 100644 index 0000000..01483c7 --- /dev/null +++ b/sql/MSIN-4401-eve-acces-promoteur-info.sql @@ -0,0 +1,99 @@ +-- MSIN-4401 — Libellés Info manquants (gestion accès V2 promoteur) +-- Prérequis : sql/MSIN-4401-eve-acces-promoteur-page.sql (clés de base déjà en Info) +-- Notes : idempotent (DELETE + INSERT). Source de vérité = table info, pas le PHP. + +DELETE FROM info +WHERE info_clef IN ( + 'eve_acces_promoteur_statut_actif', + 'eve_acces_promoteur_statut_pending', + 'eve_acces_promoteur_col_action', + 'eve_acces_promoteur_field_courriel', + 'eve_acces_promoteur_field_prenom', + 'eve_acces_promoteur_field_nom', + 'eve_acces_promoteur_field_kit', + 'eve_acces_promoteur_field_jours', + 'eve_acces_promoteur_jours_help', + 'eve_acces_promoteur_invite_courriel_help', + 'eve_acces_promoteur_btn_invite', + 'eve_acces_promoteur_btn_resend', + 'eve_acces_promoteur_btn_revoke', + 'eve_acces_promoteur_confirm_revoke', + 'eve_acces_promoteur_no_kits', + 'eve_acces_promoteur_ok_existing', + 'eve_acces_promoteur_ok_created', + 'eve_acces_promoteur_ok_revoked', + 'eve_acces_promoteur_ok_resent', + 'eve_acces_promoteur_err_courriel', + 'eve_acces_promoteur_err_kit', + 'eve_acces_promoteur_err_nom', + 'eve_acces_promoteur_err_inactive', + 'eve_acces_promoteur_err_revoke', + 'eve_acces_promoteur_err_resend', + 'eve_acces_promoteur_err_generic', + 'eve_acces_promoteur_check_exists', + 'eve_acces_promoteur_check_new', + 'eve_acces_promoteur_check_inactive', + 'eve_acces_promoteur_check_invalid' +) AND info_prg = 'compte.php'; + +INSERT INTO info (info_clef, info_langue, info_texte, info_aide, info_prg, info_description, info_trie, info_actif, info_option1, info_option2, info_option3, info_creation) VALUES +('eve_acces_promoteur_statut_actif', 'fr', 'Actif', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_statut_actif', 'en', 'Active', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_statut_pending', 'fr', 'Compte non activé', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_statut_pending', 'en', 'Account not activated', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_col_action', 'fr', 'Action', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_col_action', 'en', 'Action', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_field_courriel', 'fr', 'Courriel', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_field_courriel', 'en', 'Email', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_field_prenom', 'fr', 'Prénom', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_field_prenom', 'en', 'First name', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_field_nom', 'fr', 'Nom', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_field_nom', 'en', 'Last name', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_field_kit', 'fr', 'Kit d''accès', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_field_kit', 'en', 'Access kit', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_field_jours', 'fr', 'Durée (jours)', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_field_jours', 'en', 'Duration (days)', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_jours_help', 'fr', '0 = sans expiration. Utilisé surtout pour les nouveaux accès.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_jours_help', 'en', '0 = no expiration. Mainly for new access grants.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_invite_courriel_help', 'fr', 'Saisissez d''abord le courriel : on vérifie s''il existe déjà un compte MS1 avant de demander le nom.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_invite_courriel_help', 'en', 'Enter the email first: we check if an MS1 account already exists before asking for a name.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_btn_invite', 'fr', 'Assigner l''accès', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_btn_invite', 'en', 'Grant access', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_btn_resend', 'fr', 'Renvoyer le mail', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_btn_resend', 'en', 'Resend email', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_btn_revoke', 'fr', 'Retirer', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_btn_revoke', 'en', 'Revoke', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_confirm_revoke', 'fr', 'Retirer cet accès ?', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_confirm_revoke', 'en', 'Revoke this access?', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_no_kits', 'fr', 'Aucun kit délégable disponible. Demandez à un super admin d''activer « délégable » sur un kit.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_no_kits', 'en', 'No delegable kit available. Ask a super admin to mark a kit as delegable.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_ok_existing', 'fr', 'Accès assigné au compte existant. Un courriel a été envoyé.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_ok_existing', 'en', 'Access granted to the existing account. An email was sent.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_ok_created', 'fr', 'Compte créé et accès assigné. Un courriel avec le lien de mot de passe a été envoyé.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_ok_created', 'en', 'Account created and access granted. An email with the password link was sent.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_ok_revoked', 'fr', 'Accès retiré.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_ok_revoked', 'en', 'Access revoked.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_ok_resent', 'fr', 'Courriel d''invitation renvoyé.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_ok_resent', 'en', 'Invitation email resent.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_err_courriel', 'fr', 'Courriel invalide.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_err_courriel', 'en', 'Invalid email.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_err_kit', 'fr', 'Kit non autorisé.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_err_kit', 'en', 'Kit not allowed.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_err_nom', 'fr', 'Prénom et nom requis pour créer un nouveau compte.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_err_nom', 'en', 'First and last name required to create a new account.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_err_inactive', 'fr', 'Un compte existe avec ce courriel mais il est inactif. Contactez le support.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_err_inactive', 'en', 'An account exists with this email but it is inactive. Contact support.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_err_revoke', 'fr', 'Retrait impossible.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_err_revoke', 'en', 'Unable to revoke.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_err_resend', 'fr', 'Renvoi du courriel impossible.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_err_resend', 'en', 'Unable to resend the email.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_err_generic', 'fr', 'Une erreur est survenue.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_err_generic', 'en', 'An error occurred.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_check_exists', 'fr', 'Compte déjà existant. Un courriel de confirmation sera envoyé.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_check_exists', 'en', 'Existing account. A confirmation email will be sent.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_check_new', 'fr', 'Aucun compte avec ce courriel. Indiquez le prénom et le nom pour créer le compte.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_check_new', 'en', 'No account with this email. Enter first and last name to create the account.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_check_inactive', 'fr', 'Un compte existe avec ce courriel mais il est inactif. Contactez le support.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_check_inactive', 'en', 'An account exists with this email but it is inactive. Contact support.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_check_invalid', 'fr', 'Courriel invalide.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_check_invalid', 'en', 'Invalid email.', '', 'compte.php', '', 0, 1, '', '', '', NOW()); diff --git a/sql/MSIN-4401-v2-info-complet.sql b/sql/MSIN-4401-v2-info-complet.sql new file mode 100644 index 0000000..6d8f0dd --- /dev/null +++ b/sql/MSIN-4401-v2-info-complet.sql @@ -0,0 +1,290 @@ +-- MSIN-4401 — Libellés Info V2 manquants (source de vérité) +-- Couvre : mobile invite, courriels accès, gestion inscriptions, OCR/JS, hub dashboard +-- Prérequis : scripts MSIN-4401-eve-acces-promoteur-*.sql déjà exécutés si applicable +-- Idempotent (DELETE + INSERT). Exécuter en pilote puis prod. + +DELETE FROM info +WHERE info_prg = 'compte.php' + AND info_clef IN ( + 'mobile_invite_conflict_signed_as', + 'mobile_invite_conflict_for', + 'mobile_invite_conflict_btn_logout', + 'mobile_invite_conflict_btn_stay', + 'mobile_no_acces_v2', + 'inscr_gestion_mobile_title', + 'inscr_gestion_login_user', + 'inscr_gestion_login_pass', + 'inscr_gestion_login_btn', + 'inscr_gestion_login_intro', + 'inscr_gestion_login_err_user', + 'inscr_gestion_login_err_pass', + 'inscr_gestion_choose_event', + 'inscr_gestion_back_events', + 'tableau_promoteur_menueinscription_v2', + 'eve_acces_promoteur_access_denied', + 'eve_acces_promoteur_btn_back', + 'eve_acces_promoteur_mail_new_subject', + 'eve_acces_promoteur_mail_new_body', + 'eve_acces_promoteur_mail_new_cta', + 'eve_acces_promoteur_mail_existing_subject', + 'eve_acces_promoteur_mail_existing_body', + 'eve_acces_promoteur_mail_existing_cta', + 'inscr_gestion_show_invoice', + 'inscr_gestion_show_transaction', + 'inscr_gestion_hide_transaction', + 'inscr_gestion_renvoi', + 'inscr_gestion_modifier_participant', + 'inscr_gestion_search_prompt', + 'inscr_gestion_annulation_titre', + 'inscr_gestion_statut_annule', + 'inscr_gestion_statut_transfert', + 'inscr_gestion_back_bib', + 'inscr_gestion_back_prev', + 'inscr_gestion_back_full_account', + 'inscr_gestion_back_mon_compte', + 'inscr_gestion_chk_cancelled', + 'inscr_gestion_rech_statut', + 'inscr_gestion_rech_statut_all', + 'inscr_gestion_rech_statut_n', + 'inscr_gestion_bib_scan_found_search', + 'inscr_gestion_bib_ocr_not_configured', + 'inscr_gestion_bib_ocr_vision_prefix', + 'inscr_gestion_bib_ocr_image_invalid', + 'inscr_gestion_bib_ocr_network', + 'inscr_gestion_err_load', + 'inscr_gestion_err_network', + 'inscr_gestion_err_refund', + 'inscr_gestion_refund_ok', + 'inscr_gestion_refund_cancel_hint', + 'promoteur_hub_load_menu', + 'promoteur_hub_load_dashboard', + 'promoteur_hub_epreuves_title', + 'promoteur_hub_produits_title', + 'promoteur_hub_bibs_sold', + 'promoteur_hub_restant', + 'promoteur_hub_unlimited', + 'promoteur_hub_prix_courant', + 'promoteur_hub_prochain_prix', + 'promoteur_hub_des', + 'promoteur_hub_dossards_remis', + 'promoteur_hub_liste_active', + 'promoteur_hub_waiting_n', + 'promoteur_hub_waiting_list', + 'promoteur_hub_disponible', + 'promoteur_hub_vendus_title', + 'promoteur_hub_vendus_lbl', + 'promoteur_hub_paniers_title', + 'promoteur_hub_paniers_lbl', + 'promoteur_hub_menu_features', + 'promoteur_hub_flash_inscrits', + 'promoteur_hub_flash_rempli', + 'promoteur_hub_flash_places', + 'promoteur_hub_flash_revenus', + 'promoteur_hub_badge_completes', + 'promoteur_hub_badge_presque', + 'promoteur_hub_badge_attente', + 'promoteur_hub_badge_limite', + 'promoteur_hub_ajax_session', + 'promoteur_hub_ajax_invalid', + 'promoteur_hub_ajax_denied', + 'promoteur_hub_ajax_not_found', + 'promoteur_hub_ajax_error', + 'promoteur_hub_rapport_chrono', + 'promoteur_hub_rapport_finances', + 'promoteur_hub_rapport_finances_all', + 'promoteur_hub_rapport_finances_adh', + 'promoteur_hub_rapport_finances_eve', + 'promoteur_hub_rapport_adhesions', + 'promoteur_hub_rapport_adhesions_annuelles', + 'promoteur_hub_rapport_finances_capitaines', + 'promoteur_hub_rapport_paiements', + 'promoteur_hub_no_features' + ); + +INSERT INTO info (info_clef, info_langue, info_texte, info_aide, info_prg, info_description, info_trie, info_actif, info_option1, info_option2, info_option3, info_creation) VALUES +('mobile_invite_conflict_signed_as', 'fr', 'Vous êtes actuellement connecté en tant que %name%.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('mobile_invite_conflict_signed_as', 'en', 'You are currently signed in as %name%.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('mobile_invite_conflict_for', 'fr', 'Cette invitation concerne le compte %name%%mail_part%. Déconnectez-vous d''abord, puis reconnectez-vous avec ce compte.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('mobile_invite_conflict_for', 'en', 'This invitation is for %name%%mail_part%. Sign out first, then sign in with that account.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('mobile_invite_conflict_btn_logout', 'fr', 'Se déconnecter et continuer', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('mobile_invite_conflict_btn_logout', 'en', 'Sign out and continue', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('mobile_invite_conflict_btn_stay', 'fr', 'Rester sur mon compte actuel', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('mobile_invite_conflict_btn_stay', 'en', 'Stay on my current account', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('mobile_no_acces_v2', 'fr', 'Aucun accès inscriptions mobile (v2) n''est actif pour ce compte.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('mobile_no_acces_v2', 'en', 'No active mobile registration access (v2) for this account.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_mobile_title', 'fr', 'Inscriptions mobile', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_mobile_title', 'en', 'Mobile registrations', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_login_user', 'fr', 'Nom d''utilisateur', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_login_user', 'en', 'Username', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_login_pass', 'fr', 'Mot de passe', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_login_pass', 'en', 'Password', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_login_btn', 'fr', 'Connexion', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_login_btn', 'en', 'Sign in', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_login_intro', 'fr', 'Connectez-vous pour accéder aux inscriptions mobile.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_login_intro', 'en', 'Sign in to access mobile registrations.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_login_err_user', 'fr', 'Veuillez entrer votre nom d''usager', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_login_err_user', 'en', 'Please enter your username', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_login_err_pass', 'fr', 'Veuillez entrer votre mot de passe', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_login_err_pass', 'en', 'Please enter your password', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_choose_event', 'fr', 'Choisir un événement', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_choose_event', 'en', 'Choose an event', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_back_events', 'fr', 'Événements', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_back_events', 'en', 'Events', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('tableau_promoteur_menueinscription_v2', 'fr', 'Inscriptions V2', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('tableau_promoteur_menueinscription_v2', 'en', 'Registrations V2', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_access_denied', 'fr', 'Accès refusé.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_access_denied', 'en', 'Access denied.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_btn_back', 'fr', 'Retour', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_btn_back', 'en', 'Back', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_mail_new_subject', 'fr', 'Accès promoteur — créez votre mot de passe', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_mail_new_subject', 'en', 'Promoter access — set your password', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_mail_new_body', 'fr', 'Bonjour %prenom%,

Un accès vous a été accordé pour %eve_nom% (kit : %kit%).

Un compte a été créé avec le courriel %login%.
Cliquez ci-dessous pour choisir votre mot de passe.

Ensuite, connectez-vous ici pour travailler sur le terrain :
%mobile_url%', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_mail_new_body', 'en', 'Hello %prenom%,

You were granted access to %eve_nom% (kit: %kit%).

An account was created with %login%.
Click below to set your password.

Then sign in here for field use:
%mobile_url%', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_mail_new_cta', 'fr', 'Choisir mon mot de passe', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_mail_new_cta', 'en', 'Set my password', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_mail_existing_subject', 'fr', 'Nouvel accès — %eve_nom%', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_mail_existing_subject', 'en', 'New access — %eve_nom%', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_mail_existing_body', 'fr', 'Bonjour %prenom%,

Un accès vous a été accordé pour %eve_nom% (kit : %kit%).

Ouvrez le lien ci-dessous et connectez-vous avec le compte %login% (si un autre compte est déjà ouvert, vous serez invité à vous déconnecter).', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_mail_existing_body', 'en', 'Hello %prenom%,

You were granted access to %eve_nom% (kit: %kit%).

Open the link below and sign in as %login% (if another account is already open, you will be asked to sign out).', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_mail_existing_cta', 'fr', 'Ouvrir l''accès (mobile)', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('eve_acces_promoteur_mail_existing_cta', 'en', 'Open access (mobile)', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_show_invoice', 'fr', 'Afficher la facture', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_show_invoice', 'en', 'Show invoice', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_show_transaction', 'fr', 'Afficher la transaction', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_show_transaction', 'en', 'Show transaction', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_hide_transaction', 'fr', 'Masquer la transaction', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_hide_transaction', 'en', 'Hide transaction', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_renvoi', 'fr', 'Retourner confirmation', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_renvoi', 'en', 'Resend confirmation', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_modifier_participant', 'fr', 'Modifier l''inscription', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_modifier_participant', 'en', 'Edit registration', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_search_prompt', 'fr', 'Utilisez la recherche ci-dessus pour afficher les inscriptions. Laissez les champs vides et lancez la recherche pour toutes les afficher.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_search_prompt', 'en', 'Use the search above to display registrations. Leave the fields empty and search to show them all.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_annulation_titre', 'fr', 'Statut de l''inscription', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_annulation_titre', 'en', 'Registration status', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_statut_annule', 'fr', 'Annulée', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_statut_annule', 'en', 'Cancelled', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_statut_transfert', 'fr', 'Transférée', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_statut_transfert', 'en', 'Transferred', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_back_bib', 'fr', 'Retour aux dossards', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_back_bib', 'en', 'Back to bibs', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_back_prev', 'fr', 'Retour', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_back_prev', 'en', 'Back', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_back_full_account', 'fr', 'Retour au tableau de bord', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_back_full_account', 'en', 'Back to dashboard', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_back_mon_compte', 'fr', 'Retour à Mon compte', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_back_mon_compte', 'en', 'Back to My account', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_chk_cancelled', 'fr', 'Montrer les inscriptions annulées ou transférées', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_chk_cancelled', 'en', 'Show cancelled or transferred registrations', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_rech_statut', 'fr', 'Statut de course', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_rech_statut', 'en', 'Race status', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_rech_statut_all', 'fr', 'Tous les statuts', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_rech_statut_all', 'en', 'All statuses', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_rech_statut_n', 'fr', '%d sélectionnés', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_rech_statut_n', 'en', '%d selected', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_bib_scan_found_search', 'fr', 'Numéro %s détecté — recherche...', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_bib_scan_found_search', 'en', 'Number %s detected — searching...', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_bib_ocr_not_configured', 'fr', 'OCR non configuré (clé Vision absente sur le serveur).', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_bib_ocr_not_configured', 'en', 'OCR not configured (Vision API key missing on server).', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_bib_ocr_vision_prefix', 'fr', 'Erreur Vision : ', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_bib_ocr_vision_prefix', 'en', 'Vision error: ', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_bib_ocr_image_invalid', 'fr', 'Image invalide.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_bib_ocr_image_invalid', 'en', 'Invalid image capture.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_bib_ocr_network', 'fr', 'Erreur réseau ou capture.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_bib_ocr_network', 'en', 'Network or capture error.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_err_load', 'fr', 'Erreur de chargement.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_err_load', 'en', 'Loading error.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_err_network', 'fr', 'Erreur réseau.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_err_network', 'en', 'Network error.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_err_refund', 'fr', 'Erreur remboursement.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_err_refund', 'en', 'Refund error.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_refund_ok', 'fr', 'Remboursement effectué.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_refund_ok', 'en', 'Refund completed.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_refund_cancel_hint', 'fr', 'Votre transaction a été remboursée. Si vous voulez aussi l''annuler dans le système d''inscription :', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_refund_cancel_hint', 'en', 'Your transaction has been refunded. If you also want to cancel it in the registration system:', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_load_menu', 'fr', 'Chargement du menu…', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_load_menu', 'en', 'Loading menu…', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_load_dashboard', 'fr', 'Chargement du tableau de bord…', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_load_dashboard', 'en', 'Loading dashboard…', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_epreuves_title', 'fr', 'Épreuves en vente', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_epreuves_title', 'en', 'Events for sale', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_produits_title', 'fr', 'Produits en vente', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_produits_title', 'en', 'Products for sale', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_bibs_sold', 'fr', 'dossards vendus', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_bibs_sold', 'en', 'bibs sold', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_restant', 'fr', 'restant', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_restant', 'en', 'left', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_unlimited', 'fr', 'illimité', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_unlimited', 'en', 'unlimited', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_prix_courant', 'fr', 'Prix courant', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_prix_courant', 'en', 'Current price', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_prochain_prix', 'fr', 'Prochain prix', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_prochain_prix', 'en', 'Next price', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_des', 'fr', 'dès', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_des', 'en', 'on', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_dossards_remis', 'fr', 'Dossards remis', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_dossards_remis', 'en', 'Bibs handed out', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_liste_active', 'fr', 'liste active', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_liste_active', 'en', 'list active', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_waiting_n', 'fr', 'en attente', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_waiting_n', 'en', 'waiting', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_waiting_list', 'fr', 'Liste d''attente', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_waiting_list', 'en', 'Waiting list', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_disponible', 'fr', 'disponible(s)', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_disponible', 'en', 'available', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_vendus_title', 'fr', 'Vendus', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_vendus_title', 'en', 'Sold', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_vendus_lbl', 'fr', 'vendu(s)', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_vendus_lbl', 'en', 'sold', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_paniers_title', 'fr', 'Dans des paniers incomplets', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_paniers_title', 'en', 'In incomplete carts', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_paniers_lbl', 'fr', 'panier(s)', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_paniers_lbl', 'en', 'cart(s)', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_menu_features', 'fr', 'Menu des fonctionnalités', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_menu_features', 'en', 'Features menu', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_flash_inscrits', 'fr', 'inscrits', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_flash_inscrits', 'en', 'registered', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_flash_rempli', 'fr', 'rempli', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_flash_rempli', 'en', 'filled', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_flash_places', 'fr', 'places restantes', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_flash_places', 'en', 'spots left', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_flash_revenus', 'fr', 'revenus est.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_flash_revenus', 'en', 'est. revenue', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_badge_completes', 'fr', 'complète(s)', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_badge_completes', 'en', 'sold out', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_badge_presque', 'fr', 'presque pleine(s)', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_badge_presque', 'en', 'almost full', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_badge_attente', 'fr', 'en attente', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_badge_attente', 'en', 'waiting', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_badge_limite', 'fr', 'limite', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_badge_limite', 'en', 'deadline', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_ajax_session', 'fr', 'Session expirée', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_ajax_session', 'en', 'Session expired', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_ajax_invalid', 'fr', 'Requête invalide', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_ajax_invalid', 'en', 'Invalid request', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_ajax_denied', 'fr', 'Accès refusé', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_ajax_denied', 'en', 'Access denied', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_ajax_not_found', 'fr', 'Événement introuvable', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_ajax_not_found', 'en', 'Event not found', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_ajax_error', 'fr', 'Erreur', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_ajax_error', 'en', 'Error', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_rapport_chrono', 'fr', 'chrono + questions', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_rapport_chrono', 'en', 'chrono + questions', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_rapport_finances', 'fr', 'Transactions-Finances', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_rapport_finances', 'en', 'Transactions-Finances', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_rapport_finances_all', 'fr', 'Transactions-Finances-Tout', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_rapport_finances_all', 'en', 'Transactions-Finances-All', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_rapport_finances_adh', 'fr', 'Transactions-Finances-Adhésions Seul', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_rapport_finances_adh', 'en', 'Transactions-Finances-Memberships only', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_rapport_finances_eve', 'fr', 'Transactions-Finances-Événements Seul', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_rapport_finances_eve', 'en', 'Transactions-Finances-Events only', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_rapport_adhesions', 'fr', 'adhésions actives', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_rapport_adhesions', 'en', 'active memberships', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_rapport_adhesions_annuelles', 'fr', 'liste d''adhésions annuelles', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_rapport_adhesions_annuelles', 'en', 'annual memberships list', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_rapport_finances_capitaines', 'fr', 'Transactions-Finances (capitaines seulement)', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_rapport_finances_capitaines', 'en', 'Transactions-Finances (captains only)', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_rapport_paiements', 'fr', 'Paiements', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_rapport_paiements', 'en', 'Payments', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_no_features', 'fr', 'Aucune fonctionnalité disponible avec vos droits actuels.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('promoteur_hub_no_features', 'en', 'No features available with your current permissions.', '', 'compte.php', '', 0, 1, '', '', '', NOW());