Byline CMS
  • Accueil
  • Docs
  • À propos
Byline CMS
Voir sur GitHub
  • Getting Started
    • CLI
    • Development environment and example application
    • Upgrading from 3.21 to 4.x
  • Why Byline
    • Mission & Vision
    • Content Management in the Time of AI
  • Key Architectural Decisions
    • Core Document Storage
    • Core Composition
    • Transactions
    • Path Grammar
    • Deployment Topologies
  • Collections
    • Fields
    • Blocks
    • Relationships
    • Document Trees
    • Document Paths
    • File / Media Uploads
    • Rich Text Editor
    • Collection Versioning
  • Reading & Delivery
    • Client SDK (@byline/client)
    • Routing & API
    • Transports
    • Markdown Export
    • MCP Server
    • Caching
    • Search & Retrieval
    • Search & Document Extraction
  • Auth & Security
    • Authentication & Authorization
    • Auditability
  • Internationalization (i18n)
    • The host i18n system
    • Admin interface translations
    • Content locales
    • Administering content locales
  • Admin UI
    • UI Kit (@byline/ui)
    • Client-config registration
  • Testing
  • Accueil

Administering content locales

Companions:

  • Content locales — what a content locale is, and the resolution / fallback / advertising machinery this administrative task operates on.
  • Document Paths — the per-(document, locale) path row that a re-anchor moves onto the new default locale.
  • Internationalization — the three-axis overview.

Switching the default content locale

A system's default content locale (i18n.content.defaultLocale) does two different jobs:

  1. A config preference — which locale new content is authored in, and which locale is served for a request that doesn't specify one. Genuinely global, and genuinely should be switchable.
  2. A per-document data anchor — every document's content rows, its path row, and its completeness ledger were originally written keyed to whatever the default was at write time.

Job (2) is the trap. If the default were only a global config value, flipping en → fr on a live system would silently re-interpret every existing document against an anchor it was never written for: en-authored fields would read empty (the fallback floor moves to fr, which is empty), findByPath(slug, 'fr') would 404 (path rows live under en), and the completeness yardstick would become meaningless. Non-localized content (the 'all' sentinel) and explicit 'en' reads are unaffected — but everything anchored to the default breaks.

The fix: a per-document source_locale

Byline records a per-document source_locale on byline_documents, set once at creation to the locale the first version was authored in (defaulting to the global config default at that moment). It re-bases each anchor — the fallback floor, the path locale, the completeness yardstick — from "the global config" to "this document's own truth." With that column in place, switching i18n.content.defaultLocale is a non-event for existing data: every document rides its own source_locale, and the global default is demoted to its honest role — the authoring default for new documents plus the request-time fallback when a read specifies no locale.

source_locale is surfaced on every read payload as sourceLocale, and the editor shows it as a small neutral badge next to the document title. For in-place upgrades, the column is populated at boot — initBylineCore() stamps any unstamped rows with the configured default idempotently — so a vanilla drizzle:migrate never fails on a constraint and upgrades self-heal.

Re-anchoring documents onto the new default

Switching the config is safe immediately, but the harder part of actually moving documents onto the new default is having them fully translated into it — the system can never manufacture a primary language with holes. So the realistic workflow is: flip the config → translate documents into the new locale over time → re-run the bulk re-anchor to sweep up the now-complete ones.

The bulk re-anchor is a script (apps/webapp/byline/scripts/re-anchor.ts):

pnpm tsx byline/scripts/re-anchor.ts --to fr [--collection <path>] [--dry-run]

Per document, in one transaction, it: skips documents that are not-found, already-anchored, or incomplete in the target locale (eligibility comes from the completeness ledger — it refuses to manufacture a translation); otherwise flips source_locale, moves the path row onto the target locale (re-tagging the slug, keeping the URL stable), and writes a new immutable version recomputing its ledger against the new anchor. Each document is its own transaction, so the operation is idempotent and resumable — and --dry-run reports the would-be outcome plus the backlog. The skipped-incomplete report is your outstanding-translation list.

PrécédentContent locales
SuivantAdmin UI

Sur cette page

  • Switching the default content locale
  • The fix: a per-document source_locale
  • Re-anchoring documents onto the new default
Byline CMS

Construire l'avenir de la gestion de contenu, un commit à la fois.

Projet

  • Documentation
  • Feuille de route
  • Contribuer
  • Versions

Communauté

  • Discussions GitHub
  • Blog
  • Infolettre

Mentions légales

  • Politique de confidentialité
  • Conditions d'utilisation
  • Cookies

© 2026 Infonomic Company Limited et contributeurs. Open source et conçu avec ❤️ par la communauté.