```
```css
.table { overflow-x: auto; } /* a wide table scrolls sideways inside its box: the page stays still */
```
The page script (`pb_public\app.js`, excerpts): the session delivered by the engine, the upload of the file into a collection and the call to the app's route, with the `401` that reloads the page.
```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. Languages and texts
1. **An app can be in any language**: the one of the people who use it, or more than one if the request says so. The engine itself speaks English first and Italian next to it. Texts for people are plain, without technical jargon, also in the empty states (“No parts.”).
2. **Technical names and code comments are in English**, whatever the language of the app: collections, fields, roles, routes, files, variables, functions, comments. The names people see are translated in the page (the role `manager` is shown as “responsabile” to an Italian user).
3. **Which language to show.** Follow the choice made in the portal: the portal's language switch writes the cookie `orch_lang` (`en` or `it`) over http and `__Host-orch_lang` (Secure, `Path=/`) over https, valid on the whole engine and also on the apps' ports; then the browser's language; then the main language of the app. An app reads both names (the `__Host-` one first) and, when it has its own switch, writes the one for the scheme of the page (`orch_lang=it; path=/; max-age=31536000; SameSite=Lax` over http, `__Host-orch_lang=it; path=/; max-age=31536000; SameSite=Lax; Secure` over https), so the choice is the same everywhere. In the example app:
```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. **Server messages in the language of the page.** The page tells its language to the PocketBase SDK (`pb.lang = lang`), which sends it in the `Accept-Language` header of every request; the app's routes read it with `e.request.header.get("Accept-Language")` and answer in that language (see `lib/rules.js` in section 7.1).
5. Numbers and dates in the format of the language: `Intl.NumberFormat(lang === "it" ? "it-IT" : "en-US", …)`, `toLocaleDateString(…)`.
6. Error messages say what to do: “Only the warehouse manager can do this”, not “403 Forbidden”.
7. The messages that scripts receive from the engine (`$orch` tools, the jail) are in English: they are for whoever writes the app. If the person has to see them, the app writes its own message in the person's language.
## 11. Engine tools (`$orch`)
The engine has a toolbox of standard tools written in Go: ready-made components for the heavy jobs, which apps use from their JS scripts and, when IT allows it, from their pages, with no external services, no programs to start and no libraries to add. Before writing a file reader, a document generator or an email sender by hand, check whether the tool exists.
Rules common to all tools:
1. **They are turned on per app**, in `app.json`: `"tools": ["import"]`. An Owner can also turn them on from the engine console (app page, Configuration tab). A tool that is not turned on and is called by a script throws an error that explains how to turn it on. List only the tools the app really uses. The names of tools, functions, options and results are in English, as in this chapter: Italian names such as `$orch.importa` do not work in scripts.
2. **They are used from the app's scripts** (`pb_hooks` and `pb_migrations`) through the global object `$orch`: `$orch.