Branding and languages
A pforte installation can carry its organisation's name, logo, colours, fonts and footer links, and
speaks four languages out of the box. Both are set with one folder, BRANDING_DIR:
branding/
brand.json name, logo, colours, corner radius, fonts, footer links
logo.svg the logo
fonts/ font files, if you use your own fonts
locales/ texts: replace some, or add a language (see "Languages and texts" below)
In a container the folder is a mounted volume. Without it pforte looks neutral and is in English and German. This is deliberately small: the pages keep their layout, one card per step. Branding changes how they look, not what they do.
Branding: brand.json
Every entry is optional. This one shows them all:
{
"name": "Example Inc.",
"logo": "logo.svg",
"radius": "16px",
"colors": {
"background": "#f6f3ec",
"surface": "#ffffff",
"text": "#1d2b4a",
"muted": "#4d5870",
"border": "#e0dacb",
"primary": "#f2c94c",
"primaryText": "#1d2b4a",
"link": "#2a4a8b",
"danger": "#82071e",
"dangerBackground": "#ffebe9"
},
"fonts": {
"body": {
"family": "Example Sans",
"files": [
{ "file": "fonts/ExampleSans-Regular.woff2", "weight": 400 },
{ "file": "fonts/ExampleSans-Bold.woff2", "weight": 700 }
]
},
"heading": {
"family": "Example Display",
"files": [{ "file": "fonts/ExampleDisplay-SemiBold.woff2", "weight": 600 }]
}
},
"links": [
{ "label": { "en": "Imprint", "de": "Impressum" }, "url": "https://example.com/imprint" },
{ "label": "Privacy", "url": "https://example.com/privacy" }
]
}
| Entry | Meaning |
|---|---|
name |
Shown above the pages as text, or as the description of the logo. Also named in e-mails and SMS (instead of ORGANISATION_NAME). |
logo |
.svg, .png, .jpg or .webp, up to 512 KB. Shown at most 12 rem wide and 4 rem high. |
radius |
Corner radius of the card, like 16px or 1rem, up to 32px. Fields and buttons are two thirds of it. |
colors |
Hex colours. primary and primaryText are the main button, link the links and secondary buttons, muted hints and footer links. What you leave out stays neutral. |
fonts.body |
Text, fields and hints. |
fonts.heading |
Headings, labels and buttons. Without it they use the body font. |
fonts.*.files |
One to six .woff2 or .woff files, up to 600 KB each, with weight (100 to 900, default 400) and style (normal or italic). |
links |
Up to six links under the card, e.g. imprint and privacy policy. label is one text, or a text per language with en as the fallback. url must be https:// or mailto:. |
Paths are relative to the branding folder and must stay inside it.
Fonts and the logo are served by pforte itself
pforte does not load anything from other sites: fonts and logo come from the same address as the login page. That
keeps visitors' data with you (a font loaded from a font service sends their address to that service) and lets
pforte forbid everything else with its Content-Security-Policy. So put the font files in the folder instead of
linking to a font service. Most families can be downloaded as .woff2 from their source; check their licence
allows self-hosting, as the common open ones (SIL Open Font License) do.
The files get a path with a hash of their content (/brand/ab12cd34ef/logo.svg), so a changed file has a new
address and browsers keep the old one for a year.
Mistakes stop the start
pforte reads the folder when it starts and refuses to start, naming what to change, if for example:
- an entry is misspelled or unknown (
"colour"), a colour is not a hex colour, or a file is missing or too big, - a file is outside the folder, or does not look like its extension (a text file called
logo.png), - a link is not
https://ormailto:.
It also warns at startup when colours are hard to read: text, the main button, links, hints or error messages with less than the 4.5:1 contrast WCAG asks for. Fix those; people with weaker eyesight or a phone in sunlight cannot use the page otherwise.
What the code field looks like
The code is entered in one large field with wide letter spacing, not in six separate boxes. That keeps the pages free of JavaScript and lets phones fill in the code from an SMS, which separate boxes often prevent.
Languages and texts
pforte's pages, e-mails and SMS come in English, German, French and Hungarian. More languages are added with a file, and every text can be replaced for one installation.
The French and Hungarian texts were written without a native speaker. Have someone who speaks the language read them before you rely on them, and replace what should sound different (see below).
Which language is used
For the pages a person sees, in this order:
ui_localesof the authorization request, if the application sends it. This is the standard OpenID Connect parameter: a space-separated list, most preferred first, e.g.ui_locales=fr hu. An application that has its own language setting should pass it, so the sign-in pages match the app.- The user's own language (
languageof the user:de,fr, …), once pforte knows who is signing in, that is from the code page on. - The browser's language (
Accept-Language). - The default language (
DEFAULT_LANGUAGE,enif not set).
A region does not matter: fr-CH is French. A language pforte has no texts for is skipped.
Two things work differently on purpose:
- E-mails and SMS always use the user's own language, because the application is not involved.
- The "forgot password" pages never use the user's language, only the one in the link or the browser.
Otherwise the language of the answer would tell a stranger that an address has an account. The link in the
reset e-mail carries the user's language (
&lang=de), so the person finds the page in their language anyway.
The user's language is set through the admin API (language of a user) or the CSV import (column language).
Changing texts and adding languages
Set BRANDING_DIR to a folder (in a container: a mounted volume) with a locales folder inside:
branding/
locales/
de.json replaces some German texts
nl.json adds Dutch
A file is named after the language, de.json or pt-BR.json, and holds the texts to replace, by key:
{
"login.submit": "Los geht's",
"code.sentSms": "Wir haben Ihnen einen Code per SMS an {to} geschickt."
}
- The keys and the English wording are in
apps/server/src/i18n/en.ts. {name}marks a value that pforte fills in. A text must keep exactly the values the English one has: losing{code}from a message would send people an empty message, so pforte refuses to start. The same happens for an unknown key or a text that is not a string. The message names the file and the key.- Texts are escaped when they are shown, so they cannot contain HTML. A line break in an e-mail is
\n. - A text you leave out stays as built in. For a new language every text you leave out appears in English, and pforte writes a warning at startup saying how many are missing.
- A new language works for the pages, e-mails and SMS at once, and for every user whose
languageis that code.
Keep SMS texts short. Texts with letters outside the basic GSM alphabet (Hungarian á, í, ó, ú, ő, ű, for example) are sent as Unicode, where one message holds 70 characters instead of 160, so such an SMS is billed as several.
Settings
| Variable | Meaning |
|---|---|
DEFAULT_LANGUAGE |
Language when nothing else decides. Default en. |
BRANDING_DIR |
Folder with brand.json, its files and locales/<language>.json. |
REMEMBER_DEVICE_DEFAULT |
true ticks "Remember this device" when the code page opens. Default false: on a shared computer nobody is remembered by accident. |
ORGANISATION_NAME |
Named in e-mails and SMS, if brand.json has no name. |