Language translations
Shipped localesβ
Files live under `lang/*.json`. On install (and on Admin β Languages β Sync), Hirevia creates matching Languages rows and loads those catalogs into the database.
| Code | Language | File |
|---|---|---|
`en` | English (default) | `lang/en.json` |
`de` | German | `lang/de.json` |
`es` | Spanish | `lang/es.json` |
`fr` | French | `lang/fr.json` |
`hi` | Hindi | `lang/hi.json` |
`it` | Italian | `lang/it.json` |
`nl` | Dutch | `lang/nl.json` |
`pt-pt` | Portuguese (Portugal) | `lang/pt-PT.json` |
`ru` | Russian | `lang/ru.json` |
`tr` | Turkish | `lang/tr.json` |
English is the source of truth for new keys. Non-English shipped files may lag a few keys after a release β Sync fills missing keys from English without overwriting custom edits. Untouched English placeholders in a non-English locale are upgraded from the shipped file when Sync runs.
How it worksβ
lang/en.json (shipped English catalog)
β sync / seed
languages.messages in the database (per locale overrides)
β
LanguageService β Inertia props (i18n)
β
Vue useI18n().t('key', 'fallback') / PHP LanguageService::text()
Runtime messages merge as: English file β shipped locale file β database. The active locale comes from the `locale` cookie (or the default language).
Marketing page sections and navigation labels also register `marketing.*` keys into the same catalog when you save the customizer.
| Layer | Role |
|---|---|
`lang/*.json` | Shipped catalogs for new installs and Sync |
| Database | Buyer edits under Admin β Languages (wins over the catalogs) |
| Marketing customizer | Locale picker writes the active locale; keys are added to every language |
Cookie `locale` | Visitorβs chosen language |
| Default language | Used when there is no cookie; customizer opens on this locale |
Editing a string in Languages updates the live site. Syncing never overwrites values you already customized β it adds missing keys and can upgrade untouched English placeholders from a shipped locale file. Marketing keys are registered from the page/nav customizer the same way (add-missing only on other locales).
Admin β Languagesβ
URL: `/admin/settings/languages` (permission: manage languages).
| Action | What it does |
|---|---|
| Add language | Code (`en`, `fr`, `pt-br`, β¦), English name, native name, enabled, optional default. If a shipped `lang/{code}.json` exists, that catalog is used; otherwise optional DeepL fill from English |
| Translate on create | DeepL bulk fill from English when no shipped file exists (needs DeepL key) |
| Edit | Tabs by key group (`admin`, `auth`, `nav`, `marketing`, `interview`, `finance`, β¦) |
| Sync language | Ensure shipped locale rows exist; pull new keys / shipped upgrades into every locale |
| DeepL settings | Driver `none` or `deepl` + API key |
| Delete | Allowed except for English and the current default |
English stays enabled. Setting a language as Default forces it enabled and clears other defaults. Only enabled languages appear in the visitor switcher.
After adding new lang/*.json files in an update, open Admin β Languages (or click Sync) so the new locales are created and catalogs load. Then flip the switcher β UI chrome should change immediately.
Marketing customizerβ
Admin β Marketing β Pages (and Navigation):
- Opens on the site default language.
- Change Content language to edit another localeβs copy.
- Default language: Save draft keeps a private draft (public site unchanged). Publish writes CMS + language keys.
- Other languages: Save/publish writes that localeβs Languages entries (and registers missing keys on every language) without changing the CMS source body.
- Layout / structure changes (add section, reorder, nav structure) apply when saving/publishing in the default language.
- The same keys appear under Languages β marketing for bulk edit / DeepL.
Blog articles stay free-form CMS HTML and are not keyed into Languages.
Visitorsβ
- Language switcher on marketing, auth, and app chrome.
- Choosing a language posts to `/locale` and sets a long-lived cookie.
- Only enabled languages appear in the switcher.
- Marketing page bodies and nav labels resolve from the active locale with CMS English as fallback.
Practice content languages (separate from UI)β
Three different βlanguagesβ stay distinct:
| Kind | Where | Drives |
|---|---|---|
| UI chrome | Cookie + Languages catalog | Buttons, nav, settings |
| Interview session | Lobby picker on text / voice / video | Interviewer prompts, evaluation, AI report narrative |
| Practice packs | One-way / question bank `translations` JSON | Pack/bank prompts shown for that run |
Live interviews: the lobby language defaults to the UI locale but can be changed. That choice is stored on the session and frozen onto the report. Changing the site language later does not rewrite an old report.
Answer bank: STAR split detects the story language and returns STAR fields in that language (no separate translation storage).
Practice pack/bank bodies live on the entity (`translations` / `published_locales`), not in the Languages catalog. Target locales match the same interview language catalog as the lobby (not only UI Languages).
Admin: translate β review β publishβ
Requires DeepL under Admin β Languages. Without a key, admin shows a CTA to open Languages.
-
Generate or write English β AI and manual edits always land in the English source columns.
-
Translate β From Admin β Question practice or Admin β One-way:
- Per row: Translate (pick one or more targets).
- Toolbar: Translate missing or Translate all (optionally limited to selected rows).
-
Background job β Translation is queued (`ShouldQueue`). The admin gets a success toast immediately and does not wait for DeepL. A queue worker must be running Cron Job.
-
Review β Switch Content language to a draft locale (English plus locales that already have drafts). Edit that localeβs copy; English remains the DeepL source.
-
Publish / unpublish one language β With a non-English content language selected, the action is Publish
{Language}or Unpublish{Language}for that locale only. English is always the source and is not βpublishedβ separately. Users only see published locales.
Question practice: publish from the bank detail page (content language toggle). One-way: publish from the packs list using the same content language toggle.
Candidates: pick language, then lockβ
- A Language column appears when the bank/pack has more than English published.
- Changing the picker updates the displayed name/role for that locale immediately.
- Default preference follows the UI locale when that locale is published; otherwise English.
- Starting a run stores the chosen language on the run. While the run is in progress, language cannot be changed (shown as text). Retake can pick again.
- Evaluation and the report narrative use the run language.
See also Question practice overview and One-way overview
What is translatedβ
| In Languages | Not in Languages |
|---|---|
| App shell, admin, auth, onboarding, nav chrome | Blog article HTML bodies |
| Billing UI labels, plugin chrome | User answers, transcripts, frozen report narratives |
Marketing page section copy + nav labels (`marketing.*`) | One-way / QP pack bodies (entity `translations`) |
| Raw API / license-server errors |
Plugins do not ship separate language files. Their UI keys live in the same `lang/*.json` / Languages catalog.
DeepL (optional)β
- Admin β Languages β DeepL β set driver to DeepL and paste your API key.
- When adding a UI language with no shipped file, enable translate to prefill from English.
- The same key powers practice content translate actions (Question practice / One-way).
- Placeholders like
`{persona}`are preserved during UI translation.
DeepL is BYOK. Hirevia does not include DeepL credits. Review auto-translated strings (UI and practice drafts) before publishing / going live. Practice translate jobs need a running queue worker.
Tips after an updateβ
- Open Admin β Languages and run Sync so new UI keys and any new shipped locales appear.
- Review or translate new strings for each enabled locale (shipped files cover the defaults).
- Keep English as the fallback β missing keys fall back to the catalog / CMS copy.
Install already seeds Languages and registers marketing keys. Buyers on shared hosting do not need CLI commands for that β Sync in the admin UI is enough after updates.