docs+fix: nettoyage post-migration et conformité svelte-autofixer

- CONTRIBUTING.md et SEO.md réécrits pour la structure SvelteKit
- README : section documents de référence (charte, recette, AGENTS.md, guides Svelte 5)
- Liens internes via resolve()/asset() de $app/paths, externes en rel="external"
- Seo.svelte : JSON-LD construits dans le script (</script> échappé)
- Titres de sections : {' '} remplacé par &#32;
- Renommage svelte_core_bestpractices.md
- Passe svelte-autofixer : 0 issue restante hors {@html} first-party assumés
This commit is contained in:
sucupira
2026-07-21 08:41:55 -04:00
parent d0d80a105e
commit 30858926fa
28 changed files with 217 additions and 277 deletions
+3 -2
View File
@@ -1,11 +1,12 @@
<script lang="ts">
import { resolve } from '$app/paths';
import BrandIcon from '$lib/components/icons/BrandIcon.svelte';
import type { Bundle, Locale } from '$lib/i18n';
let { t, locale }: { t: Bundle; locale: Locale } = $props();
const year = new Date().getFullYear();
const legalHref = $derived(locale === 'en' ? '/en/legal-notice/' : '/mentions-legales/');
const legalPath = $derived(locale === 'en' ? '/en/legal-notice/' : '/mentions-legales/');
</script>
<footer>
@@ -14,7 +15,7 @@
<p class="footer-name">{t.site.name}</p>
<p class="footer-meta">© {year} {t.site.name} | {t.site.tagline}</p>
<p class="footer-legal">
<a href={legalHref}>{t.footer.legal_link}</a>
<a href={resolve(legalPath)}>{t.footer.legal_link}</a>
</p>
<div class="footer-socials">
<a href="mailto:kontak@o-k-i.net" title="Email" class="social-link" aria-label="Email">
+14 -9
View File
@@ -1,5 +1,6 @@
<script lang="ts">
import { page } from '$app/state';
import { resolve } from '$app/paths';
import { alternatePath, type Bundle, type Locale } from '$lib/i18n';
let { t, locale }: { t: Bundle; locale: Locale } = $props();
@@ -40,7 +41,7 @@
<nav aria-label={t.ui?.nav_label ?? 'Navigation principale'}>
<div class="nav-container">
<a href={locale === 'en' ? '/en/' : '/'} class="logo" onclick={closeMenu}>
<a href={resolve(locale === 'en' ? '/en/' : '/')} class="logo" onclick={closeMenu}>
<img src="/images/logo-512x512.png" alt="{t.site.shortName} Logo" class="logo-image" width="40" height="40" />
<span>{t.site.shortName}</span>
</a>
@@ -61,25 +62,29 @@
<ul class="nav-dropdown">
{#each item.children as child (child.text)}
<li>
<a
href={child.url}
target={child.external ? '_blank' : undefined}
rel={child.external ? 'noopener noreferrer' : undefined}
onclick={closeMenu}>{child.text}</a
>
{#if child.external}
<a
href={child.url}
target="_blank"
rel="external noopener noreferrer"
onclick={closeMenu}>{child.text}</a
>
{:else}
<a href={resolve(child.url)} onclick={closeMenu}>{child.text}</a>
{/if}
</li>
{/each}
</ul>
</li>
{:else}
<li>
<a href={item.url} onclick={closeMenu}>{item.text}</a>
<a href={resolve(item.url)} onclick={closeMenu}>{item.text}</a>
</li>
{/if}
{/each}
</ul>
<a href={altHref} class="lang-switch" hreflang={altLocale} onclick={rememberLangChoice}>
<a href={resolve(altHref)} class="lang-switch" hreflang={altLocale} onclick={rememberLangChoice}>
{t.ui.lang_switch_label}
</a>
+7 -2
View File
@@ -66,6 +66,11 @@
url: 'https://o-k-i.net'
}
});
// {@html} sur contenu first-party (JSON-LD généré au build, aucune donnée utilisateur).
// `<\/script>` : échappement requis pour ne pas fermer le bloc <script> du composant.
const orgJsonLdScript = $derived(`<script type="application/ld+json">${orgJsonLd}<\/script>`);
const djangokamJsonLdScript = `<script type="application/ld+json">${djangokamJsonLd}<\/script>`;
</script>
<svelte:head>
@@ -97,6 +102,6 @@
<meta name="twitter:image" content="https://o-k-i.net/images/logo-512x512.png" />
<meta name="twitter:image:alt" content="Logo {site.name}" />
{@html `<script type="application/ld+json">${orgJsonLd}</script>`}
{@html `<script type="application/ld+json">${djangokamJsonLd}</script>`}
{@html orgJsonLdScript}
{@html djangokamJsonLdScript}
</svelte:head>
+2 -2
View File
@@ -19,7 +19,7 @@
<section id="clients" class="section">
<div class="container">
<h2 class="section-title">
<KineticText as="span" text={t.clients.section_title} />{' '}
<KineticText as="span" text={t.clients.section_title} />&#32;
<span class="accent-text"
><KineticText as="span" text={t.clients.section_title_accent} /></span
>
@@ -28,7 +28,7 @@
<div class="clients-grid">
{#each t.clients.items as client (client.domain)}
<a href={client.url} target="_blank" rel="noopener noreferrer" class="client-card">
<a href={client.url} target="_blank" rel="external noopener noreferrer" class="client-card">
<div class="client-logo-container">
<ResponsiveImage
picture={LOGOS[client.logo]}
@@ -1,4 +1,5 @@
<script lang="ts">
// {@html} sur contenu first-party (JSON i18n du dépôt), aucune donnée utilisateur.
import BrandIcon from '$lib/components/icons/BrandIcon.svelte';
import KineticText from '$lib/components/motion/KineticText.svelte';
import type { Bundle, Locale } from '$lib/i18n';
+5 -4
View File
@@ -1,4 +1,5 @@
<script lang="ts">
// {@html} sur contenu first-party (JSON i18n du dépôt), aucune donnée utilisateur.
import KineticText from '$lib/components/motion/KineticText.svelte';
import ResponsiveImage from '$lib/components/ResponsiveImage.svelte';
import type { Bundle, Locale } from '$lib/i18n';
@@ -22,7 +23,7 @@
<div class="container">
<p class="djangokam-surtitre">{t.djangokam.surtitre}</p>
<h2 class="section-title">
<KineticText as="span" text={t.djangokam.titre} />{' '}
<KineticText as="span" text={t.djangokam.titre} />&#32;
<span class="accent-text"
><KineticText as="span" text={t.djangokam.titreAccent} /></span
>
@@ -48,7 +49,7 @@
</div>
<figure class="djangokam-duo">
<a href={t.djangokam.duo.url} target="_blank" rel="noopener noreferrer">
<a href={t.djangokam.duo.url} target="_blank" rel="external noopener noreferrer">
<ResponsiveImage
picture={POCHETTES[t.djangokam.duo.image]}
alt={t.djangokam.duo.alt}
@@ -77,7 +78,7 @@
<div class="djangokam-clips">
{#each t.djangokam.clips as clip (clip.titre)}
<figure class="djangokam-clip">
<a href={clip.url} target="_blank" rel="noopener noreferrer">
<a href={clip.url} target="_blank" rel="external noopener noreferrer">
<ResponsiveImage
picture={POCHETTES[clip.image]}
alt={clip.alt}
@@ -97,7 +98,7 @@
<a
href={bouton.url}
target="_blank"
rel="noopener noreferrer"
rel="external noopener noreferrer"
class="btn"
class:btn-solid={bouton.primary}
>
@@ -9,7 +9,7 @@
<section id="engagement" class="section">
<div class="container">
<h2 class="section-title">
<KineticText as="span" text={t.engagement.section_title} />{' '}
<KineticText as="span" text={t.engagement.section_title} />&#32;
<span class="accent-text"
><KineticText as="span" text={t.engagement.section_title_accent} /></span
>
+1 -1
View File
@@ -8,7 +8,7 @@
<section id="faq" class="section">
<div class="container">
<h2 class="section-title">
<KineticText as="span" text={t.faq.section_title} />{' '}
<KineticText as="span" text={t.faq.section_title} />&#32;
<span class="accent-text"
><KineticText as="span" text={t.faq.section_title_accent} /></span
>
+1 -1
View File
@@ -9,7 +9,7 @@
<section id="services-libres" class="section">
<div class="container">
<h2 class="section-title">
<KineticText as="span" text={t.services.section_title} />{' '}
<KineticText as="span" text={t.services.section_title} />&#32;
<span class="accent-text"
><KineticText as="span" text={t.services.section_title_accent} /></span
>
+1 -1
View File
@@ -8,7 +8,7 @@
<section id="hebergement" class="section">
<div class="container">
<h2 class="section-title">
<KineticText as="span" text={t.hosting.section_title} />{' '}
<KineticText as="span" text={t.hosting.section_title} />&#32;
<span class="accent-text"
><KineticText as="span" text={t.hosting.section_title_accent} /></span
>
@@ -104,7 +104,7 @@
class="bldg"
href={niyaj.url}
target="_blank"
rel="noopener noreferrer"
rel="external noopener noreferrer"
aria-label="{niyaj.name}{niyaj.platform}"
>
<g class="bldg-art">
@@ -139,7 +139,7 @@
class="bldg"
href={gade.url}
target="_blank"
rel="noopener noreferrer"
rel="external noopener noreferrer"
aria-label="{gade.name}{gade.platform}"
>
<g class="bldg-art">
@@ -172,7 +172,7 @@
class="bldg"
href={bokante.url}
target="_blank"
rel="noopener noreferrer"
rel="external noopener noreferrer"
aria-label="{bokante.name}{bokante.platform}"
>
<g class="bldg-art">
@@ -209,7 +209,7 @@
class="bldg"
href={kute.url}
target="_blank"
rel="noopener noreferrer"
rel="external noopener noreferrer"
aria-label="{kute.name}{kute.platform}"
>
<g class="bldg-art">
@@ -259,7 +259,7 @@
<span class="tag">{item.platform}</span>
</div>
<p class="vf-desc">{item.description}</p>
<a class="vf-link" href={item.url} target="_blank" rel="noopener noreferrer"
<a class="vf-link" href={item.url} target="_blank" rel="external noopener noreferrer"
>{item.linkText}</a
>
</li>
+1 -1
View File
@@ -9,7 +9,7 @@
<section id="mission" class="section">
<div class="container">
<h2 class="section-title">
<KineticText as="span" text={t.mission.section_title} />{' '}
<KineticText as="span" text={t.mission.section_title} />&#32;
<span class="accent-text"
><KineticText as="span" text={t.mission.section_title_accent} /></span
>
+2 -2
View File
@@ -19,7 +19,7 @@
<section id="partenaires" class="section">
<div class="container">
<h2 class="section-title">
<KineticText as="span" text={t.partners.section_title} />{' '}
<KineticText as="span" text={t.partners.section_title} />&#32;
<span class="accent-text"
><KineticText as="span" text={t.partners.section_title_accent} /></span
>
@@ -41,7 +41,7 @@
<p class="partner-description">{partner.description}</p>
<div class="partner-links">
{#each partner.links as link (link.url)}
<a href={link.url} target="_blank" rel="noopener noreferrer" class="partner-link">
<a href={link.url} target="_blank" rel="external noopener noreferrer" class="partner-link">
{link.text}
</a>
{/each}
+2 -2
View File
@@ -9,7 +9,7 @@
<section id="projets" class="section">
<div class="container">
<h2 class="section-title">
<KineticText as="span" text={t.projects.section_title} />{' '}
<KineticText as="span" text={t.projects.section_title} />&#32;
<span class="accent-text"
><KineticText as="span" text={t.projects.section_title_accent} /></span
>
@@ -17,7 +17,7 @@
<div class="project-cards">
{#each t.projects.items as item (item.name)}
<a href={item.url} target="_blank" rel="noopener noreferrer" class="project-card">
<a href={item.url} target="_blank" rel="external noopener noreferrer" class="project-card">
<div class="project-icon">
<svg class="icon" aria-hidden="true"
><use href="/icons.svg#{pictoFor(item.icon)}" /></svg
+1 -1
View File
@@ -9,7 +9,7 @@
<section id="solutions" class="section">
<div class="container">
<h2 class="section-title">
<KineticText as="span" text={t.publics.section_title} />{' '}
<KineticText as="span" text={t.publics.section_title} />&#32;
<span class="accent-text"
><KineticText as="span" text={t.publics.section_title_accent} /></span
>
+4 -2
View File
@@ -4,6 +4,8 @@
* FR par défaut ; EN sur /en/.
*/
import type { Pathname } from '$app/types';
export type Locale = 'fr' | 'en';
/** Bundle de traductions : une entrée par fichier JSON de section. */
@@ -39,7 +41,7 @@ export function localeFromPath(pathname: string): Locale {
}
/** Correspondances des pages entre locales (URLs historiques préservées). */
const PATH_PAIRS: Array<[fr: string, en: string]> = [
const PATH_PAIRS: Array<[fr: Pathname, en: Pathname]> = [
['/', '/en/'],
['/dons/', '/en/donate/'],
['/presse/', '/en/press/'],
@@ -47,7 +49,7 @@ const PATH_PAIRS: Array<[fr: string, en: string]> = [
];
/** Chemin équivalent dans l'autre locale (sans le hash). */
export function alternatePath(pathname: string, target: Locale): string {
export function alternatePath(pathname: string, target: Locale): Pathname {
for (const [fr, en] of PATH_PAIRS) {
if (pathname === fr) return target === 'en' ? en : fr;
if (pathname === en) return target === 'fr' ? fr : en;
+2 -1
View File
@@ -1,5 +1,6 @@
<script lang="ts">
import { page } from '$app/state';
import { resolve } from '$app/paths';
const status = $derived(page.status);
const is404 = $derived(status === 404);
@@ -19,7 +20,7 @@
{:else}
<p class="fr">{page.error?.message ?? 'Une erreur est survenue.'}</p>
{/if}
<a class="btn" href="/">Retour à l'accueil</a>
<a class="btn" href={resolve('/')}>Retour à l'accueil</a>
</section>
<style>
+5 -4
View File
@@ -1,4 +1,5 @@
<script lang="ts">
// {@html} sur contenu first-party (JSON i18n du dépôt), aucune donnée utilisateur.
import Seo from '$lib/components/Seo.svelte';
import donations from '$lib/data/donations.json';
import { getBundle } from '$lib/i18n';
@@ -46,7 +47,7 @@
<div class="donation-grid">
{#each donations.oneTime as d (d.url)}
<a href={d.url} class="donation-amount" target="_blank" rel="noopener noreferrer">
<a href={d.url} class="donation-amount" target="_blank" rel="external noopener noreferrer">
{d.amount}{t.support.one_time_suffix}
</a>
{/each}
@@ -86,7 +87,7 @@
href={d.url}
class="donation-amount monthly"
target="_blank"
rel="noopener noreferrer"
rel="external noopener noreferrer"
>
{d.amount}{t.support.monthly_suffix}
</a>
@@ -126,7 +127,7 @@
<a
href={donations.liberapay}
target="_blank"
rel="noopener noreferrer"
rel="external noopener noreferrer"
class="alt-donation-card alt-donation-card--liberapay"
>
<svg class="icon alt-donation-icon" aria-hidden="true"
@@ -139,7 +140,7 @@
<a
href={donations.kofi}
target="_blank"
rel="noopener noreferrer"
rel="external noopener noreferrer"
class="alt-donation-card alt-donation-card--kofi"
>
<svg class="icon alt-donation-icon" aria-hidden="true"
+5 -4
View File
@@ -1,4 +1,5 @@
<script lang="ts">
// {@html} sur contenu first-party (JSON i18n du dépôt), aucune donnée utilisateur.
import Seo from '$lib/components/Seo.svelte';
import donations from '$lib/data/donations.json';
import { getBundle } from '$lib/i18n';
@@ -46,7 +47,7 @@
<div class="donation-grid">
{#each donations.oneTime as d (d.url)}
<a href={d.url} class="donation-amount" target="_blank" rel="noopener noreferrer">
<a href={d.url} class="donation-amount" target="_blank" rel="external noopener noreferrer">
{d.amount}{t.support.one_time_suffix}
</a>
{/each}
@@ -86,7 +87,7 @@
href={d.url}
class="donation-amount monthly"
target="_blank"
rel="noopener noreferrer"
rel="external noopener noreferrer"
>
{d.amount}{t.support.monthly_suffix}
</a>
@@ -126,7 +127,7 @@
<a
href={donations.liberapay}
target="_blank"
rel="noopener noreferrer"
rel="external noopener noreferrer"
class="alt-donation-card alt-donation-card--liberapay"
>
<svg class="icon alt-donation-icon" aria-hidden="true"
@@ -139,7 +140,7 @@
<a
href={donations.kofi}
target="_blank"
rel="noopener noreferrer"
rel="external noopener noreferrer"
class="alt-donation-card alt-donation-card--kofi"
>
<svg class="icon alt-donation-icon" aria-hidden="true"
+2 -1
View File
@@ -1,4 +1,5 @@
<script lang="ts">
import { resolve } from '$app/paths';
import Seo from '$lib/components/Seo.svelte';
import { getBundle } from '$lib/i18n';
@@ -212,7 +213,7 @@
<!-- Back to Home -->
<div class="legal-back">
<a href="/en/" class="btn btn-solid">{t.ui.back_home}</a>
<a href={resolve('/en/')} class="btn btn-solid">{t.ui.back_home}</a>
</div>
</div>
</section>
+5 -4
View File
@@ -1,4 +1,5 @@
<script lang="ts">
import { asset } from '$app/paths';
import ResponsiveImage from '$lib/components/ResponsiveImage.svelte';
import Seo from '$lib/components/Seo.svelte';
import KineticText from '$lib/components/motion/KineticText.svelte';
@@ -48,18 +49,18 @@
<div class="press-visual-grid">
{#each t.press.visuals as visual, i (visual.pdf)}
<div class="press-visual">
<a href={pictures[i].img.src} target="_blank" rel="noopener noreferrer">
<a href={asset(pictures[i].img.src)} target="_blank" rel="noopener noreferrer">
<ResponsiveImage picture={pictures[i]} alt={visual.caption} loading="lazy" />
<span class="press-visual-caption">{visual.caption}</span>
</a>
<a href="/assets/press/{visual.pdf}" class="press-visual-pdf-link">
<a href={asset(`/assets/press/${visual.pdf}`)} class="press-visual-pdf-link">
{t.press.visual_pdf_label}
</a>
</div>
{/each}
</div>
<div class="press-visuals-pdf">
<a href="/assets/press/{t.press.visuals_pdf_file}" class="btn btn-solid">
<a href={asset(`/assets/press/${t.press.visuals_pdf_file}`)} class="btn btn-solid">
{t.press.visuals_pdf_label}
</a>
</div>
@@ -71,7 +72,7 @@
<KineticText text={t.press.links_title} as="h2" class="press-section-title" />
<div class="press-links-list">
{#each t.press.links as link (link.url)}
<a href={link.url} target="_blank" rel="noopener noreferrer">{link.text}</a>
<a href={link.url} target="_blank" rel="external noopener noreferrer">{link.text}</a>
{/each}
</div>
</div>
+2 -1
View File
@@ -1,4 +1,5 @@
<script lang="ts">
import { resolve } from '$app/paths';
import Seo from '$lib/components/Seo.svelte';
import { getBundle } from '$lib/i18n';
@@ -221,7 +222,7 @@
<!-- Retour à l'accueil -->
<div class="legal-back">
<a href="/" class="btn btn-solid">{t.ui.back_home}</a>
<a href={resolve('/')} class="btn btn-solid">{t.ui.back_home}</a>
</div>
</div>
</section>
+5 -1
View File
@@ -1,3 +1,7 @@
<script lang="ts">
import { resolve } from '$app/paths';
</script>
<svelte:head>
<title>Hors ligne · ORGANISATION KA INTERNATIONALE</title>
<meta name="robots" content="noindex" />
@@ -12,7 +16,7 @@
rétablie.
</p>
<p class="fr" lang="en">This page is not available offline. It will be back as soon as your connection is restored.</p>
<a class="btn" href="/">Retour à l'accueil</a>
<a class="btn" href={resolve('/')}>Retour à l'accueil</a>
</section>
<style>
+5 -4
View File
@@ -1,4 +1,5 @@
<script lang="ts">
import { asset } from '$app/paths';
import ResponsiveImage from '$lib/components/ResponsiveImage.svelte';
import Seo from '$lib/components/Seo.svelte';
import KineticText from '$lib/components/motion/KineticText.svelte';
@@ -48,18 +49,18 @@
<div class="press-visual-grid">
{#each t.press.visuals as visual, i (visual.pdf)}
<div class="press-visual">
<a href={pictures[i].img.src} target="_blank" rel="noopener noreferrer">
<a href={asset(pictures[i].img.src)} target="_blank" rel="noopener noreferrer">
<ResponsiveImage picture={pictures[i]} alt={visual.caption} loading="lazy" />
<span class="press-visual-caption">{visual.caption}</span>
</a>
<a href="/assets/press/{visual.pdf}" class="press-visual-pdf-link">
<a href={asset(`/assets/press/${visual.pdf}`)} class="press-visual-pdf-link">
{t.press.visual_pdf_label}
</a>
</div>
{/each}
</div>
<div class="press-visuals-pdf">
<a href="/assets/press/{t.press.visuals_pdf_file}" class="btn btn-solid">
<a href={asset(`/assets/press/${t.press.visuals_pdf_file}`)} class="btn btn-solid">
{t.press.visuals_pdf_label}
</a>
</div>
@@ -71,7 +72,7 @@
<KineticText text={t.press.links_title} as="h2" class="press-section-title" />
<div class="press-links-list">
{#each t.press.links as link (link.url)}
<a href={link.url} target="_blank" rel="noopener noreferrer">{link.text}</a>
<a href={link.url} target="_blank" rel="external noopener noreferrer">{link.text}</a>
{/each}
</div>
</div>