v2.6.0
Highlights
-
@byline/i18n— new package landing the admin-interface translation system. Ships the framework-agnostic primitives at the root entry (TranslationBundle,mergeTranslations(...),createFormatter,resolveInterfaceLocale) and the React surface at@byline/i18n/react(I18nProvider,useTranslation,LanguageMenu), with the built-inbyline-adminnamespace plus anadminTranslations({ locales })factory shipping at@byline/i18n/admin. The package layers cleanly with the rest of the lockstep set — root is React-free and safe in server contexts (loaders, server fns, Workers), the React surface uses a single barrel to dodge the ViteoptimizeDepsContext-identity trap that has bitten this codebase before. English and French bundle in the box; new locales drop in as a single JSON file alongsideen.json/fr.jsonand are registered in theBUNDLESmap — no new package, no new build, no new publish. -
monorepo — the admin shell now renders end-to-end in English and French. Every editor-facing surface has been wired through
useTranslation('byline-admin'): the sign-in page (with a pre-auth language menu), the admin chrome (app bar, menu drawer, breadcrumbs overflow, hamburger, preview toggle, language menu), the dashboard, the Account page (profile, password, preferences), the fullAdmin Users/Admin Roles/Admin Permissionsmodules (list views, detail containers, drawer forms, delete modals, inline permissions editor), the route-error shell, the collections list / create / edit / history / restore-version / api / view-menu / preview-link surfaces, and the entire document editor (forms, fields, presentation layout, status badge, diff modal). ICU MessageFormat handles plurals, dates, and numbers — no manual concatenation in translatable strings. -
@byline/admin— per-user locale preference is now first-class. A newpreferred_localecolumn onbyline_admin_usersstores each editor's choice; the language menu writes both abyline_admin_lngcookie (which carries the choice pre-auth, e.g. on the sign-in page) and the user row (post-auth). A reconcile flow at sign-in time resolves the two: the cookie picked on the sign-in page is persisted intopreferred_localeif the user hasn't set one yet, otherwise the stored preference wins and the cookie is updated to match. The locale-resolution cascade ispreferred → cookie → Accept-Language → defaultLocale, so even an anonymous visitor lands on a sensible default. -
@byline/admin— substantial structural cleanup: the CMS-aware editor surface —forms/*(FormRenderer, form-context, document-actions, path-widget, navigation-guard, upload-executor),fields/*(FieldRenderer + every per-type field widget + the field-side services Context),presentation/*(AdminGroup, AdminRow, AdminTabs),widgets/*(StatusBadge, DiffModal) — moved from@byline/uiinto@byline/admin. The motivation was layering:@byline/uiwas carrying two unrelated jobs (framework-agnostic primitives like Button/Modal/Drawer/Table and the document-editor UI), and the latter embeds deep CMS concepts (CollectionDefinition, CollectionAdminConfig, DocumentPatch, blocks, locales, workflow status) that don't belong in a primitives package. The reshape opens a clean path for a future@byline/host-nextadapter — the React components in@byline/adminare host-framework agnostic, the host adapter wires routing and server actions. A new@byline/admin/reactbarrel re-exports everything. breaking for external consumers — see the Breaking Changes section below. -
@byline/core— thepasswordSchemapolicy validator in@byline/core/validationnow emits stable error codes (password.tooShort,password.tooLong,password.complexity) instead of free-form English messages, exposed as thePASSWORD_ERROR_CODESconst. The matching code-to-translation map lives in the newtranslateValidationError(t, message)helper in@byline/admin/react, which client form components call onfield.state.meta.errors. Keeps@byline/coreportable to non-React / non-admin consumers (server-side request validation, future programmatic clients) while centralising the i18n surface in@byline/admin. This is the documented pattern for future shared schemas across the@byline/core/validationset. -
@byline/webapp— the customMediaListViewis now the worked example for extending the i18n system from outside@byline/admin. Newapps/webapp/byline/collections/media/i18n/shipsen.json,fr.json, and amediaAdminTranslations({ locales })factory exporting awebapp-media-adminnamespace bundle.apps/webapp/byline/i18n.tscomposes both bundles throughmergeTranslations(adminTranslations({...}), mediaAdminTranslations({...})). The component itself callsuseTranslation('webapp-media-admin')— toolbar labels, sort-option labels, the empty state, "no image" placeholder, pager aria-labels, and the upload aria all flow throught. The same pattern fits any third-party plugin, richtext extension, or custom field that wants to ship its own translations.
Migrations
-
@byline/db-postgres— the v2.6.0 admin-user table grows apreferred_locale varchar(16)column to back the per-user locale preference described above. Fresh installs (byline initor a brand-newpnpm drizzle:migrate) provision the column as part of the consolidated0000_black_sabra.sqlmigration — the previously-split0001_unknown_alice.sqlhas been collapsed into the initial migration so new projects always land on a single-step baseline.Existing installations — run the following one-liner against your database to bring the live schema up to v2.6.0:
ALTER TABLE "byline_admin_users" ADD COLUMN "preferred_locale" varchar(16);
The column is nullable; existing rows pick up
nulland fall back to the cookie /Accept-Language/ default-locale cascade until each user picks a language. No data backfill is required.
Breaking Changes
-
@byline/admin/@byline/ui— the document-editor React surface moved from@byline/ui/reactto@byline/admin/react. External consumers (anyone outside this monorepo importing these symbols directly) must update their imports.Symbols affected — all now live at
@byline/admin/react:- Forms:
FormRenderer,FormProvider,useFormContext,useFieldValue,useFieldError,useIsDirty,useIsFieldUploading,useSystemPath,useFormStore,useFormMeta,DocumentActions,PathWidget,NavigationGuardProvider,useNavigationGuardAdapter,useBeforeUnloadGuard,UseNavigationGuard,NavigationGuardResult. - Fields:
FieldRenderer,ArrayField,BlocksField,CheckboxField,DateTimeField,FileField,FileUploadField,GroupField,ImageField,ImageUploadField,NumericalField,RelationField,RelationPicker,RelationSummary,SelectField,SortableItem,TextField,TextAreaField,DraggableContextMenu,LocalDateTime,LocaleBadge,ColumnFormatter,DateTimeFormatter,renderFormatted,useFieldChangeHandler,defaultScalarForField,placeholderForField. - Field-side services:
BylineFieldServicesProvider,useBylineFieldServices,BylineFieldServices,GetCollectionDocumentsFn,UploadFieldFn,UploadedFileResult,CollectionListDoc,CollectionListParams,CollectionListResponse. - Presentational layout:
AdminGroup,AdminRow,AdminTabs,AdminTabItem. - Editor-shared widgets:
StatusBadge,DiffModal.
Migration: most call sites can
sed -i 's|@byline/ui/react|@byline/admin/react|g'the affected files, then split the import into two when the file legitimately uses both packages (the in-tree refactor split ~30 files this way during the move).@byline/adminis a workspace dep already if you depend on any admin module — otherwise add"@byline/admin": "^2.6.0"topackage.json.Generic primitives (Button, Modal, Drawer, Table, Search, Datepicker, Section, Container, Card, Alert, every icon, every loader, the
DraggableSortable/useSortable/moveItemdnd helpers) remain at@byline/ui/react— the boundary is "embeds CMS concepts in its API or types" vs "framework-agnostic React primitive". The new@byline/admin/reactbarrel is a single specifier on purpose, mirroring@byline/ui/react, to dodge the ViteoptimizeDepsper-subpath Context-identity trap. - Forms:
All other @byline/* packages bumped to 2.6.0 in lockstep with no behavioural changes this cycle.