diff --git a/docs/branding.md b/docs/branding.md index 1848681..d0f1a74 100644 --- a/docs/branding.md +++ b/docs/branding.md @@ -21,8 +21,10 @@ Everything that ties Orbit to a particular network or brand lives in } ``` -This changes the connect screen, the console title, the network icon, and the CTCP VERSION/SOURCE -replies the client sends. +`branding.name` and `branding.icon` reach **everywhere** the app identifies itself: the connect +screen, the browser tab title + **favicon**, desktop and Web Push notifications, the PWA install +prompt (name + description, localized), the iOS home-screen title, the network icon, and the CTCP +VERSION/SOURCE replies. Point `icon` at your own logo URL and the tab icon follows. ## Themes @@ -49,6 +51,11 @@ Hide whole features per deployment: { "features": { "push": true, "imageUpload": true, "register": false } } ``` -- `push` — the Web Push notifications row. -- `imageUpload` — the composer image button + paste/drag upload. -- `register` — the "create an account" tab. +- `push` — Web Push notifications: the Settings row **and** the re-subscribe-on-connect. +- `imageUpload` — the composer image button **and** paste / drag-drop upload. +- `register` — account self-service: the "Create account" button, the "Forgot password" link, and their FAQ entries (all hidden together). + +## Languages + +The UI and the bundled plugins are fully translated in **10 languages** (auto-detected). Pin a +default with [`defaults.lang`](https://orbit.tchatou.fr/docs/config/); users can switch in Settings. diff --git a/docs/config.md b/docs/config.md index 8597585..d1610e3 100644 --- a/docs/config.md +++ b/docs/config.md @@ -18,31 +18,47 @@ Merge rules: objects merge key-by-key; **arrays and scalars replace** wholesale. | Key | Type | What it does | |-----|------|--------------| | `server.url` | string | WebSocket URL of the IRCv3 server. | +| `server.guestIdent` | string | Ident for **guests** (not logged in) — e.g. `Foo!Invité@…`. Folded to ASCII (IRC idents are `[A-Za-z0-9._-]`, so `Invité`→`Invite`). Logged-in members use their own nick as the ident. | | `startup.channels` | string[] | Channels auto-joined (first = active). A `?channel=` URL param overrides. | -| `branding.name` | string | App/network name shown in the UI + CTCP VERSION. | -| `branding.icon` | string | Logo / favicon URL. | +| `branding.name` | string | App/network name — UI, tab title, **notifications**, **PWA install prompt**, and CTCP VERSION. | +| `branding.icon` | string | Logo/icon — connect screen, **browser favicon**, desktop + push notifications, PWA. | | `branding.url` | string | Homepage (used in CTCP VERSION/SOURCE). | | `branding.tagline` / `taglineEm` / `subtitle` | string | Connect-screen copy. | -| `branding.projectUrl` | string | "Powered by Orbit" link in Settings. | -| `turnstile.enabled` / `sitekey` | bool / string | Cloudflare Turnstile on registration. | -| `report.target` | string | Channel that user reports are sent to. | +| `branding.projectUrl` | string | Orbit project/source link (Settings → About). | +| `turnstile.enabled` | bool | Render the anti-bot challenge inline (Cloudflare Turnstile). `false` = show the server's verification step as a link, no Cloudflare script. Whether a challenge is *required* is decided server-side. | +| `turnstile.sitekey` | string | Public Turnstile site key. | +| `report.service` | string | Services pseudo-client that receives reports (e.g. `ReportServ`) — not blocked by a `+n` staff channel. Empty = fall back to `report.target`. | +| `report.target` | string | Channel that reports are sent to when `report.service` is empty. | | `defaults.theme` | string | `light` · `dark` · `yomirc` · `yomirc-dark`. | | `defaults.compact` / `sound` / `hideJoinQuit` / `clock24` | bool | Preset new-user prefs. | -| `defaults.lang` | string | Pin a default UI language (e.g. `en`, `fr`, `de`). Overrides browser detection for users who haven't picked one. Omit to auto-detect. | -| `features.push` / `imageUpload` / `register` | bool | Turn whole features on/off. | +| `defaults.lang` | string | Force the UI language (`en`, `fr`, `de`, `es`, `it`, `tr`, `ru`, `ne`, `pt-BR`, `pt-PT`). Empty = auto-detect from the browser. | +| `features.push` | bool | Web Push notifications — the Settings row **and** the re-subscribe-on-connect. | +| `features.imageUpload` | bool | Composer image button **and** paste / drag-drop upload. | +| `features.register` | bool | Account self-service: the "Create account" button, the "Forgot password" link, and their FAQ entries. | +| `plugins` | string[] | Plugin script URLs loaded at startup. See [Plugins](https://orbit.tchatou.fr/docs/plugins/). | > `defaults.*` only seed a user's preferences the **first** time — once someone changes a setting, > it's stored in their browser and the config no longer overrides it. +## Languages + +The UI ships fully translated in **10 languages** — English, French, German, Spanish, +Italian, Turkish, Russian, Nepali, and Brazilian + European Portuguese. Orbit +auto-detects the visitor's browser language; pin a default with `defaults.lang` (users +can still switch in **Settings**). The bundled plugins are localized too — see +[Plugins](https://orbit.tchatou.fr/docs/plugins/). + ## Example ```json { - "server": { "url": "wss://irc.example.org/ws/" }, + "server": { "url": "wss://irc.example.org/ws/", "guestIdent": "Guest" }, "startup": { "channels": ["#lobby", "#help"] }, - "branding": { "name": "ExampleChat", "tagline": "Chat with", "taglineEm": "everyone." }, + "branding": { "name": "ExampleChat", "icon": "https://example.org/logo.svg", "tagline": "Chat with", "taglineEm": "everyone." }, "turnstile":{ "enabled": false, "sitekey": "" }, - "defaults": { "theme": "dark", "compact": true }, - "features": { "register": false } + "report": { "service": "ReportServ", "target": "#staff" }, + "defaults": { "theme": "dark", "compact": true, "lang": "" }, + "features": { "push": true, "imageUpload": true, "register": false }, + "plugins": [] } ``` diff --git a/docs/overview.md b/docs/overview.md index af0b529..ff34b8d 100644 --- a/docs/overview.md +++ b/docs/overview.md @@ -22,6 +22,9 @@ network** and **rebranded** from a single `config.json`, without recompiling. [config.json reference](https://orbit.tchatou.fr/docs/config/). - **Installable PWA.** Mobile-first layout, offline app shell, and notifications when the tab is closed. +- **Speaks 10 languages.** The UI, system messages, and the bundled plugins are fully translated + and auto-detected (English, French, German, Spanish, Italian, Turkish, Russian, Nepali, and + Brazilian + European Portuguese). Pin a default with `defaults.lang`. - **Self-hostable.** A static build behind any web server. The project even runs its own git server with [push-to-deploy](https://orbit.tchatou.fr/docs/push-to-deploy/). - **Free software.** AGPL-3.0: use it, modify it, self-host it. diff --git a/docs/plugins.md b/docs/plugins.md index 72349bb..b12b623 100644 --- a/docs/plugins.md +++ b/docs/plugins.md @@ -80,6 +80,10 @@ instead. See **[Compiled plugins](https://orbit.tchatou.fr/docs/compiled-plugins | `orbit.irc.list()` | request the channel list | | `orbit.themes.current()/list()/set(id)` | read/set the theme | | `orbit.storage.get(key, def)/set(key, val)` | namespaced persistence | +| `orbit.config()` | the resolved runtime config (branding, features, …) | +| `orbit.i18n.language()` | current UI language code (e.g. `es`, `pt-BR`) | +| `orbit.i18n.t(key, opts?)` | translate an app locale key (supports `{{interpolation}}`) | +| `orbit.i18n.pick(table)` | pick a string from a `{ lang: text }` table by the current language | | `orbit.addUi(slot, render)` | add UI to a slot (returns a remover) | | `orbit.addSettingsSection({label, icon?, render})` | add a whole Settings section | | `orbit.addMessageDecorator(m => …)` | inline UI after every message's text; `m` = `{id, nick, text, raw, kind, ts, mine}` (`text` is formatting-stripped, `raw` keeps mIRC codes) | @@ -109,6 +113,28 @@ to reply/react. Every contributed slot, action and decorator renders inside its own error boundary, so a crashing plugin renders nothing instead of taking down the app. +### Localization (i18n) + +The UI ships in 10 languages — keep your plugin in step so it isn't stuck in one. Two ways: + +- **Self-contained** — carry a `{ lang: text }` table and let Orbit pick the current language: + + ```js + const LABEL = { en: 'Copy', fr: 'Copier', de: 'Kopieren' }; + orbit.html``; + ``` + +- **Interpolated** — `orbit.i18n.t('key', { name })` resolves an app locale key with + `{{placeholders}}`, so word order is correct per language. The four bundled plugins + (clock, copy, invite, games) use this. + +Read the string **at render time**, not once at load: plugin UI re-renders when the language +changes, so the text follows automatically. `orbit.i18n.language()` returns the current code +if you need to branch. + +> `orbit.i18n` and `orbit.config()` were added in **`apiVersion` 4** — guard with +> `if (orbit.apiVersion >= 4)` if your plugin must run on older builds. + ## Trust & security Plugins are **operator-controlled**: a deployment lists them in `config.json`, so