Skip to content

Add L10N storage for localized database object content - #6703

Merged
dtdesign merged 37 commits into
6.3from
63-l10n
Oct 1, 2026
Merged

dtdesign merged 37 commits into
6.3from
63-l10n

Conversation

@BurntimeX

Copy link
Copy Markdown
Member

Summary

Introduces a dedicated L10N storage system for localized content of database objects, replacing the previous approach of storing such content as i18n phrases in wcf1_language_item. Localized values now live in a per-object <table>_l10n table (objectID, nullable languageID, payload columns), read/written through a single L10nStorage service. Captcha questions and user options are migrated to the new system as the first adopters.

The L10N system

  • L10nStorage – the only writer/reader for *_l10n tables. Exchanges values as columnName => [languageID => value], supports monolingual content (languageID IS NULL, exposed as L10nStorage::MONOLINGUAL), per-column language sets, a deterministic fallback resolver (resolveValue()), and a correlated getSubSelect() for sortable/filterable list queries.
  • L10nDefinition – describes a localized table (primary table, *_l10n table, object column, payload columns). Optionally declares support for synchronization with language variables via an identifierColumnName + per-column language-variable suffixes.
  • Form field – IL10nFormField / TL10nFormField add ->l10n() support to form builder fields, mutually exclusive with the phrase-based ->i18n() mode.
  • Collections – TCollectionL10n trait batch-loads the localized values of all objects in a DatabaseObjectCollection in a single query; OptionCollection / UserOptionCollection / CaptchaQuestionCollection and the L10n*List classes wire this into lists and grid views. L10nTextFilter filters grid columns on localized values.

Synchronization with language variables

Delivered objects (e.g. shipped user options) keep their wcf.* phrases as the source
of truth and mirror them into the L10N storage:

  • The primary table gains an l10nIdentifier column (the language-variable base name, NULL for admin-created objects), and the *_l10n table an isPristine flag.
  • Definitions that support the sync register via the L10nDefinitionCollecting event; SyncL10nLanguageItems runs at the end of every install/update and refreshes pristine rows from the phrases, preserving values an administrator changed locally.
  • isPristine is maintained automatically inside L10nStorage::setValues(): a delivered object keeps the flag per language while the value is unchanged; a changed value or a user-created object is written as modified. L10nLanguageItemSync provides reusable one-time migrate() / ongoing sync() helpers (usable by other packages).

Migrations

  • Captcha questions and user options are refactored to DatabaseObjectBuilder with dedicated create/update commands and events.
  • User options move their title/description from wcf.user.option.* phrases into wcf1_user_option_l10n; system options stay linked (pristine, kept in sync), admin-created options own their values and their obsolete phrases are removed.
  • UserOptionPackageInstallationPlugin and UserOptionImporter write through the new builder / L10N storage; the ACP forms, grid views, cache builder and templates read localized titles/descriptions from the collection.
  • Removed template scripting from the shipped user-option descriptions, which is no longer supported for L10N content.

Notes

  • New *_l10n tables are created via explicit database scripts (install + update); no automatic table provisioning.
  • PackageInstallationDispatcher invokes the sync after the bootstrap loader is rebuilt, so listeners added by the same update are already active.

See #6109
See #6290

@dtdesign
dtdesign force-pushed the 63-l10n branch 5 times, most recently from 5520e16 to 69647a6 Compare September 29, 2026 10:27
BurntimeX and others added 20 commits October 1, 2026 13:08
Packages register a `L10nDefinition` (DBO class + payload columns) via the `L10nDefinitionCollecting` event in their bootstrap file. At the end of a package installation or update, `SyncL10nTables` creates or extends the `<table>_l10n` tables (objectID, nullable languageID for monolingual content, payload columns) using `DatabaseTableChangeProcessor`, so all changes are recorded in the sql log and dropped on package uninstallation.
@dtdesign
dtdesign merged commit 4b78afe into 6.3 Oct 1, 2026
9 checks passed
@dtdesign
dtdesign deleted the 63-l10n branch October 1, 2026 12:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants