diff --git a/public/images/report/cover.webp b/public/images/report/cover.webp new file mode 100644 index 0000000..b7f8d67 Binary files /dev/null and b/public/images/report/cover.webp differ diff --git a/public/images/report/day2-04.webp b/public/images/report/day2-04.webp index ffa2b69..e852d9d 100644 Binary files a/public/images/report/day2-04.webp and b/public/images/report/day2-04.webp differ diff --git a/public/images/report/extra-1.webp b/public/images/report/extra-1.webp new file mode 100644 index 0000000..377a7e3 Binary files /dev/null and b/public/images/report/extra-1.webp differ diff --git a/public/images/report/extra-2.webp b/public/images/report/extra-2.webp new file mode 100644 index 0000000..386f170 Binary files /dev/null and b/public/images/report/extra-2.webp differ diff --git a/public/images/report/extra-3.webp b/public/images/report/extra-3.webp new file mode 100644 index 0000000..c4cc9ae Binary files /dev/null and b/public/images/report/extra-3.webp differ diff --git a/public/images/report/gallery-01.webp b/public/images/report/gallery-01.webp new file mode 100644 index 0000000..abe481e Binary files /dev/null and b/public/images/report/gallery-01.webp differ diff --git a/public/images/report/gallery-02.webp b/public/images/report/gallery-02.webp new file mode 100644 index 0000000..5e0f765 Binary files /dev/null and b/public/images/report/gallery-02.webp differ diff --git a/public/images/report/gallery-03.webp b/public/images/report/gallery-03.webp new file mode 100644 index 0000000..b3e1fac Binary files /dev/null and b/public/images/report/gallery-03.webp differ diff --git a/public/images/report/gallery-04.webp b/public/images/report/gallery-04.webp new file mode 100644 index 0000000..d2d7736 Binary files /dev/null and b/public/images/report/gallery-04.webp differ diff --git a/public/images/report/gallery-05.webp b/public/images/report/gallery-05.webp new file mode 100644 index 0000000..21d3860 Binary files /dev/null and b/public/images/report/gallery-05.webp differ diff --git a/public/images/report/gallery-06.webp b/public/images/report/gallery-06.webp new file mode 100644 index 0000000..7e6815d Binary files /dev/null and b/public/images/report/gallery-06.webp differ diff --git a/public/images/report/gallery-07.webp b/public/images/report/gallery-07.webp new file mode 100644 index 0000000..3395b95 Binary files /dev/null and b/public/images/report/gallery-07.webp differ diff --git a/public/images/report/gallery-08.webp b/public/images/report/gallery-08.webp new file mode 100644 index 0000000..9929a93 Binary files /dev/null and b/public/images/report/gallery-08.webp differ diff --git a/public/images/report/gallery-09.webp b/public/images/report/gallery-09.webp new file mode 100644 index 0000000..8907766 Binary files /dev/null and b/public/images/report/gallery-09.webp differ diff --git a/public/images/report/gallery-10.webp b/public/images/report/gallery-10.webp new file mode 100644 index 0000000..3b1b0fb Binary files /dev/null and b/public/images/report/gallery-10.webp differ diff --git a/public/images/report/gallery-11.webp b/public/images/report/gallery-11.webp new file mode 100644 index 0000000..f5d2879 Binary files /dev/null and b/public/images/report/gallery-11.webp differ diff --git a/public/images/report/gallery-12.webp b/public/images/report/gallery-12.webp new file mode 100644 index 0000000..796c079 Binary files /dev/null and b/public/images/report/gallery-12.webp differ diff --git a/public/images/report/gallery-13.webp b/public/images/report/gallery-13.webp new file mode 100644 index 0000000..14b4126 Binary files /dev/null and b/public/images/report/gallery-13.webp differ diff --git a/public/images/report/gallery-14.webp b/public/images/report/gallery-14.webp new file mode 100644 index 0000000..597ea07 Binary files /dev/null and b/public/images/report/gallery-14.webp differ diff --git a/public/images/report/gallery-15.webp b/public/images/report/gallery-15.webp new file mode 100644 index 0000000..3843460 Binary files /dev/null and b/public/images/report/gallery-15.webp differ diff --git a/public/images/report/gallery-16.webp b/public/images/report/gallery-16.webp new file mode 100644 index 0000000..e8a571e Binary files /dev/null and b/public/images/report/gallery-16.webp differ diff --git a/public/images/report/gallery-17.webp b/public/images/report/gallery-17.webp new file mode 100644 index 0000000..282d2e8 Binary files /dev/null and b/public/images/report/gallery-17.webp differ diff --git a/public/images/report/gallery-18.webp b/public/images/report/gallery-18.webp new file mode 100644 index 0000000..7f5d41e Binary files /dev/null and b/public/images/report/gallery-18.webp differ diff --git a/public/images/report/gallery-19.webp b/public/images/report/gallery-19.webp new file mode 100644 index 0000000..0ccebdb Binary files /dev/null and b/public/images/report/gallery-19.webp differ diff --git a/public/images/report/gallery-20.webp b/public/images/report/gallery-20.webp new file mode 100644 index 0000000..507a111 Binary files /dev/null and b/public/images/report/gallery-20.webp differ diff --git a/public/images/report/gallery-21.webp b/public/images/report/gallery-21.webp new file mode 100644 index 0000000..fb44743 Binary files /dev/null and b/public/images/report/gallery-21.webp differ diff --git a/public/images/report/gallery-22.webp b/public/images/report/gallery-22.webp new file mode 100644 index 0000000..efdc548 Binary files /dev/null and b/public/images/report/gallery-22.webp differ diff --git a/public/images/report/gallery-23.webp b/public/images/report/gallery-23.webp new file mode 100644 index 0000000..20d339f Binary files /dev/null and b/public/images/report/gallery-23.webp differ diff --git a/public/images/report/gallery-24.webp b/public/images/report/gallery-24.webp new file mode 100644 index 0000000..77566b4 Binary files /dev/null and b/public/images/report/gallery-24.webp differ diff --git a/public/images/report/speaker.webp b/public/images/report/speaker.webp new file mode 100644 index 0000000..e823570 Binary files /dev/null and b/public/images/report/speaker.webp differ diff --git a/public/reports/pycon-cameroon-2026-report-en.pdf b/public/reports/pycon-cameroon-2026-report-en.pdf index 36663d0..dd6e077 100644 Binary files a/public/reports/pycon-cameroon-2026-report-en.pdf and b/public/reports/pycon-cameroon-2026-report-en.pdf differ diff --git a/public/reports/pycon-cameroon-2026-report-fr.pdf b/public/reports/pycon-cameroon-2026-report-fr.pdf index 1b3c823..481e222 100644 Binary files a/public/reports/pycon-cameroon-2026-report-fr.pdf and b/public/reports/pycon-cameroon-2026-report-fr.pdf differ diff --git a/scripts/build-report-pdf.mjs b/scripts/build-report-pdf.mjs index 874f446..e143f8f 100644 --- a/scripts/build-report-pdf.mjs +++ b/scripts/build-report-pdf.mjs @@ -26,12 +26,13 @@ const work = mkdtempSync(join(tmpdir(), 'pycon-report-')); const asset = (path) => pathToFileURL(join(publicDir, path)).href; const photo = (day, n) => pathToFileURL(join(work, `${day}-${String(n).padStart(2, '0')}.jpg`)).href; +const extraPhoto = (name) => pathToFileURL(join(work, `${name}.jpg`)).href; // ponytail: Chrome embeds WebP uncompressed in PDFs, so photos go in as JPEG via macOS `sips`; swap for another converter off macOS. const convertPhotos = () => { const dir = join(publicDir, 'images/report'); for (const file of readdirSync(dir).filter((f) => f.endsWith('.webp'))) { - execFileSync('sips', ['-Z', '1400', '-s', 'format', 'jpeg', '-s', 'formatOptions', '75', join(dir, file), '--out', join(work, file.replace(/\.webp$/, '.jpg'))], { stdio: 'ignore' }); + execFileSync('sips', ['-Z', file.startsWith('gallery-') ? '600' : '1200', '-s', 'format', 'jpeg', '-s', 'formatOptions', '70', join(dir, file), '--out', join(work, file.replace(/\.webp$/, '.jpg'))], { stdio: 'ignore' }); } }; @@ -71,12 +72,13 @@ const build = (lang) => { `${r.programTitle} ${r.programHighlight}`, `${r.daysTitle} ${r.daysHighlight}`, `${r.sponsorsTitle} ${r.sponsorsHighlight}`, + `${r.pdf.galleryTitle} ${r.pdf.galleryHighlight}`, `${r.aidTitle} ${r.aidHighlight}`, `${r.partnersTitle} ${r.partnersHighlight}`, ]; const cover = page(` -
+
@@ -96,7 +98,7 @@ const build = (lang) => {
    ${contents.map((c, i) => `
  1. ${String(i + 1).padStart(2, '0')}${esc(c)}
  2. `).join('')}
- +
${sectionTitle(1, r.summaryTitle, r.summaryHighlight)} @@ -105,8 +107,9 @@ const build = (lang) => {

${esc(r.summaryP3)}

-
- +
+ +
`, { n: 2, footer }); const numbers = page(` @@ -124,7 +127,7 @@ const build = (lang) => {
${bars(r.speakersByCountry, speakerCountries.map(([country, count]) => ({ label: country ? r.countryNames?.[country] ?? country : r.countryNotListed, count })))} - +
`, { n: 3, footer }); const programme = page(` @@ -136,7 +139,7 @@ const build = (lang) => { ${bars(r.topicsTitle, sessionTopics.slice(0, 6).map(([label, count]) => ({ label: r.topicNames?.[label] ?? label, count })))}
- ${[['day1', 4], ['day2', 3], ['day2', 6], ['day3', 3]].map(([day, n]) => ``).join('')} + ${[['day3', 8], ['day1', 8], ['day2', 8], ['day1', 9]].map(([day, n]) => ``).join('')}
`, { n: 4, footer }); const days = DAYS.map((day, i) => { @@ -172,13 +175,19 @@ const build = (lang) => {

${esc(r.pdf.sponsorTiers)}

${tierBlocks}`, { n: 8, footer }); + const gallery = page(` + ${sectionTitle(6, r.pdf.galleryTitle, r.pdf.galleryHighlight)} + `, { n: 9, footer }); + const closing = page(` - ${sectionTitle(6, r.aidTitle, r.aidHighlight)} + ${sectionTitle(7, r.aidTitle, r.aidHighlight)}

${esc(r.aidSubtitle)}

${r.aid.map((v) => `

${esc(v.title)}

${esc(v.text)}

`).join('')}
- ${sectionTitle(7, r.partnersTitle, r.partnersHighlight)} + ${sectionTitle(8, r.partnersTitle, r.partnersHighlight)}

${esc(r.partnersSubtitle)}

${partnerCommunities.map((p) => `
${esc(p.name)}
`).join('')} @@ -193,7 +202,7 @@ const build = (lang) => {

${esc(r.pdf.closingTitle)}

-
`, { n: 9, cls: 'last' }); +
`, { n: 10, cls: 'last' }); return ` @@ -248,6 +257,10 @@ h3 { font-size: 13pt; margin-bottom: 3mm; } .foreword-photo { width: 100%; height: 62mm; object-fit: cover; object-position: center top; border-radius: 2mm; } .strip { margin-top: auto; padding-top: 6mm; display: grid; grid-template-columns: repeat(3, 1fr); gap: 3mm; } .strip img { width: 100%; height: 48mm; object-fit: cover; object-position: center top; border-radius: 2mm; } +.gallery { flex: 1; min-height: 0; display: grid; grid-template-columns: repeat(4, 1fr); grid-template-rows: repeat(6, 1fr); gap: 2mm; margin-top: 2mm; } +.gallery img { width: 100%; height: 100%; min-height: 0; object-fit: cover; object-position: center top; border-radius: 1.5mm; } +.strip.fill { flex: 1; min-height: 0; grid-template-rows: 1fr 1fr; } +.strip.fill img { height: 100%; } .strip.tall { flex: 1; min-height: 0; grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; } .strip.tall img { height: 100%; } @@ -308,7 +321,7 @@ h3 { font-size: 13pt; margin-bottom: 3mm; } -${[cover, foreword, numbers, programme, ...days, sponsors, closing].join('\n')} +${[cover, foreword, numbers, programme, ...days, sponsors, gallery, closing].join('\n')} `; }; diff --git a/src/i18n/en.json b/src/i18n/en.json index b4a3b08..fbf0f79 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -793,10 +793,10 @@ "titleHighlight": "Event Report", "subtitle": "Three days of Python, Ubuntu and open source in Yaound\u00e9 \u2014 what happened, who made it possible, and what comes next.", "meta": "September 17\u201319, 2026 \u00b7 Girafe H\u00f4tel, Yaound\u00e9, Cameroon", - "heroAlt": "Attendees of PyCon & UbuCon Cameroon 2026 gathered for the closing group photo", + "heroAlt": "A group of attendees in front of the PyCon & UbuCon Cameroon 2026 banner", "summaryTitle": "Executive", "summaryHighlight": "Summary", - "summaryP1": "PyCon Cameroon 2026 brought the country's Python community together for three days at the Girafe H\u00f4tel in Yaound\u00e9, from September 17 to 19. For the first time, the conference was paired with UbuCon Cameroon, a full day dedicated to Ubuntu and open source, co-organized with the local Canonical user group.", + "summaryP1": "PyCon Cameroon 2026, the first PyCon ever held in Cameroon, brought the country's Python community together for three days at the Girafe H\u00f4tel in Yaound\u00e9, from September 17 to 19. The conference was also paired with UbuCon Cameroon, a full day dedicated to Ubuntu and open source, co-organized with the local Canonical user group.", "summaryP2": "The programme mixed keynotes, talks across three parallel rooms, hands-on workshops, a bilingual panel on tech jobs and AI, lightning talks and community games. Sessions ran in English, French and both, reflecting the bilingual nature of Cameroon's tech scene.", "summaryP3": "None of this happens without our sponsors, partner communities, speakers and volunteers. This report is our thank-you, and an open account of what their support made possible.", "glanceTitle": "At a glance", @@ -855,7 +855,7 @@ "UbuCon Cameroon group photo at the sponsor wall", "A full room for UbuCon Cameroon", "The workshop hall, laptops open", - "A career talk in the main hall", + "Adonis Simo's opening keynote on building a SaaS in Africa", "Lunch together between workshops", "Attendees working through a hands-on workshop", "Friends made at UbuCon", @@ -923,7 +923,9 @@ { "title": "Transport", "text": "Travel costs to Yaound\u00e9 covered for speakers and grant recipients coming from other regions." } ], "pdf": { - "kicker": "Event Report", + "kicker": "Event Report \u00b7 First Edition", + "galleryTitle": "Photo", + "galleryHighlight": "Gallery", "edition": "PyCon & UbuCon Cameroon 2026", "contents": "Contents", "foreword": "Foreword", diff --git a/src/i18n/fr.json b/src/i18n/fr.json index 18ea90f..d49c1bd 100644 --- a/src/i18n/fr.json +++ b/src/i18n/fr.json @@ -794,10 +794,10 @@ "titleHighlight": "2026", "subtitle": "Trois jours de Python, d'Ubuntu et d'open source \u00e0 Yaound\u00e9 : ce qui s'est pass\u00e9, qui l'a rendu possible et la suite.", "meta": "17\u201319 septembre 2026 \u00b7 Girafe H\u00f4tel, Yaound\u00e9, Cameroun", - "heroAlt": "Les participants de PyCon & UbuCon Cameroon 2026 r\u00e9unis pour la photo de groupe de cl\u00f4ture", + "heroAlt": "Un groupe de participants devant la banni\u00e8re PyCon & UbuCon Cameroon 2026", "summaryTitle": "Synth\u00e8se", "summaryHighlight": "g\u00e9n\u00e9rale", - "summaryP1": "PyCon Cameroon 2026 a r\u00e9uni la communaut\u00e9 Python du pays pendant trois jours au Girafe H\u00f4tel de Yaound\u00e9, du 17 au 19 septembre. Pour la premi\u00e8re fois, la conf\u00e9rence \u00e9tait associ\u00e9e \u00e0 UbuCon Cameroon, une journ\u00e9e enti\u00e8re consacr\u00e9e \u00e0 Ubuntu et \u00e0 l'open source, co-organis\u00e9e avec le groupe d'utilisateurs Canonical local.", + "summaryP1": "PyCon Cameroon 2026, toute premi\u00e8re PyCon organis\u00e9e au Cameroun, a r\u00e9uni la communaut\u00e9 Python du pays pendant trois jours au Girafe H\u00f4tel de Yaound\u00e9, du 17 au 19 septembre. La conf\u00e9rence \u00e9tait aussi associ\u00e9e \u00e0 UbuCon Cameroon, une journ\u00e9e enti\u00e8re consacr\u00e9e \u00e0 Ubuntu et \u00e0 l'open source, co-organis\u00e9e avec le groupe d'utilisateurs Canonical local.", "summaryP2": "Le programme a m\u00eal\u00e9 keynotes, talks r\u00e9partis sur trois salles en parall\u00e8le, ateliers pratiques, un panel bilingue sur l'emploi tech et l'IA, des lightning talks et des jeux communautaires. Les sessions se sont tenues en anglais, en fran\u00e7ais et dans les deux langues, \u00e0 l'image de la sc\u00e8ne tech bilingue du Cameroun.", "summaryP3": "Rien de tout cela n'est possible sans nos sponsors, communaut\u00e9s partenaires, intervenants et b\u00e9n\u00e9voles. Ce rapport est notre fa\u00e7on de les remercier, et un compte rendu transparent de ce que leur soutien a permis.", "glanceTitle": "En bref", @@ -856,7 +856,7 @@ "Photo de groupe UbuCon Cameroon devant le mur des sponsors", "Une salle comble pour UbuCon Cameroon", "La salle des ateliers, ordinateurs ouverts", - "Un talk carri\u00e8re dans la grande salle", + "La keynote d'ouverture d'Adonis Simo sur la cr\u00e9ation d'un SaaS en Afrique", "D\u00e9jeuner ensemble entre deux ateliers", "Des participants en plein atelier pratique", "Des amiti\u00e9s n\u00e9es \u00e0 UbuCon", @@ -946,7 +946,9 @@ { "title": "Transport", "text": "Les frais de voyage vers Yaound\u00e9 pris en charge pour les speakers et boursiers venus d'autres r\u00e9gions." } ], "pdf": { - "kicker": "Rapport d'activit\u00e9", + "kicker": "Rapport d'activit\u00e9 \u00b7 Premi\u00e8re \u00e9dition", + "galleryTitle": "Galerie", + "galleryHighlight": "photos", "edition": "PyCon & UbuCon Cameroon 2026", "contents": "Sommaire", "foreword": "Avant-propos", diff --git a/src/pages/Report.jsx b/src/pages/Report.jsx index fdf246b..6ac39bf 100644 --- a/src/pages/Report.jsx +++ b/src/pages/Report.jsx @@ -61,7 +61,7 @@ const Report = () => {
- {t('report.heroAlt')} + {t('report.heroAlt')}