Localization
Keep localized API reads, canonical slugs, formatting, and interface copy consistent.
A
JUse the board’s configured language for canonical board content and SDK formatting. Your application still owns route translation and copy outside the SDK’s vocabulary.
Prerequisites
- A locale decision at the server boundary.
- A single request locale shared by data loading and rendering.
- An existing i18n system for your application-specific interface copy.
The board context exposes its canonical language. Operators set it in Cavuno under Settings → General → Board language. It is the single publishing language for board-owned UI copy, date and number formatting, and canonical taxonomy and salary slugs. It is not automatically the browser language and it does not translate your application’s own interface.
Send a request language
Pass Accept-Language per request when your application negotiates a locale:
123456789101112131415161718// src/data/localized-board.tsimport type { FetchOptions } from '@cavuno/board';import { board } from '../lib/board';function localized(locale: string): FetchOptions {return { headers: { 'accept-language': locale } };}export async function loadLocalizedJobs(locale: string) {const options = localized(locale);const [context, jobs] = await Promise.all([board.context(options),board.jobs.list({ limit: 20 }, options),]);return { context, jobs };}
If the entire client always uses one locale, set the same header with globalHeaders when creating the client. Do not mutate a shared server client’s headers between users.
Use canonical localized slugs
Some salary and taxonomy methods accept a locale and return both source and canonical slugs:
12345678const context = await board.context();const title = await board.salaries.titles.retrieve(inboundSlug, {locale: context.language,});if (title.canonicalSlug !== inboundSlug) {// Permanently redirect to salaryTitlePath(title.canonicalSlug).}
Never translate an identifier or slug locally. Use display names for UI and returned canonical slugs for URLs.
Format with the same language
1234567891011121314151617import {formatDate,formatSalaryRange,locationLabel,uiCopy,} from '@cavuno/board/format';const copy = uiCopy(context.language, context.labels);const published = formatDate(context.language, job.publishedAt);const location = locationLabel(context.language, job);const salary = formatSalaryRange(context.language,job.salaryMin,job.salaryMax,job.salaryTimeframe,job.salaryCurrency,);
Keep your framework’s document language, date output, salary output, and SDK labels on the same server-selected value to avoid hydration differences.
Expected result
API reads, canonical slugs, document language, and SDK display strings agree on one board language. Non-canonical inbound slugs produce permanent redirects instead of duplicate pages.
Verify the result
- The document
langvalue matches the board language used by formatters. - A localized salary or taxonomy alias permanently redirects to the returned canonical slug.
- Server and client render identical date, currency, and location strings.
- Navigation and application chrome outside the SDK are translated by your own i18n system.
- Canonical and sitemap URLs use canonical slugs, not translated guesses.
Errors and edge cases
- Do not treat browser locale as the canonical board language without a product decision; it can produce different HTML on server and client.
- An invalid locale falls back inside some formatting helpers, but should still be treated as a configuration error in your app.
- Place and taxonomy display names can be localized while IDs remain stable.
- The fixed remote-location wrapper copy has narrower localization coverage than all board-supplied place names; review it in every supported language.
Production cautions
- Include locale in any cache key for localized reads.
- Do not cache localized authenticated responses publicly.
- Test dates around UTC day boundaries and currencies with symbol-after-number conventions.
- Crawl every supported locale for canonical and redirect loops before launch.
Continue with Framework guides to wire these framework-neutral helpers into your runtime.