Améliore l'édition du site public
This commit is contained in:
parent
e786756f22
commit
81f259ea6e
11 changed files with 123 additions and 26 deletions
|
|
@ -2,6 +2,15 @@
|
||||||
|
|
||||||
Toutes les modifications importantes du projet sont consignées ici. Le format s’inspire de [Keep a Changelog](https://keepachangelog.com/fr/1.1.0/) et le projet suit le [versionnage sémantique](https://semver.org/lang/fr/).
|
Toutes les modifications importantes du projet sont consignées ici. Le format s’inspire de [Keep a Changelog](https://keepachangelog.com/fr/1.1.0/) et le projet suit le [versionnage sémantique](https://semver.org/lang/fr/).
|
||||||
|
|
||||||
|
## [1.0.0-rc.18] — 2026-09-09
|
||||||
|
|
||||||
|
### Finitions du site public
|
||||||
|
|
||||||
|
- présence des mentions légales et de la politique de confidentialité dans le pied de chaque page publique ;
|
||||||
|
- insertion guidée des images de la bibliothèque dans les textes, avec description alternative ;
|
||||||
|
- ajout d’une image sans rechargement de la page ni perte des textes en cours de saisie ;
|
||||||
|
- présentation des réseaux sociaux sous forme de liens identifiables, colorés et accessibles.
|
||||||
|
|
||||||
## [1.0.0-rc.17] — 2026-09-08
|
## [1.0.0-rc.17] — 2026-09-08
|
||||||
|
|
||||||
### Interface mobile et médias publics
|
### Interface mobile et médias publics
|
||||||
|
|
|
||||||
|
|
@ -77,3 +77,7 @@ Création d’un parcours de mise à jour commun à l’interface et à la ligne
|
||||||
## `1.0.0-rc.17` — Utilisation mobile
|
## `1.0.0-rc.17` — Utilisation mobile
|
||||||
|
|
||||||
Remplacement de la barre de navigation horizontale par un menu mobile repliable et fiabilisation des permissions appliquées aux nouvelles photos publiques.
|
Remplacement de la barre de navigation horizontale par un menu mobile repliable et fiabilisation des permissions appliquées aux nouvelles photos publiques.
|
||||||
|
|
||||||
|
## `1.0.0-rc.18` — Finitions du site public
|
||||||
|
|
||||||
|
Déplacement des informations légales dans le pied de page, insertion guidée des images sans perdre les textes en cours d’édition et identification visuelle des réseaux sociaux.
|
||||||
|
|
|
||||||
|
|
@ -200,7 +200,7 @@ L’option `--apply` ne doit être utilisée qu’après lecture des rapports. L
|
||||||
|
|
||||||
## État du projet
|
## État du projet
|
||||||
|
|
||||||
La candidate actuelle est `1.0.0-rc.17`. La branche 1.0 est gelée fonctionnellement : seuls les défauts bloquants découverts pendant les validations terrain sont corrigés avant `1.0.0`.
|
La candidate actuelle est `1.0.0-rc.18`. La branche 1.0 est gelée fonctionnellement : seuls les défauts bloquants découverts pendant les validations terrain sont corrigés avant `1.0.0`.
|
||||||
|
|
||||||
Le contrôle de stabilisation est disponible dans [docs/AUDIT-1.0.md](docs/AUDIT-1.0.md).
|
Le contrôle de stabilisation est disponible dans [docs/AUDIT-1.0.md](docs/AUDIT-1.0.md).
|
||||||
|
|
||||||
|
|
|
||||||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
||||||
1.0.0-rc.17
|
1.0.0-rc.18
|
||||||
|
|
|
||||||
|
|
@ -775,8 +775,22 @@ final class SettingsController
|
||||||
$filename = ImageService::storeUploaded((string) $upload['tmp_name'], $directory, 'site', 'photo');
|
$filename = ImageService::storeUploaded((string) $upload['tmp_name'], $directory, 'site', 'photo');
|
||||||
@chmod($directory, 0755);
|
@chmod($directory, 0755);
|
||||||
AuditService::log('public_site_image_uploaded', '/settings/public-site/image', 'Image du site public ajoutée');
|
AuditService::log('public_site_image_uploaded', '/settings/public-site/image', 'Image du site public ajoutée');
|
||||||
|
if (str_contains((string) ($_SERVER['HTTP_ACCEPT'] ?? ''), 'application/json')) {
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
echo json_encode([
|
||||||
|
'filename' => $filename,
|
||||||
|
'url' => '/media/site/' . rawurlencode($filename),
|
||||||
|
], JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
header('Location: /settings/public-site?image=' . rawurlencode($filename));
|
header('Location: /settings/public-site?image=' . rawurlencode($filename));
|
||||||
} catch (Throwable $e) {
|
} catch (Throwable $e) {
|
||||||
|
if (str_contains((string) ($_SERVER['HTTP_ACCEPT'] ?? ''), 'application/json')) {
|
||||||
|
http_response_code(422);
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
echo json_encode(['error' => SecurityService::publicError($e)], JSON_THROW_ON_ERROR);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
header('Location: /settings/public-site?error=' . rawurlencode(SecurityService::publicError($e)));
|
header('Location: /settings/public-site?error=' . rawurlencode(SecurityService::publicError($e)));
|
||||||
}
|
}
|
||||||
exit();
|
exit();
|
||||||
|
|
|
||||||
|
|
@ -31,10 +31,10 @@
|
||||||
<div class="col-lg-6">
|
<div class="col-lg-6">
|
||||||
<section class="card h-100">
|
<section class="card h-100">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<span class="badge bg-blue-lt mb-2">1.0.0-rc.17</span>
|
<span class="badge bg-blue-lt mb-2">1.0.0-rc.18</span>
|
||||||
<h3>Utilisation mobile</h3>
|
<h3>Site public</h3>
|
||||||
<p class="text-muted">Navigation repliable sur les petits écrans et publication fiable des nouvelles
|
<p class="text-muted">Informations légales accessibles sur chaque page, insertion guidée des images
|
||||||
photographies, y compris depuis l’application Android.</p>
|
dans les contenus et liens sociaux plus lisibles.</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,16 @@ $obfuscate = static fn (string$value): string => implode('', array_reverse(mb_st
|
||||||
<?php endif; ?><?php if ($settings['association_email']): ?><div class="contact-item">✉ <span
|
<?php endif; ?><?php if ($settings['association_email']): ?><div class="contact-item">✉ <span
|
||||||
class="contact-obfuscated"><?= h($obfuscate($settings['association_email'])) ?></span></div>
|
class="contact-obfuscated"><?= h($obfuscate($settings['association_email'])) ?></span></div>
|
||||||
<?php endif; ?></div>
|
<?php endif; ?></div>
|
||||||
<div class="chips" style="margin-top:25px">
|
<div class="social-links">
|
||||||
<?php foreach (['facebook' => 'Facebook','instagram' => 'Instagram','youtube' => 'YouTube','tiktok' => 'TikTok','whatsapp' => 'WhatsApp','linkedin' => 'LinkedIn','x' => 'X','snapchat' => 'Snapchat'] as $network => $label):
|
<?php $socialNetworks = [
|
||||||
|
'facebook' => ['Facebook','f'], 'instagram' => ['Instagram','◎'], 'youtube' => ['YouTube','▶'],
|
||||||
|
'tiktok' => ['TikTok','♪'], 'whatsapp' => ['WhatsApp','☎'], 'linkedin' => ['LinkedIn','in'],
|
||||||
|
'x' => ['X','𝕏'], 'snapchat' => ['Snapchat','👻'],
|
||||||
|
];
|
||||||
|
foreach ($socialNetworks as $network => [$label,$icon]):
|
||||||
$url = trim((string)($settings['public_site_' . $network . '_url'] ?? ''));
|
$url = trim((string)($settings['public_site_' . $network . '_url'] ?? ''));
|
||||||
if ($url === '') continue; ?>
|
if ($url === '') continue; ?>
|
||||||
<a class="button secondary" href="<?= h($url) ?>" rel="noopener noreferrer"><?= h($label) ?></a>
|
<a class="social-link social-<?= h($network) ?>" href="<?= h($url) ?>" target="_blank" rel="noopener noreferrer"><span class="social-icon" aria-hidden="true"><?= h($icon) ?></span><span><?= h($label) ?></span></a>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</div>
|
</div>
|
||||||
<?php if ($lat !== null && $lng !== null): ?><h2 style="margin-top:35px">
|
<?php if ($lat !== null && $lng !== null): ?><h2 style="margin-top:35px">
|
||||||
|
|
@ -27,8 +32,6 @@ $obfuscate = static fn (string$value): string => implode('', array_reverse(mb_st
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<div class="wrap" style="text-align:center"><a href="<?= h($siteUrl('legal')) ?>"><?= h(t('public_site.legal')) ?></a> ·
|
|
||||||
<a href="<?= h($siteUrl('privacy')) ?>"><?= h(t('public_site.privacy')) ?></a></div>
|
|
||||||
<?php if ($lat !== null && $lng !== null): ?><script src="/assets/vendor/leaflet/leaflet.js"></script>
|
<?php if ($lat !== null && $lng !== null): ?><script src="/assets/vendor/leaflet/leaflet.js"></script>
|
||||||
<script>
|
<script>
|
||||||
(() => {
|
(() => {
|
||||||
|
|
|
||||||
|
|
@ -373,6 +373,23 @@ if (!function_exists('public_age')) {
|
||||||
background: var(--paper)
|
background: var(--paper)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.social-links { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 25px }
|
||||||
|
.social-link {
|
||||||
|
display: inline-flex; align-items: center; gap: 9px; padding: 9px 14px;
|
||||||
|
border-radius: 999px; color: #fff; font-weight: 800; text-decoration: none;
|
||||||
|
box-shadow: 0 5px 14px rgba(0, 0, 0, .12); transition: .15s ease
|
||||||
|
}
|
||||||
|
.social-link:hover { color: #fff; filter: brightness(.92); transform: translateY(-1px) }
|
||||||
|
.social-icon { min-width: 1.15em; text-align: center; font-size: 1.05rem; line-height: 1 }
|
||||||
|
.social-facebook { background: #1877f2 }
|
||||||
|
.social-instagram { background: linear-gradient(135deg, #833ab4, #e1306c, #f77737) }
|
||||||
|
.social-youtube { background: #f00 }
|
||||||
|
.social-tiktok, .social-x { background: #111 }
|
||||||
|
.social-whatsapp { background: #128c7e }
|
||||||
|
.social-linkedin { background: #0a66c2 }
|
||||||
|
.social-snapchat { background: #fffc00; color: #111 }
|
||||||
|
.social-snapchat:hover { color: #111 }
|
||||||
|
|
||||||
footer {
|
footer {
|
||||||
margin-top: 55px;
|
margin-top: 55px;
|
||||||
border-top: 1px solid var(--line);
|
border-top: 1px solid var(--line);
|
||||||
|
|
@ -395,6 +412,9 @@ if (!function_exists('public_age')) {
|
||||||
font-size: .9rem
|
font-size: .9rem
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.footer-links { display: flex; flex-wrap: wrap; gap: 8px 16px; margin-top: 8px }
|
||||||
|
.footer-links a { color: var(--muted); font-weight: 700 }
|
||||||
|
|
||||||
.footer-powered a {
|
.footer-powered a {
|
||||||
color: var(--accent-dark);
|
color: var(--accent-dark);
|
||||||
font-weight: 800
|
font-weight: 800
|
||||||
|
|
@ -488,6 +508,10 @@ if (!function_exists('public_age')) {
|
||||||
<?= h($settings['association_rna']) ?></span><?php endif; ?><?php if (($settings['association_rna'] ?? '') !== '' && ($settings['association_siret'] ?? '') !== ''): ?>
|
<?= h($settings['association_rna']) ?></span><?php endif; ?><?php if (($settings['association_rna'] ?? '') !== '' && ($settings['association_siret'] ?? '') !== ''): ?>
|
||||||
· <?php endif; ?><?php if (($settings['association_siret'] ?? '') !== ''): ?><span><?= h(t('public_site.siret')) ?>
|
· <?php endif; ?><?php if (($settings['association_siret'] ?? '') !== ''): ?><span><?= h(t('public_site.siret')) ?>
|
||||||
: <?= h($settings['association_siret']) ?></span><?php endif; ?></div>
|
: <?= h($settings['association_siret']) ?></span><?php endif; ?></div>
|
||||||
|
<nav class="footer-links" aria-label="<?= h(t('public_site.legal_privacy')) ?>">
|
||||||
|
<a href="<?= h($siteUrl('legal')) ?>"><?= h(t('public_site.legal')) ?></a>
|
||||||
|
<a href="<?= h($siteUrl('privacy')) ?>"><?= h(t('public_site.privacy')) ?></a>
|
||||||
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
<div class="footer-powered"><?= h(t('public_site.powered_by')) ?> <a
|
<div class="footer-powered"><?= h(t('public_site.powered_by')) ?> <a
|
||||||
href="https://globinours.fr" target="_blank"
|
href="https://globinours.fr" target="_blank"
|
||||||
|
|
|
||||||
|
|
@ -11,20 +11,20 @@
|
||||||
<div class="card mb-3">
|
<div class="card mb-3">
|
||||||
<div class="card-header"><h3 class="card-title">🖼️ <?= h(t('public_site.image_library')) ?></h3></div>
|
<div class="card-header"><h3 class="card-title">🖼️ <?= h(t('public_site.image_library')) ?></h3></div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<form method="post" action="/settings/public-site/image" enctype="multipart/form-data" class="row g-2 align-items-end mb-3">
|
<form id="public-image-upload" method="post" action="/settings/public-site/image" enctype="multipart/form-data" class="row g-2 align-items-end mb-3">
|
||||||
<input type="hidden" name="csrf" value="<?= h(Auth::csrf()) ?>">
|
<input type="hidden" name="csrf" value="<?= h(Auth::csrf()) ?>">
|
||||||
<div class="col-md"><label class="form-label"><?= h(t('public_site.image_file')) ?></label><input class="form-control" type="file" name="public_site_image" accept="image/jpeg,image/png,image/webp" required></div>
|
<div class="col-md"><label class="form-label"><?= h(t('public_site.image_file')) ?></label><input class="form-control" type="file" name="public_site_image" accept="image/jpeg,image/png,image/webp" required></div>
|
||||||
<div class="col-md-auto"><button class="btn btn-outline-primary"><?= h(t('public_site.image_upload')) ?></button></div>
|
<div class="col-md-auto"><button class="btn btn-outline-primary"><?= h(t('public_site.image_upload')) ?></button></div>
|
||||||
</form>
|
</form>
|
||||||
<div class="form-hint mb-3"><?= h(t('public_site.image_help')) ?></div>
|
<div class="form-hint mb-3" id="public-image-help"><?= h(t('public_site.image_help')) ?></div>
|
||||||
<?php if (!$images): ?><div class="text-muted"><?= h(t('public_site.image_none')) ?></div><?php else: ?>
|
<div class="text-muted<?= $images ? ' d-none' : '' ?>" id="public-image-empty"><?= h(t('public_site.image_none')) ?></div>
|
||||||
<div class="row g-3">
|
<div class="row g-3" id="public-image-library">
|
||||||
<?php foreach ($images as $image): ?><div class="col-6 col-md-3 col-xl-2">
|
<?php foreach ($images as $image): ?><div class="col-6 col-md-3 col-xl-2">
|
||||||
<div class="card h-100"><img src="<?= h($image['url']) ?>" alt="" class="card-img-top" style="height:110px;object-fit:cover">
|
<div class="card h-100"><img src="<?= h($image['url']) ?>" alt="" class="card-img-top" style="height:110px;object-fit:cover">
|
||||||
<div class="card-body p-2"><button type="button" class="btn btn-sm btn-outline-secondary w-100 public-image-insert" data-markup="<?= h('') ?>"><?= h(t('public_site.image_insert')) ?></button></div>
|
<div class="card-body p-2"><button type="button" class="btn btn-sm btn-outline-secondary w-100 public-image-insert" data-url="<?= h($image['url']) ?>"><?= h(t('public_site.image_insert')) ?></button></div>
|
||||||
</div>
|
</div>
|
||||||
</div><?php endforeach; ?>
|
</div><?php endforeach; ?>
|
||||||
</div><?php endif; ?>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<form method="post" action="/settings/public-site/save"><input type="hidden" name="csrf" value="<?= h(Auth::csrf()) ?>">
|
<form method="post" action="/settings/public-site/save"><input type="hidden" name="csrf" value="<?= h(Auth::csrf()) ?>">
|
||||||
|
|
@ -162,6 +162,17 @@
|
||||||
</form>
|
</form>
|
||||||
<script>
|
<script>
|
||||||
let activePublicEditor = null;
|
let activePublicEditor = null;
|
||||||
|
const imageLibrary = document.getElementById('public-image-library');
|
||||||
|
const imageHelp = document.getElementById('public-image-help');
|
||||||
|
const insertImage = (button) => {
|
||||||
|
const textarea = activePublicEditor || document.querySelector('.public-rich-text');
|
||||||
|
if (!textarea) return;
|
||||||
|
const alt = window.prompt(<?= json_encode(t('public_site.image_alt_prompt'), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>, '');
|
||||||
|
if (alt === null) return;
|
||||||
|
const safeAlt = alt.replace(/[\[\]]/g, '').trim() || <?= json_encode(t('public_site.image_default_alt'), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
|
||||||
|
textarea.setRangeText(`\n`, textarea.selectionStart, textarea.selectionEnd, 'end');
|
||||||
|
textarea.focus();
|
||||||
|
};
|
||||||
document.querySelectorAll('.public-rich-text').forEach((textarea) => {
|
document.querySelectorAll('.public-rich-text').forEach((textarea) => {
|
||||||
textarea.addEventListener('focus', () => { activePublicEditor = textarea; });
|
textarea.addEventListener('focus', () => { activePublicEditor = textarea; });
|
||||||
const toolbar = document.createElement('div');
|
const toolbar = document.createElement('div');
|
||||||
|
|
@ -181,15 +192,47 @@ document.querySelectorAll('.public-rich-text').forEach((textarea) => {
|
||||||
});
|
});
|
||||||
toolbar.append(button);
|
toolbar.append(button);
|
||||||
});
|
});
|
||||||
|
const imageButton = document.createElement('button');
|
||||||
|
imageButton.type = 'button';
|
||||||
|
imageButton.className = 'btn btn-sm btn-outline-primary';
|
||||||
|
imageButton.textContent = <?= json_encode('🖼️ ' . t('public_site.image_insert'), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
|
||||||
|
imageButton.addEventListener('click', () => {
|
||||||
|
activePublicEditor = textarea;
|
||||||
|
imageHelp.textContent = <?= json_encode(t('public_site.image_target_selected'), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
|
||||||
|
imageLibrary.scrollIntoView({behavior: 'smooth', block: 'center'});
|
||||||
|
});
|
||||||
|
toolbar.append(imageButton);
|
||||||
textarea.before(toolbar);
|
textarea.before(toolbar);
|
||||||
});
|
});
|
||||||
document.querySelectorAll('.public-image-insert').forEach((button) => {
|
document.querySelectorAll('.public-image-insert').forEach((button) => {
|
||||||
button.addEventListener('click', () => {
|
button.addEventListener('click', () => insertImage(button));
|
||||||
const textarea = activePublicEditor || document.querySelector('.public-rich-text');
|
|
||||||
if (!textarea) return;
|
|
||||||
const start = textarea.selectionStart;
|
|
||||||
textarea.setRangeText(button.dataset.markup + '\n', start, textarea.selectionEnd, 'end');
|
|
||||||
textarea.focus();
|
|
||||||
});
|
});
|
||||||
|
document.getElementById('public-image-upload').addEventListener('submit', async (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
const form = event.currentTarget;
|
||||||
|
const submit = form.querySelector('button');
|
||||||
|
submit.disabled = true;
|
||||||
|
try {
|
||||||
|
const response = await fetch(form.action, {method: 'POST', headers: {'Accept': 'application/json'}, body: new FormData(form)});
|
||||||
|
const data = await response.json();
|
||||||
|
if (!response.ok) throw new Error(data.error || <?= json_encode(t('public_site.image_invalid'), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>);
|
||||||
|
const column = document.createElement('div');
|
||||||
|
column.className = 'col-6 col-md-3 col-xl-2';
|
||||||
|
column.innerHTML = '<div class="card h-100"><img alt="" class="card-img-top" style="height:110px;object-fit:cover"><div class="card-body p-2"><button type="button" class="btn btn-sm btn-outline-secondary w-100 public-image-insert"></button></div></div>';
|
||||||
|
const image = column.querySelector('img');
|
||||||
|
const insert = column.querySelector('button');
|
||||||
|
image.src = data.url;
|
||||||
|
insert.dataset.url = data.url;
|
||||||
|
insert.textContent = <?= json_encode(t('public_site.image_insert'), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
|
||||||
|
insert.addEventListener('click', () => insertImage(insert));
|
||||||
|
imageLibrary.prepend(column);
|
||||||
|
document.getElementById('public-image-empty').classList.add('d-none');
|
||||||
|
form.reset();
|
||||||
|
insert.click();
|
||||||
|
} catch (error) {
|
||||||
|
window.alert(error.message);
|
||||||
|
} finally {
|
||||||
|
submit.disabled = false;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,7 @@ return [
|
||||||
'accounting.document_save_failed'=>'The document could not be saved.',
|
'accounting.document_save_failed'=>'The document could not be saved.',
|
||||||
'search.title'=>'Global search','search.placeholder'=>'Animal, contact, appointment or invoice…','search.action'=>'Search','search.hint'=>'Enter at least two characters.','search.none'=>'No accessible result.','search.open'=>'Open global search','search.shortcut'=>'Ctrl K','search.group.animals'=>'Animals','search.group.directory'=>'Directory','search.group.agenda'=>'Calendar','search.group.accounting'=>'Accounting','search.loading'=>'Searching…','search.all_results'=>'Show all results','search.error'=>'Search is temporarily unavailable.',
|
'search.title'=>'Global search','search.placeholder'=>'Animal, contact, appointment or invoice…','search.action'=>'Search','search.hint'=>'Enter at least two characters.','search.none'=>'No accessible result.','search.open'=>'Open global search','search.shortcut'=>'Ctrl K','search.group.animals'=>'Animals','search.group.directory'=>'Directory','search.group.agenda'=>'Calendar','search.group.accounting'=>'Accounting','search.loading'=>'Searching…','search.all_results'=>'Show all results','search.error'=>'Search is temporarily unavailable.',
|
||||||
'public_site.settings_heading'=>'Public website','public_site.theme_custom'=>'Custom','public_site.site_name'=>'Website name','public_site.custom_css'=>'Custom CSS','public_site.custom_css_help'=>'Used by the custom theme. Imports and external resources are blocked.','public_site.invalid_css'=>'The stylesheet contains a forbidden instruction.','public_site.routing_management_help'=>'Management stays at / and the public website at /site.','public_site.publish_animal'=>'Publish this animal on the public website','public_site.publish_animal_help'=>'The profile will only be visible when the animal is also available for adoption. Only public photos and the text below are used.','lab_reference.add_species'=>'Add a species to lab reports','lab_reference.copy_from'=>'Copy reference ranges from','lab_reference.target_species'=>'New species','lab_reference.copy_help'=>'Ranges are copied as a starting point and remain editable.','lab_reference.invalid_species'=>'Choose two different valid species.','lab_reference.nothing_copied'=>'This species already has these reference ranges.','statistics.hair_types'=>'Hair types','animals.filtered_by'=>'Filter: :value','service.icad.home_unavailable'=>'The I-CAD website is unavailable','service.icad.search_unavailable'=>'I-CAD search is unavailable','service.icad.record_unavailable'=>'The I-CAD record is unavailable',
|
'public_site.settings_heading'=>'Public website','public_site.theme_custom'=>'Custom','public_site.site_name'=>'Website name','public_site.custom_css'=>'Custom CSS','public_site.custom_css_help'=>'Used by the custom theme. Imports and external resources are blocked.','public_site.invalid_css'=>'The stylesheet contains a forbidden instruction.','public_site.routing_management_help'=>'Management stays at / and the public website at /site.','public_site.publish_animal'=>'Publish this animal on the public website','public_site.publish_animal_help'=>'The profile will only be visible when the animal is also available for adoption. Only public photos and the text below are used.','lab_reference.add_species'=>'Add a species to lab reports','lab_reference.copy_from'=>'Copy reference ranges from','lab_reference.target_species'=>'New species','lab_reference.copy_help'=>'Ranges are copied as a starting point and remain editable.','lab_reference.invalid_species'=>'Choose two different valid species.','lab_reference.nothing_copied'=>'This species already has these reference ranges.','statistics.hair_types'=>'Hair types','animals.filtered_by'=>'Filter: :value','service.icad.home_unavailable'=>'The I-CAD website is unavailable','service.icad.search_unavailable'=>'I-CAD search is unavailable','service.icad.record_unavailable'=>'The I-CAD record is unavailable',
|
||||||
'public_site.image_library'=>'Image library','public_site.image_file'=>'Image to add','public_site.image_upload'=>'Upload image','public_site.image_help'=>'Upload an image, place the cursor in a text field, then choose “Insert”. Accepted formats: JPEG, PNG and WebP.','public_site.image_none'=>'No image uploaded.','public_site.image_insert'=>'Insert','public_site.image_invalid'=>'Choose a valid image.',
|
'public_site.image_library'=>'Image library','public_site.image_file'=>'Image to add','public_site.image_upload'=>'Upload image','public_site.image_help'=>'From the text you want to enrich, choose “Insert”, then select an image. Accepted formats: JPEG, PNG and WebP.','public_site.image_none'=>'No image uploaded.','public_site.image_insert'=>'Insert','public_site.image_invalid'=>'Choose a valid image.','public_site.image_alt_prompt'=>'Briefly describe the image for people who cannot see it:','public_site.image_default_alt'=>'Association image','public_site.image_target_selected'=>'Text selected: now choose an image from the library.',
|
||||||
'rooms.duplicate'=>'A room with this name or code already exists.','settings.species_duplicate'=>'A species with this name or code already exists.',
|
'rooms.duplicate'=>'A room with this name or code already exists.','settings.species_duplicate'=>'A species with this name or code already exists.',
|
||||||
'settings.invalid_rna'=>'The RNA number must start with W followed by 9 digits.','settings.invalid_siret'=>'The SIRET number must contain 14 digits and be valid.','rooms.name_too_long'=>'A room name cannot exceed 100 characters.','rooms.box_name_too_long'=>'A box name cannot exceed 100 characters.','animal.name_too_long'=>'An animal name cannot exceed 150 characters.',
|
'settings.invalid_rna'=>'The RNA number must start with W followed by 9 digits.','settings.invalid_siret'=>'The SIRET number must contain 14 digits and be valid.','rooms.name_too_long'=>'A room name cannot exceed 100 characters.','rooms.box_name_too_long'=>'A box name cannot exceed 100 characters.','animal.name_too_long'=>'An animal name cannot exceed 150 characters.',
|
||||||
'pricing.adoption_fees_help'=>'Indicative amounts shared by all animals, according to their sterilisation status.','rooms.inactive_help'=>'An unchecked room becomes inactive, while its previous assignments remain in the history. Its internal code never changes.',
|
'pricing.adoption_fees_help'=>'Indicative amounts shared by all animals, according to their sterilisation status.','rooms.inactive_help'=>'An unchecked room becomes inactive, while its previous assignments remain in the history. Its internal code never changes.',
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,7 @@ return [
|
||||||
'accounting.document_save_failed'=>'Impossible d’enregistrer le document.',
|
'accounting.document_save_failed'=>'Impossible d’enregistrer le document.',
|
||||||
'search.title'=>'Recherche globale','search.placeholder'=>'Animal, contact, rendez-vous ou facture…','search.action'=>'Rechercher','search.hint'=>'Saisissez au moins deux caractères.','search.none'=>'Aucun résultat accessible.','search.open'=>'Ouvrir la recherche globale','search.shortcut'=>'Ctrl K','search.group.animals'=>'Animaux','search.group.directory'=>'Annuaire','search.group.agenda'=>'Agenda','search.group.accounting'=>'Comptabilité','search.loading'=>'Recherche…','search.all_results'=>'Afficher tous les résultats','search.error'=>'La recherche est momentanément indisponible.',
|
'search.title'=>'Recherche globale','search.placeholder'=>'Animal, contact, rendez-vous ou facture…','search.action'=>'Rechercher','search.hint'=>'Saisissez au moins deux caractères.','search.none'=>'Aucun résultat accessible.','search.open'=>'Ouvrir la recherche globale','search.shortcut'=>'Ctrl K','search.group.animals'=>'Animaux','search.group.directory'=>'Annuaire','search.group.agenda'=>'Agenda','search.group.accounting'=>'Comptabilité','search.loading'=>'Recherche…','search.all_results'=>'Afficher tous les résultats','search.error'=>'La recherche est momentanément indisponible.',
|
||||||
'public_site.settings_heading'=>'Site public','public_site.theme_custom'=>'Personnalisé','public_site.site_name'=>'Nom du site','public_site.custom_css'=>'CSS personnalisé','public_site.custom_css_help'=>'Actif avec le thème personnalisé. Les imports et les ressources externes sont bloqués.','public_site.invalid_css'=>'La feuille de style contient une instruction interdite.','public_site.routing_management_help'=>'La gestion reste sur / et le site public sur /site.','public_site.publish_animal'=>'Publier cet animal sur le site public','public_site.publish_animal_help'=>'La fiche ne sera visible que si l’animal est également disponible à l’adoption. Seules les photos publiques et le texte ci-dessous seront utilisés.','lab_reference.add_species'=>'Ajouter une espèce aux bilans','lab_reference.copy_from'=>'Copier les références de','lab_reference.target_species'=>'Nouvelle espèce','lab_reference.copy_help'=>'Les intervalles sont copiés comme point de départ, puis restent modifiables.','lab_reference.invalid_species'=>'Choisissez deux espèces différentes et valides.','lab_reference.nothing_copied'=>'Cette espèce possède déjà ces références.','statistics.hair_types'=>'Types de poil','animals.filtered_by'=>'Filtre : :value','service.icad.home_unavailable'=>'Le site I-CAD est indisponible','service.icad.search_unavailable'=>'La recherche I-CAD est indisponible','service.icad.record_unavailable'=>'La fiche I-CAD est indisponible',
|
'public_site.settings_heading'=>'Site public','public_site.theme_custom'=>'Personnalisé','public_site.site_name'=>'Nom du site','public_site.custom_css'=>'CSS personnalisé','public_site.custom_css_help'=>'Actif avec le thème personnalisé. Les imports et les ressources externes sont bloqués.','public_site.invalid_css'=>'La feuille de style contient une instruction interdite.','public_site.routing_management_help'=>'La gestion reste sur / et le site public sur /site.','public_site.publish_animal'=>'Publier cet animal sur le site public','public_site.publish_animal_help'=>'La fiche ne sera visible que si l’animal est également disponible à l’adoption. Seules les photos publiques et le texte ci-dessous seront utilisés.','lab_reference.add_species'=>'Ajouter une espèce aux bilans','lab_reference.copy_from'=>'Copier les références de','lab_reference.target_species'=>'Nouvelle espèce','lab_reference.copy_help'=>'Les intervalles sont copiés comme point de départ, puis restent modifiables.','lab_reference.invalid_species'=>'Choisissez deux espèces différentes et valides.','lab_reference.nothing_copied'=>'Cette espèce possède déjà ces références.','statistics.hair_types'=>'Types de poil','animals.filtered_by'=>'Filtre : :value','service.icad.home_unavailable'=>'Le site I-CAD est indisponible','service.icad.search_unavailable'=>'La recherche I-CAD est indisponible','service.icad.record_unavailable'=>'La fiche I-CAD est indisponible',
|
||||||
'public_site.image_library'=>'Bibliothèque d’images','public_site.image_file'=>'Image à ajouter','public_site.image_upload'=>'Ajouter l’image','public_site.image_help'=>'Ajoutez une image, placez le curseur dans un texte puis choisissez « Insérer ». Formats acceptés : JPEG, PNG et WebP.','public_site.image_none'=>'Aucune image ajoutée.','public_site.image_insert'=>'Insérer','public_site.image_invalid'=>'Choisissez une image valide.',
|
'public_site.image_library'=>'Bibliothèque d’images','public_site.image_file'=>'Image à ajouter','public_site.image_upload'=>'Ajouter l’image','public_site.image_help'=>'Depuis le texte à enrichir, choisissez « Insérer » puis sélectionnez une image. Formats acceptés : JPEG, PNG et WebP.','public_site.image_none'=>'Aucune image ajoutée.','public_site.image_insert'=>'Insérer','public_site.image_invalid'=>'Choisissez une image valide.','public_site.image_alt_prompt'=>'Décrivez brièvement l’image pour les personnes qui ne peuvent pas la voir :','public_site.image_default_alt'=>'Image de l’association','public_site.image_target_selected'=>'Texte sélectionné : choisissez maintenant une image dans la bibliothèque.',
|
||||||
'rooms.duplicate'=>'Une salle portant ce nom ou ce code existe déjà.','settings.species_duplicate'=>'Une espèce portant ce nom ou ce code existe déjà.',
|
'rooms.duplicate'=>'Une salle portant ce nom ou ce code existe déjà.','settings.species_duplicate'=>'Une espèce portant ce nom ou ce code existe déjà.',
|
||||||
'settings.invalid_rna'=>'Le numéro RNA doit commencer par W et être suivi de 9 chiffres.','settings.invalid_siret'=>'Le numéro SIRET doit contenir 14 chiffres et être valide.','rooms.name_too_long'=>'Le nom d’une salle ne peut pas dépasser 100 caractères.','rooms.box_name_too_long'=>'Le nom d’un box ne peut pas dépasser 100 caractères.','animal.name_too_long'=>'Le nom d’un animal ne peut pas dépasser 150 caractères.',
|
'settings.invalid_rna'=>'Le numéro RNA doit commencer par W et être suivi de 9 chiffres.','settings.invalid_siret'=>'Le numéro SIRET doit contenir 14 chiffres et être valide.','rooms.name_too_long'=>'Le nom d’une salle ne peut pas dépasser 100 caractères.','rooms.box_name_too_long'=>'Le nom d’un box ne peut pas dépasser 100 caractères.','animal.name_too_long'=>'Le nom d’un animal ne peut pas dépasser 150 caractères.',
|
||||||
'pricing.adoption_fees_help'=>'Montants indicatifs communs à tous les animaux, selon leur état de stérilisation.','rooms.inactive_help'=>'Une salle décochée devient inactive, mais ses anciennes affectations restent dans l’historique. Son code interne ne change jamais.',
|
'pricing.adoption_fees_help'=>'Montants indicatifs communs à tous les animaux, selon leur état de stérilisation.','rooms.inactive_help'=>'Une salle décochée devient inactive, mais ses anciennes affectations restent dans l’historique. Son code interne ne change jamais.',
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue