Member languages
Your members, signed in or about to sign in, can use your app in a language Arc CMS does not ship, such as German. You declare it and write the translations; Arc CMS shows it on the member screens. The admin keeps Arc CMS's own languages.
Add a language
Declare it in
src/custom/languages.ts:import type { MemberLanguage } from '../app/core/i18n/member-languages'; export const MEMBER_LANGUAGES: readonly MemberLanguage[] = [ { code: 'de', label: 'Deutsch', locale: 'de-CH', localeData: () => import('@angular/common/locales/de-CH') }, ];codenames the translation file,labelis the language's name in itself,localeis how dates and numbers are written, andlocaleDataloads Angular's data for that locale (leave it out for an English locale). A mistake here stopsnpm run devandnpm run buildwith a message saying what to change.Translate it in
src/custom/i18n/de.json, with the same keys as English. To see what is left to translate:npm run i18n:member -- --lang=deIt lists every member-facing Arc CMS key and every key of your own
src/custom/i18n/en.jsonthatde.jsonlacks. A key you leave out shows in English, never as the key itself.Run the tests. The parity test fails naming each missing key. While a language is still being translated, add
partial: trueto its entry: the missing keys are then listed as a warning instead.
Members get English plus the languages you declare. Arc CMS's Hindi is offered to members only if you declare it too ({ code: 'hi', label: 'हिन्दी', locale: 'hi', localeData: () => import('@angular/common/locales/hi') }); its member keys are already translated.
Example: German for a front desk
An app that declared de above, with one screen of its own whose English is in src/custom/i18n/en.json:
{ "custom": { "desk": { "checkIn": "Check in a visitor" } } }
Its src/custom/i18n/de.json translates Arc CMS's member keys and its own, with the same nesting:
{
"member": { "auth": { "continue": "Weiter" } },
"user": { "nav": { "dashboard": "Übersicht" } },
"custom": { "desk": { "checkIn": "Besucher anmelden" } }
}
With only these three keys, npm run i18n:member -- --lang=de lists the rest, and the parity test fails naming them until partial: true is set. A German-speaking member then sees "Weiter" on the sign-in button, "Übersicht" in the member menu and "Besucher anmelden" on your screen, and English for every key not yet written. The sign-in page has a language picker as soon as you declare a language (below). Elsewhere, put Arc CMS's picker on a page of yours, or call setMemberLanguage from a picker of your own:
import { setMemberLanguage } from '../../app/core/i18n/member-language.service';
// In your component, for a button such as <button (click)="german()">Deutsch</button>
german(): void {
setMemberLanguage('de');
}
Which language a member sees
- Their choice on this device. The sign-in page shows a picker at its top right, listing English and your languages, each in its own name. Put the same picker on your own pages with
<arc-member-language-picker>(src/shared/components/member-language-picker/member-language-picker.component.ts), or set the language from your own picker or settings:setMemberLanguage('de'), fromsrc/app/core/i18n/member-language.service.ts, oruse()onMemberLanguageService. It is kept on the device, so a shared device shows it on the sign-in screen too. - On a first visit, the browser's language, when you declared it (
de-ATfindsde); otherwise English. - In the admin area (
/admin), the admin's own language, always. Pages outside it use the member language.
The language picker in the page header is the admin's: once you declare member languages it shows only in the admin area, since the admin's choice does not change member pages. Members choose on the sign-in page, or through the member picker on your own pages.
Build your own picker
<arc-member-language-picker> carries its own button styles and icon (inline SVG), so it works inside a Shadow DOM host; its menu opens in the page's overlay, outside the host, with the app's Material styles. To draw your own menu in your app's look, use MemberLanguageService (src/app/core/i18n/member-language.service.ts). These members are supported and stable:
| Member | What it is |
|---|---|
languages | English and your declared languages, in order: each has code ('de'), label (its own name, "Deutsch") and locale ('de-CH'). Show a picker only when there is more than one. |
activeLang() | A signal: the code member screens show now. |
activeLocale() | A signal: its locale, for dates and numbers. |
use(code, { reload }) | Switch member screens to that language at once and keep it on this device. A code you did not declare is ignored. reload: true reloads the page after. |
Outside Angular code, setMemberLanguage(code) from the same file does what use() does.
The sign-in page
With member languages declared, the sign-in page shows the member picker, and both halves of the page follow it: the form through your translation files, and the brand panel beside it through the site's strings file for that language. For German, give your panel's data-arc-t keys in src/custom/site/strings/de.json; Arc CMS's own panel uses sign_in_title and sign_in_body:
{
"sign_in_title": "Willkommen",
"sign_in_body": "Melden Sie sich an oder erstellen Sie Ihr Konto, um fortzufahren."
}
Without that file the panel keeps the text as written. Hindi needs no file: Arc CMS ships one. With no member languages declared, there is no picker and the panel follows the page's language, as before.
With no languages declared, nothing changes: member screens follow the admin language as they always did.
Dates and numbers
The text switches at once. Angular's date and number pipes take the locale when the page loads, so they follow a new choice on the next load; setMemberLanguage('de', { reload: true }) reloads at once. For a value that must follow the switch without a reload, pass the locale yourself: {{ when | date: 'mediumDate' : undefined : member.activeLocale() }}. A person who opens the admin first and then a member page in the same visit keeps the admin's formats until the next load.
Which screens follow the language
Sign-in and sign-up (with email, phone and PIN, and Google), forgot password and PIN, the code steps, the profile with its sign-in methods, the account and membership page, the member area with its menu, dashboard, payments and premium pages, the install and update prompts, the feedback panel, the reload message, the page-not-found and no-access pages, and the cookie banner's own words (its "Learn more" link, and the buttons when the admin leaves their text empty). The sign-in errors Arc CMS shows itself, including Firebase's, are translated too, and so are the messages the sign-in functions send back, such as a wrong code, too many tries or a limit with the time to try again (member.auth.server_error.*, see Messages in the member's language).
Which keys are member-facing
The list is src/app/core/i18n/member-keys.ts: everything under member.*, user.* (the member area), common.pwa.* (install and update prompts), common.feedback.* (the feedback panel), common.version.* (the reload message), and a few shared keys such as common.actions.close. A test fails when a member screen uses a key the list does not cover.
Check every screen by eye
npm run i18n:pseudo -- --out=src/custom/i18n/zz.json
This writes a pseudo translation of the member keys, every word accented and in brackets. Declare { code: 'zz', label: 'Pseudo', locale: 'en-GB' }, call setMemberLanguage('zz'), and walk the member screens: any plain English left is text that is not translated yet. Remove the file and the entry afterwards.
Your own screens can be checked the same way in a spec: render them with pseudoTestingModule() from src/test/pseudo-language.ts and expect textNotInPseudo(fixture.nativeElement) to be empty. To include your own keys, pass your English: pseudoTestingModule(customEnglish), with customEnglish imported from src/custom/i18n/en.json.
What stays in English
- Emails and SMS. The code emails (sign-up and password reset) and SMS that Arc CMS sends stay in English. Only a site whose email engine sends nothing falls back to Firebase's own reset email, which Arc CMS asks for in the member language; Firebase uses it where it has a template for it.
- Pages of optional features: pricing and checkout, signup forms, notifications. An app that needs them in its language rewords their text in its own files once they have keys.
- The published site, content and list pages included (with what they say when there is nothing to show): it follows the page's own language, through the site's strings files, not the member language. See Languages.
- What an admin writes, such as the cookie banner's message: it is the site's own content.
- The browser tab's title on admin pages and optional features' pages. The pages members see name themselves in the tab through
member.titles.*(see Brand the admin). - The "Built with Arc CMS" line under the pages, which an admin can turn off in Settings: like the published site, it follows the site's strings files (the
powered_bykeys, see Publish in more than one language), not the member language.