-
Notifications
You must be signed in to change notification settings - Fork 0
Customizing an Install
Two shops running the same Forge image can show different words on the same screen and different colors around them. That is deliberate: the words a manufacturer uses are their own, and an ERP that insists on its vocabulary loses arguments it should never have started. This page covers the wording layers, the language machinery behind them, and how far an install can be branded — plus the one boundary that branding does not cross.
Canonical detail: docs/functional-reference/app-shell.md for the theme system and the language selector, docs/functional-reference/account.md for the per-user customization screen, and docs/brand-lockups.md for which lockup belongs on which surface.
They are separate mechanisms with separate controls, and conflating them is the usual source of confusion.
| Layer | What it changes | Who changes it | Takes effect |
|---|---|---|---|
| Shipped catalogs | The baseline text of every interface string, per language | Contributors, in the repo | Next build |
| Label overrides | Any individual string, for one language | An admin, in the app | Immediately |
| Terminology | What an entity is called across the whole app | An admin, or a preset | Immediately |
Shipped catalogs are the build-time baseline: one JSON file per language in the UI's public assets, loaded at runtime by the translation loader. A lint gate in the front-end build enforces them — it fails when a key referenced from code or a template is missing from the English catalog, and it fails in both directions between English and Spanish, so a key present in one and absent from the other blocks the build rather than surfacing later as a raw foo.bar token on someone's screen. Adding a language to Forge means contributing a catalog and passing that gate.
Label overrides sit above the catalog. Each is one row per key per language, fetched after sign-in and deep-merged over the loaded catalog, so only the leaves an install actually overrode are replaced. Switching language reloads that language's catalog, so the overrides are re-layered on every switch. There is no rebuild and no redeploy — the admin editor re-fetches after a save, and every other session picks the change up on its next load. Reverting is a soft delete of the override row, which drops the shipped text straight back in. The editor lists every key in the shipped catalog with its override alongside, searchable, one language at a time; an override whose key has since disappeared from the catalog stays visible specifically so it can be reverted rather than stranded.
The editing surface is capability-gated and admin-only. When the capability is off — or the call simply fails — the app keeps the shipped labels and says nothing. Nothing breaks; the install just speaks stock Forge. See Capability Gating.
Terminology is the third and most visible layer, and it is not translation. It resolves through its own three-tier lookup: a per-install terminology row wins, else the active language's translation of that key, else a humanized form of the key. That is what renames the central object from Job to Work Order to Project across every screen at once, and it is what the vertical presets install as a bundle. Terminology rows record whether an admin hand-edited them and which preset last seeded them, so re-applying or switching a preset can leave hand edits alone instead of overwriting the vocabulary an admin already settled. Business Shapes covers what the presets change; Capability Gating covers how a preset is applied.
An admin editing a label can ask for the edit to fan out. Every other configured language then receives a machine-translated row for that key, flagged as machine-translated and individually editable afterwards.
The rule that makes this safe to leave on: a human override in another language is never clobbered. The fan-out inspects each sibling row and skips any that is not itself machine-translated. Someone who took the trouble to write the Spanish keeps it, no matter how many times the English is revised.
If the translation service is unreachable, the edit the admin actually made still saves. The siblings are written as pending — holding the source-language text as a placeholder and excluded from the active merge map, so no user ever sees a half-translated placeholder presented as a translation. A recurring job and an explicit admin retry action both work the backlog; a pending row whose source override was reverted in the meantime is cleaned up rather than translated into nothing.
Translation runs through the same self-hosted AI module the rest of Forge uses, so label text never leaves your stack. Availability is probed before each request, which is why a stopped AI container costs one fast check and a pending row instead of a failed save. On an install running the mock integration posture the "translations" are fabricated like every other mock response — check your readiness report before believing them. See Configuration and Integrations.
An install registers the languages it supports as rows carrying a code, an English and a native name, a default flag, an active flag and a completion percentage. The active rows are what a fan-out targets; with none registered, the shipped languages stand in.
Language itself is a per-user choice, remembered in that browser and applied to the document's language attribute — not a server-wide switch. It is offered in the header user menu on the desktop shell and as a toggle on the phone and native surfaces, including the enrolment screens, so a worker can pick a language before they have an account context at all. Two people on one install work in different languages at the same time, which is the point on a bilingual floor. See App Surfaces.
Reading the language list and the translations is exempt from capability gating — the app cannot render a screen without them, so the gate would lock everyone out of a running install. The editing, import and export surface is gated normally.
Two honest edges. First, Forge ships English and Spanish catalogs today, and both the in-app switcher and the label editor offer the languages there are catalogs for; registering a further language widens the fan-out and the override machinery but does not by itself hand a user a third language to choose — that still takes a contributed catalog. Second, training content is translated separately: a training module keeps per-locale rows for its title, summary and body, with English as the canonical base and the fallback when a locale has none.
Two dials that do not touch each other.
Per user, per browser. Light or dark, from the header toggle or Account → Customization, alongside a font-scale setting for people who want larger type. The choice is stored in that browser and broadcast to that user's other open tabs so they do not disagree. Dark is the default on a fresh profile — worth knowing before an evaluator concludes Forge has no light mode, and worth mentioning to a floor that expects a light business app.
Per install, by an admin. An application name, which also becomes the browser tab title; a primary and an accent color, injected as CSS custom properties over the base palette so every component picks them up; an uploaded logo for the app chrome; and the branding lockups — a wordmark for app chrome, a marquee for the sign-in screen, and a favicon. These are settings rows and uploaded files, not build inputs, so they survive image upgrades like the rest of your admin settings (Configuration and Integrations explains why that matters).
Two details that come up in practice. The brand reads are anonymous and bootstrap-exempt, because the sign-in screen has to be branded before anyone has signed in; the colors are also cached in the browser so they apply before the API answers, instead of flashing stock Forge and then repainting. And when nothing is uploaded, the bundled Forge defaults are used and follow the active light/dark theme automatically — an uploaded lockup is served as-is with no light and dark variant, so supply artwork that reads on both, or standardize your install on one theme.
The kiosk sits outside all of this on purpose. A shop-floor terminal keeps its own theme and font size, stored on that terminal, and defaults to light where the office app defaults to dark — a bright floor and a dim office have opposite needs, and neither should win by being someone's browser preference. While the kiosk is on screen it pushes its own theme onto the document so dialogs and toasts match, and restores the office theme on exit. See Shop Floor Kiosk.
Apache 2.0 licenses the code. It does not license the marks. Section 6 of the license is explicit that it grants no permission to use the licensor's trade names, trademarks, service marks or product names, and Forge, Armory Works, the shield mark and the lockups shipped in the repo are exactly that.
The practical reading is simple. Setting your own name, colors and lockups on your own install is what the branding controls exist for, and nothing about that needs permission. Distributing a modified Forge under the Forge name, or using the shield to identify your own product or service, is outside what the license gives you. If you are packaging a derivative for other people, change the name and the artwork — the code is yours to fork, the identity is not. Limits and Non-Goals covers the other boundaries of what Forge is willing to be.
Forge · Apache 2.0 · built by Armory Works — this wiki maps the docs; the authoritative detail lives in docs/.
Evaluating
Running it
- Installation
- First Week
- Configuration and Integrations
- Hardening a Production Install
- Backup and Restore
- Upgrades and Rollback
- Accounting Modes
Using it
- App Surfaces
- Shop Floor Kiosk
- Mobile and Offline
- Access and Roles
- Customizing an Install
- Feature Reference
- UI Flows
Building on it
- API Access
- Architecture
- Capability Gating
- Workflow, Gates and Approvals
- Data Ownership and Export
- Glossary
Contributing
Repo wikis