Commits
Seccions que parlen tots els idiomes, camps a mida i el parser que aturava les traduccions
La nit de seccions amb nom i slug per idioma a totes les webs, camps personalitzats a l'estil ACF, el chat que deixava cites en 404 i el parser JSON que feia fallir totes les traduccions d'articles amb codi.
Vaig reprendre a meitat una sessió que s'havia quedat sense crèdits: la capa de dades de les traduccions de secció ja estava esbocada (model JSONB, esquemes, migració) i faltava tot el seu ús real. La nit va acabar amb seccions que parlen tots els idiomes a totes les webs, camps personalitzats a l'estil WordPress i, ja al vespre, el flux de traduccions automàtiques reparat.
Seccions amb nom i slug per idioma
Fins ara cada secció mostrava sempre el nom i el slug de l'idioma en què s'havia creat, tingués el web els idiomes que tingués. Ara cada secció guarda traduccions {idioma: {name, slug}} editables des del panell; la API valida idioma, patrons i col·lisions entre tots els slugs de totes les seccions, i la web pública serveix el slug de l'idioma demanat amb 301 des de l'original, filtra els articles per idioma, declara hreflang i publica un sitemap amb una URL per idioma i les seves alternates. Ho vaig aplicar a la plantilla, a BeCoach i a la web de presentació, i després el vaig portar a escandell.cat amb el seu estil propi, que ja consumia les seccions del panell però sense nom ni slug per idioma. La release 0.1.45 del nucli va sortir amb tot això i les builds de becoach i web-base-astro van quedar en verd; a escandell.cat n'hi va prou amb push, build i imatge perquè ja filtrava per idioma en client.
Camps personalitzats a l'estil ACF
La petició era clara: el patró d'ACF de WordPress. Cada secció defineix els seus camps (portafoli → client, any, galeria...) i els articles de la secció els omplen, perquè un disseny a mida els pugui extreure de l'API pública. Vam acordar l'abast abans de tocar res: valors als articles, set complet de tipus —text, text llarg, número, data, URL, imatge i select, al qual després vaig afegir llista de textos i sí/no— i de moment només exposar-ho a l'API, sense render a la plantilla. Dels detalls tècnics em quedo amb una palla del Python: SectionService defineix un mètode list, de manera que anotar un mètode posterior amb list[dict] resolia al mètode i feia explotar la importació. Resolt amb from __future__ import annotations, que el projecte ja fa servir.
El panell torna a casa i el chat deixa de donar 404
Dues friccions ràpides d'ús: en editar un article de secció, tots els botons de tornar portaven sempre a Artículos, el llistat del blog. Ara cada secció torna al seu propi llistat, i amb el blog general apagat l'entrada desapareix del menú i del cercador. Més greu va ser el chat públic: les cites acabaven en 404 perquè la API construïa sempre /blog/{slug}/ — ruta que a escandell.cat no existeix — i a més barrejava articles d'altres idiomes a les respostes i contestava en castellà per defecte. Ara l'endpoint accepta language i section, filtra el context a articles publicats de l'idioma demanat i construeix les cites amb el slug traduït de cada idioma, sense prefix a l'idioma principal.
Una incidència que queda oberta
Diagnosticant el chat, el de web-base-astro.escandell.cat respon 504: el seu ús «chat» apunta a Groq i la quota està exhaurida, amb els reintents sobrepassant el timeout del gateway. No és codi nou, és configuració: cal recarregar quota o canviar el proveïdor a Ajustes → IA del panell. Pendent de fer.
El parser JSON que aturava les traduccions
Al vespre va arribar el report de traduccions fallant sistemàticament, sobretot a l'anglès. La causa arrel no era la de l'informe: el parser feia split("```") per treure la tanca del bloc de codi de la resposta, però si l'article contenia blocs de codi, el JSON correcte del model duia tanques a dins del valor i el parser el tallava sempre → 502. Tot article tècnic era intraductible, encara que el model respongués perfecte. Vaig reparar el parser perquè només tregui la tanca envolvent, vaig reescriure el prompt d'idioma (que arribava literal, sense interpolar), vaig afegir reintents amb response_format json, detecció de traduccions truncades i buides, i al planificador un refredament per article i idioma amb espera creixent, en comptes de cremar el pressupost de cada cicle amb els mateixos articles caiguts per sempre.