Skip to content

fix: multilingual i18n issues - #172

Merged
mobeenabdullah merged 134 commits into
mainfrom
fix/multilingual-i18n-findings
Jul 19, 2026
Merged

fix: multilingual i18n issues#172
mobeenabdullah merged 134 commits into
mainfrom
fix/multilingual-i18n-findings

Conversation

@faisal-rx

@faisal-rx faisal-rx commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

Summary

Type of change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would change existing behavior)
  • Documentation update
  • Refactor / chore (no user-facing change)

Related issues

Changeset

This repo uses Changesets. If your PR changes any publishable package under packages/*, you must include a changeset:

pnpm changeset

Then commit the generated .changeset/*.md file.

  • I added a changeset (or this PR only touches non-publishable code: docs, tests, internal tooling, apps/*)
  • I selected the correct semver bump (patch / minor / major)

The changeset-check CI job will fail if a publishable package is modified without a changeset.

Test plan

  • pnpm lint
  • pnpm check-types
  • pnpm build
  • Manually verified the change

Checklist

  • I read CONTRIBUTING (if it exists)
  • My commits follow the Conventional Commits spec (enforced by commitlint)
  • I targeted the main branch
  • I updated relevant documentation

Screenshots / recordings

Notes for reviewers

Summary by CodeRabbit

  • New Features
    • Added end-to-end localization for collections, Singles, and components (including per-locale editing, fallback controls, translation-status data, and draft/published behavior), with companion locale tables.
    • Added code-first Authors, SEO, and Site Settings in the playground.
    • Added i18n:restore to recover archived translations, plus safe localization enable/disable transitions.
  • Bug Fixes
    • Fixed localized admin behavior (RTL title direction, translation completeness counts) and ensured locale-aware reads/saves/search/publish won’t mix draft translations.
    • Improved compatibility with older databases missing localization metadata.
  • Tests
    • Added/updated coverage for locale routing, SQL generation, and restore/idempotency.

aqib-rx added 30 commits July 9, 2026 12:27
Companion-table migration generator (enable/disable), nextly_i18n_archive
system table, snapshot-less .sql emission on the verbatim runner. Proven
end-to-end on real sqlite. +22 tests, 0 new failures.
…o base

localization config block (validated + normalized), per-field/collection/single/
component localized flags, locale-resolution + field-classification helpers, and
locale delivery to the admin (admin-meta + dashboard tile). +21 tests, 0 new failures.
Companion _locales runtime Drizzle generator, field->M1-spec bridge
(deriveCompanionSpec), main-table diff+runtime omit localized fields, and the
isCompanionTable pipeline-exclusion helper. Migration-owned companion (Option B).
Pure/unit-tested; no boot/HMR wiring (that's M3b). +17 tests, 0 new failures.
Persist collection/single localized flag in dynamic_collections/dynamic_singles
(mirrors status; write path fully threaded), and exclude companion _locales tables
from the migrate drift scope + drop-guard noise. Inert foundation (no consumer yet),
no race. +4 tests, 0 new failures.
…d cols from main snapshot

Thread the collection-level localized flag and defaultLocale through
migrate:create so localized collections omit their translatable columns from the
main-table snapshot and get a snapshot-less companion _locales migration.

- planCompanionMigrations derives the transition (fresh create-only vs enable
  seed+drop vs none) purely from the previous snapshot's main table.
- stripRelocatedDrops removes the main-table drop_column ops a companion enable
  migration already performs, avoiding a double-drop.
- writeCompanionMigrationFile writes the planned up/down SQL verbatim (no paired
  snapshot); companions get a strictly-later timestamp so a fresh companion's FK
  to the main table resolves.
- Mirror the localized threading into migrate:check so drift parity holds.
…-findings

# Conflicts:
#	packages/nextly/src/domains/collections/services/collection-mutation-service.ts
#	packages/nextly/src/domains/dynamic-collections/services/dynamic-collection-registry-service.ts
#	packages/nextly/src/domains/singles/services/single-mutation-service.ts
#	packages/nextly/src/init/reload-config.ts
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

- strip internal review-finding identifiers from branch-added comments
- add rationale comments to generated component localized flag and sqlite
  archive timestamp default
- SingleForm: drop the exhaustive-deps suppression; keep latest defaults in a
  ref and guard the reset by a last-applied key so refetches don't reset edits
- i18n:restore: resolve UI-built (ui-schema.json) entities so Builder-created
  collections/singles/components can be restored, not just code-config ones
- i18n:restore --purge: bound the delete to the max archive id read so a
  concurrent archive write is not purged unrestored (+ race test)
- align load-dynamic-tables SELECT-shape test with the localized column
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

- add fallbackLocale to the component populate params and thread it through
  every single/batch populate helper into overlayLocalizedComponent, so a
  no-fallback read (?fallback-locale=none) leaves untranslated embedded
  component fields blank instead of showing default-language text
- forward fallbackLocale from the collection list/detail and single read
  paths into component population
- overlay localized component fields for ?locale=all as language-keyed maps
  (mirroring the parent read) instead of skipping them, and decode
  JSON-backed values per locale
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

A localized single's main table omits translatable columns (they live in the
companion `single_<slug>_locales`), so createDefaultDocument must not insert
required/defaulted translatable values into the main row. Inserting them
targeted a column that only exists on the companion and failed the first
GET/PATCH auto-create of a localized single.
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

A Schema-Builder localization toggle has no `nextly migrate` step, so the data
move must run live. Previously the runtime reconcile only created an empty
companion (enable) or no-oped (disable), stranding content.

- add buildCompanionTransitionStatements: enable seeds the companion default
  locale from the existing main columns then drops them; disable restores the
  default onto main, archives other languages into nextly_i18n_archive, then
  drops the companion; a field change ADDs/DROPs localized columns
- reconcile the companion `_status` column when Draft/Published is toggled on
  an already-localized entity (companionHasStatus introspection)
- extract statement-array builders (up/down) so the runtime path runs them
  individually; add companionHasStatusColumn probe
- single dispatcher: pass wasLocalized, ensure the archive table exists before
  a disable, and run the transition on a flag-only toggle (not just field edits)
- skip localized fields when auto-creating a localized single's main row
- cover enable seed+drop and disable restore+archive+drop with a sqlite test
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

Apply the shared companion-transition planner to the component reconcile so a
Schema-Builder localization toggle preserves data:

- reconcileComponentCompanion routes enable → seed + drop, disable → restore +
  archive + drop, field change → ADD/DROP, using wasLocalized to detect the
  transition; ensures nextly_i18n_archive exists before a disable
- updateComponent runs the reconcile on a flag-only Internationalization toggle
  (not only when fields change), so a settings-only enable/disable actually
  moves the data instead of stranding it
- forward wasLocalized from the create and apply paths
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

…ions

Apply the shared companion-transition planner to the collection update + apply
paths so a Schema-Builder localization toggle preserves data (previously enable
created an empty companion and dropped nothing, losing default-language values,
and disable did nothing, leaving content stranded).

- generateCollectionUpdate: on an enable/disable transition, build the main
  ALTER from shared fields only (excluding columns localized in either state)
  and emit the transition SQL (seed+drop on enable, restore+archive+drop on
  disable, ADD/DROP on a field change); runs on a flag-only toggle too
- inject the app default locale into DynamicCollectionService for the seed
- collection-metadata-service: re-register the runtime schema on a flag-only
  localization toggle, not only on field edits
- collection-dispatcher apply path: forward the request's localized flag (not
  the stale persisted one) and run the data-preserving transition + archive
- ensure nextly_i18n_archive exists before a disable archives
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

…ints, preview

- companion `toColumn` now reuses the canonical descriptor snake_case, so a
  field with an uppercase acronym (URLTitle) resolves to the same column the
  DDL creates instead of misrouting reads/writes
- add the `localized` column to the raw dynamic_components DDL (all dialects)
  so a fresh DB via the SystemTableService fallback accepts component creation
- separate the collection companion migration statements with
  `--> statement-breakpoint` so the file runs as single statements (drivers
  with multi-statements disabled, e.g. MySQL, reject a multi-statement chunk)
- forward `localized` into the single and component schema previews so the
  diff no longer reports phantom main-table changes for translatable fields
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

Forward the active `locale` and `onLocaleChange` to EntrySystemHeader from
SingleForm, so a localized single has a primary header language switcher —
previously editors could not switch locales when the rail was collapsed or on
narrow layouts, even though the page fetches and saves by locale.
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

… DDL

A disable companion migration re-adds the formerly-localized columns to the
main table, so it must run BEFORE any main-migration DDL that references a
restored column (e.g. an index on a now-shared field). Emit disable companion
files with a timestamp before the main file, and enable/create-only ones after
(their FK references the main table).
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

…-findings

# Conflicts:
#	packages/nextly/src/domains/collections/services/collection-mutation-service.ts
#	packages/nextly/src/domains/components/services/component-query-service.ts
#	packages/nextly/src/domains/singles/services/single-query-service.ts
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

scope: admin @nextlyhq/admin scope: core nextly type: docs Documentation only

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants