From dc3d6a5c0be388d3436c86adbe1b09bb8865ad35 Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 13 Jul 2026 17:42:21 -0400 Subject: [PATCH] Refactor inscription management to improve localization handling by replacing hardcoded strings with dynamic keys. Enhance CSS for better layout and responsiveness in the inscription management UI. Increment version code to 4.72.814 to reflect these changes. --- css/style.css | 41 ++++- js/v2/inscr-gestion.js | 18 ++- php/inc_fx_inscriptions_gestion.php | 146 +++++++++++++++--- php/inc_settings.php | 2 +- php/inc_v2_assets.php | 41 ++++- sql/MSIN-4401-inscr-gestion-bib-scan-i18n.sql | 52 +++++++ 6 files changed, 264 insertions(+), 36 deletions(-) create mode 100644 sql/MSIN-4401-inscr-gestion-bib-scan-i18n.sql diff --git a/css/style.css b/css/style.css index 91f23cc..3fc2f31 100644 --- a/css/style.css +++ b/css/style.css @@ -3798,7 +3798,9 @@ a.ms1-trad-link.btn-aide-trad{ .inscr-gestion-list-head{ display:flex; justify-content:space-between; - align-items:center; + align-items:flex-start; + gap:10px 16px; + flex-wrap:wrap; font-size:13px; font-weight:600; color:#004085; @@ -3810,9 +3812,46 @@ a.ms1-trad-link.btn-aide-trad{ border-radius:6px; } +.inscr-gestion-list-head__main{ + display:flex; + flex-wrap:wrap; + align-items:center; + gap:6px 8px; + min-width:0; + flex:1 1 12rem; +} + +.inscr-gestion-list-head__count{ + white-space:nowrap; +} + +.inscr-gestion-list-head__filters{ + display:inline-flex; + flex-wrap:wrap; + gap:4px 6px; + align-items:center; + font-weight:500; +} + +.inscr-gestion-list-head__chip{ + display:inline-block; + max-width:100%; + padding:2px 8px; + border-radius:999px; + background:#fff; + border:1px solid #b8d0ef; + color:#1a4a7a; + font-size:12px; + font-weight:500; + line-height:1.35; + word-break:break-word; +} + .inscr-gestion-list-head__pg{ font-weight:500; color:#6c757d; + white-space:nowrap; + margin-left:auto; } .inscr-gestion-list{ diff --git a/js/v2/inscr-gestion.js b/js/v2/inscr-gestion.js index 2ab1fb7..cb01d1c 100644 --- a/js/v2/inscr-gestion.js +++ b/js/v2/inscr-gestion.js @@ -802,17 +802,27 @@ // soit le prefixe (inscr_gestion_, ancien inscr_mobile_, etc.) : une vraie // traduction contient des espaces/accents, jamais ce motif "cle_technique". function isRawI18nKey(str) { - return typeof str === 'string' && /^[a-z][a-z0-9_]*$/.test(str) && str.indexOf('_') !== -1; + return typeof str === 'string' && /^[a-z][a-z0-9_]*$/i.test(str) && str.indexOf('_') !== -1; } - // Renvoie la valeur de l'attribut si elle est traduite, sinon le texte - // integre (bilingue) — evite d'afficher une clef brute a l'usager. + var SCAN_MSG_I18N = { + 'data-msg-scanning': 'bibScanScanning', + 'data-msg-loading': 'bibScanLoading', + 'data-msg-ready': 'bibScanReady', + 'data-msg-found': 'bibScanFound', + 'data-msg-found-search': 'bibScanFoundSearch', + 'data-msg-invalid': 'bibScanInvalid', + 'data-msg-camera': 'bibScanCamera' + }; + + // Attribut data-* si traduit, sinon libelle injecte (cfg.i18n) — jamais une clef brute. function scanMsg(panel, attr) { var value = panel ? panel.getAttribute(attr) : ''; if (value && !isRawI18nKey(value)) { return value; } - return ''; + var i18nKey = SCAN_MSG_I18N[attr]; + return i18nKey ? t(i18nKey) : ''; } BibScanSession.prototype.showReady = function () { diff --git a/php/inc_fx_inscriptions_gestion.php b/php/inc_fx_inscriptions_gestion.php index e165cb8..d57b086 100644 --- a/php/inc_fx_inscriptions_gestion.php +++ b/php/inc_fx_inscriptions_gestion.php @@ -303,7 +303,8 @@ function fxInscrGestionLookupTexteDb($strClef, $strLangue) { LIMIT 1"; $strTexte = $objDatabase->fxGetVar($sql); - if ($strTexte === null || trim((string)$strTexte) === '' || trim((string)$strTexte) === $strClef) { + if ($strTexte === null || trim((string)$strTexte) === '' || trim((string)$strTexte) === $strClef + || fxInscrGestionIsRawI18nKey($strTexte)) { $tabMemo[$strMemoKey] = null; return null; } @@ -312,6 +313,20 @@ function fxInscrGestionLookupTexteDb($strClef, $strLangue) { return $tabMemo[$strMemoKey]; } +/** + * True si $str ressemble a une clef info technique (ex. inscr_gestion_bib_scan_tap), + * pas a un vrai libelle (espaces/accents/points). Ignore marqueurs ZWSP trad. + */ +function fxInscrGestionIsRawI18nKey($str) { + $str = preg_replace('/[\x{200B}\x{200C}]/u', '', (string)$str); + // Payload base64 eventuel (mode edition textes) entre ZWSP deja retire. + $str = trim($str); + if ($str === '') { + return false; + } + return (bool)preg_match('/^[a-z][a-z0-9]*(_[a-z0-9]+)+$/i', $str); +} + function fxInscrGestionResolveTexte($strClef, $strLangue) { global $vtexte_page; @@ -319,20 +334,20 @@ function fxInscrGestionResolveTexte($strClef, $strLangue) { if (isset($tab[$strClef]['info_texte'])) { $str = trim((string)$tab[$strClef]['info_texte']); - if ($str !== '' && $str !== $strClef) { + if ($str !== '' && $str !== $strClef && !fxInscrGestionIsRawI18nKey($str)) { return $str; } } if (is_array($vtexte_page) && isset($vtexte_page[$strClef]['info_texte'])) { $str = trim((string)$vtexte_page[$strClef]['info_texte']); - if ($str !== '' && $str !== $strClef) { + if ($str !== '' && $str !== $strClef && !fxInscrGestionIsRawI18nKey($str)) { return $str; } } $strDb = fxInscrGestionLookupTexteDb($strClef, $strLangue); - if ($strDb !== null) { + if ($strDb !== null && !fxInscrGestionIsRawI18nKey($strDb)) { return $strDb; } @@ -345,7 +360,8 @@ function fxInscrGestionResolveTexte($strClef, $strLangue) { if (strpos($str, '*') === 0 && substr($str, -1) === '*') { return fxInscrGestionFallbackTexte($strClef, $strLangue); } - if (trim($str) === '' || trim($str) === $strClef) { + $strPlain = trim(preg_replace('/[\x{200B}\x{200C}]/u', '', (string)$str)); + if ($strPlain === '' || $strPlain === $strClef || fxInscrGestionIsRawI18nKey($strPlain)) { return fxInscrGestionFallbackTexte($strClef, $strLangue); } return $str; @@ -459,6 +475,10 @@ function fxInscrGestionFallbackTexte($strClef, $strLangue) { 'inscr_gestion_list_count_one' => $blnFr ? '%d inscription' : '%d registration', 'inscr_gestion_list_count_many' => $blnFr ? '%d inscriptions' : '%d registrations', 'inscr_gestion_list_page' => $blnFr ? 'Page %d / %d' : 'Page %d / %d', + 'inscr_gestion_list_filter_kv' => $blnFr ? '%s : %s' : '%s: %s', + 'inscr_gestion_rech_bib_equipe' => $blnFr ? 'Dossard ou équipe' : 'Bib or team', + 'chk_bib' => $blnFr ? 'Sans numéro de dossard' : 'Without bib number', + 'inscr_gestion_reset_search' => $blnFr ? 'Réinitialiser la recherche' : 'Reset search', ); } @@ -486,8 +506,9 @@ function fxInscrGestionT($strClef) { } $strTexte = fxInscrGestionResolveTexte($strClef, $strLangue); + $strPlain = trim(preg_replace('/[\x{200B}\x{200C}]/u', '', (string)$strTexte)); - if ($strTexte === $strClef || trim((string)$strTexte) === '') { + if ($strPlain === '' || $strPlain === $strClef || fxInscrGestionIsRawI18nKey($strPlain)) { $strFallback = fxInscrGestionFallbackTexte($strClef, $strLangue); if ($strFallback !== $strClef && trim((string)$strFallback) !== '') { $strTexte = $strFallback; @@ -505,18 +526,16 @@ function fxInscrGestionT($strClef) { } /** - * Libelle "robuste" pour les pastilles du scanner : si la table info renvoie une - * clef i18n brute (donnee corrompue, ex. ancien 'inscr_mobile_bib_scan_tap' jamais - * traduit), on l'ignore et on prend le libelle integre. Evite d'afficher une clef - * technique a l'usager. Pas de marqueur d'edition ici (texte affiche dans la video). + * Libelle "robuste" pour le scanner : jamais de clef technique ni marqueur trad + * (affiche dans la video / data-msg lus par le JS). */ function fxInscrGestionScanLabel($strClef) { global $strLangue; $strLang = (isset($strLangue) && $strLangue !== '') ? $strLangue : 'fr'; - $strTexte = trim((string)fxInscrGestionResolveTexte($strClef, $strLang)); + $strTexte = trim(preg_replace('/[\x{200B}\x{200C}]/u', '', (string)fxInscrGestionResolveTexte($strClef, $strLang))); - if ($strTexte === '' || preg_match('/^[a-z][a-z0-9]*(_[a-z0-9]+)+$/', $strTexte)) { + if ($strTexte === '' || $strTexte === $strClef || fxInscrGestionIsRawI18nKey($strTexte)) { $strTexte = fxInscrGestionFallbackTexte($strClef, $strLang); } @@ -1688,28 +1707,93 @@ function fxInscrGestionVisionReadBibNumber($strImageBase64) { function fxInscrGestionRenderBibScanBlock($strBibInputId, $strLangue) { echo '
'; + // MSIN-4401 — ScanLabel : jamais de clef brute ni marqueur trad dans data-* / pastille. + echo ' data-msg-scanning="' . fxInscrGestionEsc(fxInscrGestionScanLabel('inscr_gestion_bib_scan_scanning')) . '"'; + echo ' data-msg-loading="' . fxInscrGestionEsc(fxInscrGestionScanLabel('inscr_gestion_bib_scan_loading')) . '"'; + echo ' data-msg-ready="' . fxInscrGestionEsc(fxInscrGestionScanLabel('inscr_gestion_bib_scan_ready')) . '"'; + echo ' data-msg-found="' . fxInscrGestionEsc(fxInscrGestionScanLabel('inscr_gestion_bib_scan_found')) . '"'; + echo ' data-msg-found-search="' . fxInscrGestionEsc(fxInscrGestionScanLabel('inscr_gestion_bib_scan_found_search')) . '"'; + echo ' data-msg-invalid="' . fxInscrGestionEsc(fxInscrGestionScanLabel('inscr_gestion_bib_scan_invalid')) . '"'; + echo ' data-msg-camera="' . fxInscrGestionEsc(fxInscrGestionScanLabel('inscr_gestion_qr_camera_error')) . '">'; echo '

' - . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_bib_scan_hint')) . '

'; + . fxInscrGestionEsc(fxInscrGestionScanLabel('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 — texte Info (inscr_gestion_bib_scan_tap). echo '' - . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_bib_scan_tap')) . ''; + . fxInscrGestionEsc(fxInscrGestionScanLabel('inscr_gestion_bib_scan_tap')) . ''; echo '
'; echo '
'; } +/** + * MSIN-4401 — Pastilles des filtres actifs (non vides) pour la barre de resultats. + * @return array liste de libelles deja pret a afficher (non escapes). + */ +function fxInscrGestionActiveFilterLabels($arrReq, $strLangue) { + $tab = array(); + $strKv = fxInscrGestionT('inscr_gestion_list_filter_kv'); + if ($strKv === 'inscr_gestion_list_filter_kv' || fxInscrGestionIsRawI18nKey($strKv)) { + $strKv = ($strLangue === 'en') ? '%s: %s' : '%s : %s'; + } + + if ($arrReq['rech_prenom'] !== '') { + $tab[] = sprintf($strKv, fxInscrGestionT('rech_prenom'), $arrReq['rech_prenom']); + } + if ($arrReq['rech_nom'] !== '') { + $tab[] = sprintf($strKv, fxInscrGestionT('rech_nom'), $arrReq['rech_nom']); + } + if ($arrReq['rech_no_commande'] !== '') { + $tab[] = sprintf($strKv, fxInscrGestionT('rech_no_commande'), $arrReq['rech_no_commande']); + } + if ($arrReq['rech_nom_equipe'] !== '') { + $tab[] = sprintf( + $strKv, + fxInscrGestionT('inscr_gestion_rech_bib_equipe'), + $arrReq['rech_nom_equipe'] + ); + } + if ($arrReq['sel_rech_epreuve'] > 0) { + $tab[] = sprintf( + $strKv, + fxInscrGestionT('sel_rech_epreuve'), + fxInscrGestionEpreuveLabel($arrReq['sel_rech_epreuve'], $strLangue) + ); + } + if (!empty($arrReq['sel_rech_statut']) && is_array($arrReq['sel_rech_statut'])) { + $tabOptions = fxInscrGestionGetStatutCourseOptions($strLangue); + $tabMap = array(); + foreach ($tabOptions as $arrOpt) { + $strCode = trim((string)$arrOpt['info_option2']); + if ($strCode !== '') { + $tabMap[$strCode] = trim((string)$arrOpt['info_texte']); + } + } + $tabStatLabels = array(); + foreach ($arrReq['sel_rech_statut'] as $strCode) { + $tabStatLabels[] = isset($tabMap[$strCode]) && $tabMap[$strCode] !== '' + ? $tabMap[$strCode] + : $strCode; + } + if (count($tabStatLabels) > 0) { + $tab[] = sprintf( + $strKv, + fxInscrGestionT('inscr_gestion_rech_statut'), + implode(', ', $tabStatLabels) + ); + } + } + if ($arrReq['chk_bib']) { + $tab[] = fxInscrGestionT('chk_bib'); + } + if ($arrReq['chk_cancelled']) { + $tab[] = fxInscrGestionT('inscr_gestion_chk_cancelled'); + } + + return $tab; +} + function fxInscrGestionRenderList($intEveId, $strLangue, $strBaseUrl, $arrReq) { global $vDomaine; @@ -1864,8 +1948,20 @@ function fxInscrGestionRenderList($intEveId, $strLangue, $strBaseUrl, $arrReq) { } echo '
'; + echo '
'; $strCountKey = ($intNbTotal === 1) ? 'inscr_gestion_list_count_one' : 'inscr_gestion_list_count_many'; - echo '' . fxInscrGestionEsc(sprintf(fxInscrGestionT($strCountKey), $intNbTotal)) . ''; + echo '' + . fxInscrGestionEsc(sprintf(fxInscrGestionT($strCountKey), $intNbTotal)) . ''; + // MSIN-4401 — Rappel des filtres / criteres non vides a cote du compteur. + $tabFilters = fxInscrGestionActiveFilterLabels($arrReq, $strLangue); + if (count($tabFilters) > 0) { + echo ''; + foreach ($tabFilters as $strFilt) { + echo '' . fxInscrGestionEsc($strFilt) . ''; + } + echo ''; + } + echo '
'; if ($intNbTotal > $intNbItemsParPage) { $intNbPages = (int)ceil($intNbTotal / $intNbItemsParPage); echo '' diff --git a/php/inc_settings.php b/php/inc_settings.php index fc15460..d0e2b95 100644 --- a/php/inc_settings.php +++ b/php/inc_settings.php @@ -7,7 +7,7 @@ * Constantes * * **************/ -define('_VERSION_CODE', '4.72.813'); +define('_VERSION_CODE', '4.72.814'); 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 60812be..c54b182 100644 --- a/php/inc_v2_assets.php +++ b/php/inc_v2_assets.php @@ -74,11 +74,42 @@ if (!function_exists('fxV2RegisterScript')) { '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'), + 'bibOcrNotConfigured' => function_exists('fxInscrGestionScanLabel') + ? fxInscrGestionScanLabel('inscr_gestion_bib_ocr_not_configured') + : fxInscrGestionT('inscr_gestion_bib_ocr_not_configured'), + 'bibOcrVisionPrefix' => function_exists('fxInscrGestionScanLabel') + ? fxInscrGestionScanLabel('inscr_gestion_bib_ocr_vision_prefix') + : fxInscrGestionT('inscr_gestion_bib_ocr_vision_prefix'), + 'bibOcrImageInvalid' => function_exists('fxInscrGestionScanLabel') + ? fxInscrGestionScanLabel('inscr_gestion_bib_ocr_image_invalid') + : fxInscrGestionT('inscr_gestion_bib_ocr_image_invalid'), + 'bibOcrNetwork' => function_exists('fxInscrGestionScanLabel') + ? fxInscrGestionScanLabel('inscr_gestion_bib_ocr_network') + : fxInscrGestionT('inscr_gestion_bib_ocr_network'), + 'bibOcrUnreadable' => function_exists('fxInscrGestionScanLabel') + ? fxInscrGestionScanLabel('inscr_gestion_bib_scan_invalid') + : fxInscrGestionT('inscr_gestion_bib_scan_invalid'), + 'bibScanScanning' => function_exists('fxInscrGestionScanLabel') + ? fxInscrGestionScanLabel('inscr_gestion_bib_scan_scanning') + : '', + 'bibScanLoading' => function_exists('fxInscrGestionScanLabel') + ? fxInscrGestionScanLabel('inscr_gestion_bib_scan_loading') + : '', + 'bibScanReady' => function_exists('fxInscrGestionScanLabel') + ? fxInscrGestionScanLabel('inscr_gestion_bib_scan_ready') + : '', + 'bibScanFound' => function_exists('fxInscrGestionScanLabel') + ? fxInscrGestionScanLabel('inscr_gestion_bib_scan_found') + : '', + 'bibScanFoundSearch' => function_exists('fxInscrGestionScanLabel') + ? fxInscrGestionScanLabel('inscr_gestion_bib_scan_found_search') + : '', + 'bibScanInvalid' => function_exists('fxInscrGestionScanLabel') + ? fxInscrGestionScanLabel('inscr_gestion_bib_scan_invalid') + : '', + 'bibScanCamera' => function_exists('fxInscrGestionScanLabel') + ? fxInscrGestionScanLabel('inscr_gestion_qr_camera_error') + : '', ] : [], ]; } diff --git a/sql/MSIN-4401-inscr-gestion-bib-scan-i18n.sql b/sql/MSIN-4401-inscr-gestion-bib-scan-i18n.sql new file mode 100644 index 0000000..355ded3 --- /dev/null +++ b/sql/MSIN-4401-inscr-gestion-bib-scan-i18n.sql @@ -0,0 +1,52 @@ +-- MSIN-4401 — Libellés Info scan dossard + pastille filtres liste +-- Prérequis : aucun (complète MSIN-inscriptions-mobile-qr-i18n / v2-info-complet) +-- Idempotent (DELETE + INSERT). Corrige les textes manquants ou corrompus (clef brute). + +DELETE FROM info +WHERE info_prg = 'compte.php' + AND info_clef IN ( + 'inscr_gestion_bib_scan_btn', + 'inscr_gestion_bib_scan_hint', + 'inscr_gestion_bib_scan_ready', + 'inscr_gestion_bib_scan_tap', + 'inscr_gestion_bib_scan_scanning', + 'inscr_gestion_bib_scan_loading', + 'inscr_gestion_bib_scan_found', + 'inscr_gestion_bib_scan_found_search', + 'inscr_gestion_bib_scan_invalid', + '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_list_filter_kv' + ); + +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 +('inscr_gestion_bib_scan_btn', 'fr', 'Scanner le numéro', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_bib_scan_btn', 'en', 'Scan number', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_bib_scan_hint', 'fr', 'Cadrez le numéro dans le cadre, puis touchez l''image.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_bib_scan_hint', 'en', 'Frame the number in the box, then tap the image.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_bib_scan_ready', 'fr', 'Touchez l''image pour lire', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_bib_scan_ready', 'en', 'Tap the image to read', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_bib_scan_tap', 'fr', 'Touchez pour lire', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_bib_scan_tap', 'en', 'Tap to read', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_bib_scan_scanning', 'fr', 'Lecture en cours...', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_bib_scan_scanning', 'en', 'Reading...', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_bib_scan_loading', 'fr', 'Initialisation (1re fois)...', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_bib_scan_loading', 'en', 'Initializing (first time)...', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_bib_scan_found', 'fr', 'Numéro %s détecté — validez avec OK.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_bib_scan_found', 'en', 'Number %s detected — press OK.', '', '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_scan_invalid', 'fr', 'Numéro illisible. Cadrez un seul numéro.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_bib_scan_invalid', 'en', 'Unreadable number. Frame a single number.', '', '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_list_filter_kv', 'fr', '%s : %s', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('inscr_gestion_list_filter_kv', 'en', '%s: %s', '', 'compte.php', '', 0, 1, '', '', '', NOW());