```
```css
.table { overflow-x: auto; } /* a wide table scrolls sideways inside its box: the page stays still */
```
Lo script della pagina (`pb_public\app.js`, estratti): l'accesso consegnato dal motore, il caricamento del file in una raccolta e la chiamata alla rotta dell'app, con il `401` che ricarica la pagina.
```js
const pb = new PocketBase("/");
const $ = (id) => document.getElementById(id);
const me = pb.authStore.record; // { id, email, name, role, ... }
let lastUpload = ""; // the "uploads" record of the last import, for the Undo button
```
```js
async function upload(ev) {
ev.preventDefault();
const btn = ev.target.querySelector("button");
btn.disabled = true;
$("undo").hidden = true;
$("result").textContent = t("importing");
try {
const rec = await pb.collection("uploads").create(new FormData(ev.target)); // the file goes into the collection
const res = await pb.send("/api/pricelist/import/" + rec.id, { method: "POST" }); // the app's route imports it
$("result").textContent = t("done", res);
lastUpload = rec.id;
$("undo").hidden = false;
ev.target.reset();
list();
summary();
} catch (err) {
if (err?.status === 401) return location.reload(); // session expired: the engine signs in again from the portal
$("result").textContent = err.response?.message || err.message;
const res = err.response?.result; // the rows to fix, with row number and reason
if (res?.errors?.length) {
const ul = document.createElement("ul");
for (const r of res.errors) ul.appendChild(document.createElement("li")).textContent = `${t("row", r)}${r.column ? " (" + r.column + ")" : ""}: ${r.reason}`;
$("result").appendChild(ul);
}
} finally {
btn.disabled = false;
}
}
```
## 10. Lingue e testi
1. **Un'app può essere in qualsiasi lingua**: quella delle persone che la usano, o più d'una se la richiesta lo dice. Il motore stesso parla prima inglese e accanto italiano. I testi per le persone sono semplici, senza gergo tecnico, anche negli stati vuoti («Nessun articolo»).
2. **I nomi tecnici e i commenti del codice sono in inglese**, qualunque sia la lingua dell'app: raccolte, campi, ruoli, rotte, file, variabili, funzioni, commenti. I nomi che vedono le persone si traducono nella pagina (il ruolo `manager` si mostra come «responsabile» a chi usa l'italiano).
3. **Quale lingua mostrare.** Segui la scelta fatta nel portale: il selettore della lingua del portale scrive il cookie `orch_lang` (`en` o `it`) in http e `__Host-orch_lang` (Secure, `Path=/`) in https, valido su tutto il motore e anche sulle porte delle app; poi la lingua del browser; poi la lingua principale dell'app. Un'app legge tutti e due i nomi (prima quello con `__Host-`) e, se ha un suo selettore, scrive quello giusto per lo schema della pagina (`orch_lang=it; path=/; max-age=31536000; SameSite=Lax` in http, `__Host-orch_lang=it; path=/; max-age=31536000; SameSite=Lax; Secure` in https), così la scelta è la stessa dappertutto. Nell'app di esempio:
```js
function pickLang() {
const ck = (n) => (document.cookie.match(new RegExp('(?:^|;\\s*)' + n + '=(en|it)\\b')) || [])[1];
const chosen = ck('__Host-orch_lang') || ck('orch_lang');
const browser = (navigator.languages || [navigator.language || ""]).map((l) => l.slice(0, 2).toLowerCase()).find((l) => TEXTS[l]);
return chosen || browser || "en";
}
```
4. **Messaggi del server nella lingua della pagina.** La pagina dice la sua lingua all'SDK di PocketBase (`pb.lang = lang`), che la manda nell'intestazione `Accept-Language` di ogni richiesta; le rotte dell'app la leggono con `e.request.header.get("Accept-Language")` e rispondono in quella lingua (vedi `lib/rules.js` nella sezione 7.1).
5. Numeri e date nel formato della lingua: `Intl.NumberFormat(lang === "it" ? "it-IT" : "en-US", …)`, `toLocaleDateString(…)`.
6. I messaggi d'errore dicono cosa fare: «Solo il responsabile del magazzino può farlo», non «403 Forbidden».
7. I messaggi che gli script ricevono dal motore (strumenti `$orch`, recinto) sono in inglese: sono per chi scrive l'app. Se la persona li deve vedere, l'app scrive un suo messaggio nella lingua della persona.
## 11. Strumenti del motore (`$orch`)
Il motore ha una cassetta di strumenti standard scritti in Go: componenti pronti per i lavori pesanti, che le app usano dagli script JS e, quando l'IT lo permette, dalle loro pagine, senza servizi esterni, senza programmi da avviare e senza librerie da aggiungere. Prima di scrivere a mano un lettore di file, un generatore di documenti o un invio di email, guarda se lo strumento c'è.
Regole comuni a tutti gli strumenti:
1. **Si attivano per app**, in `app.json`: `"tools": ["import"]`. Un Proprietario li può attivare anche dalla console del motore (pagina dell'app, scheda Configurazione). Uno strumento non attivo chiamato da uno script dà un errore che spiega come attivarlo. Elenca solo gli strumenti che l'app usa davvero. I nomi degli strumenti, delle funzioni, delle opzioni e dei risultati sono in inglese, come in questo capitolo: i nomi italiani come `$orch.importa` negli script non valgono.
2. **Si usano dagli script dell'app** (`pb_hooks` e `pb_migrations`) attraverso l'oggetto globale `$orch`: `$orch.