Skip to content

feat(core): type the locale layer (#DS-3897) - #1899

Open
lskramarov wants to merge 6 commits into
mainfrom
fix/DS-3897
Open

feat(core): type the locale layer (#DS-3897)#1899
lskramarov wants to merge 6 commits into
mainfrom
fix/DS-3897

Conversation

@lskramarov

Copy link
Copy Markdown
Contributor

No description provided.

KbqLocaleService was untyped at its core: `locales: any`, an implicit `any` on
`current` and on the data parameter of `addLocale`, and `getParams(name): any`.
With `noImplicitAny` off and `no-explicit-any` disabled nothing pushed back, so
custom locale data registered through KBQ_LOCALE_DATA or addLocale() was never
checked, and 21 components each re-invented their own typing on top of that `any`.

- `KbqLocaleData` describes every section; the five locale files carry one
  `satisfies` each instead of scattered per-section ones.
- `getParams` resolves the section type for a literal name and still returns `any`
  for a dynamically built string, so no existing call site changes.
- Partial locale data is completed at registration from the shipped locale of the
  same id, which lets `getParams` promise a complete section and removes the class
  of crashes on a missing one.
- Signals `localeId()`, `data()`, `items()` and `params()` join `changes`. Reading
  them from a template is what makes a runtime `setLocale()` reach OnPush children.
- One shared `kbqInjectLocaleConfiguration` replaces five hand-rolled copies, the
  four `InjectionToken<unknown>` and four `configuration: any` members are typed,
  and the configuration providers accept a partial.

Fixed while typing: `addLocale` never updated `current`, five unsubscribed
`changes.subscribe`, a missing optional chain in KbqRoundDecimalPipe, and a crash
in the multiple-file-upload example when tk-TM is selected.

Adds a localization guide, which the docs did not have, and `check-public-api-any`
- a ratchet on the amount of hand-written `any` in the golden files, 553 across 53
packages at the baseline.

Nothing breaks: every renamed type keeps a deprecated alias, and every widened
signature still accepts what it accepted before.
Every component that renders localized strings now reads
`kbqInjectLocaleConfiguration(section, token)`, which returns a signal. The read
registers on the view that performs it, so a runtime `setLocale()` reaches OnPush
children that a `markForCheck()` in the parent could never mark. Twelve components
lose their `localeService.changes` subscription, `updateLocaleParams`,
`initDefaultParams` and `externalConfiguration` along the way.

The two roles the tokens used to share are now separate. `KBQ_<X>_CONFIGURATION`
carries the defaults and applies only when the application provides no
`KBQ_LOCALE_SERVICE`, while `kbq<X>LocaleConfigurationProvider(patch)` registers an
override that is deep-merged on top of the active locale: the keys it passes stay
pinned across `setLocale()`, the rest keep following it. Overrides travel through one
`multi` token, because overriding two sections in the same `providers` array is the
normal case and a single-value token would let the second provider drop the first.

`select` is the one section three packages render, so its token lives in core rather
than in `@koobiq/components/select`, which neither tree nor tree-select depends on.

Three defects fixed on the way:

- a partial `KBQ_LOCALE_DATA` registered only the ids it patched while `items` kept
  offering the full shipped list, so activating any other locale left `data()`
  undefined and crashed `KbqDataSizePipe`;
- the configuration providers merged with a shallow spread, so overriding one key of
  a nested section blanked every sibling key in it;
- an explicit `[hiddenItemsText]` binding was wiped by the next `setLocale()`.

BREAKING CHANGE: `{ provide: KBQ_<X>_CONFIGURATION, useValue }` no longer outranks the
locale service. Replace it with `kbq<X>LocaleConfigurationProvider(...)`; the
`ng update` migration `locale-configuration-providers` rewrites it. The same change
removes `externalConfiguration` from those components and makes `configuration`
read-only.
@lskramarov lskramarov self-assigned this Aug 14, 2026
Copilot AI lite review requested due to automatic review settings August 14, 2026 15:14
@lskramarov lskramarov added the enhancement New feature or request label Aug 14, 2026
@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown

Visit the preview URL for this PR (updated for commit 841ebf1):

https://koobiq-next--prs-1899-rmoelgf5.web.app

(expires Thu, 20 Aug 2026 11:03:02 GMT)

🔥 via Firebase Hosting GitHub Action 🌎

Sign: c9e37e518febda70d0317d07e8ceb35ac43c534c

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR types and unifies the locale configuration layer across Koobiq components, introducing signal-based locale configuration injection with deep-partial overrides, and shipping tooling + schematics support to help consumers migrate safely.

Changes:

  • Introduces typed locale contracts (Kbq*LocaleConfiguration), partial locale input support, and signal-first locale APIs in KbqLocaleService.
  • Migrates multiple components to read locale configuration via kbqInjectLocaleConfiguration(...) + deep-partial override providers, ensuring runtime setLocale() updates reach OnPush children.
  • Adds an any/unknown public API “ratchet” tool and a schematic to migrate provider usages of defaults-only configuration tokens.

Reviewed changes

Copilot reviewed 87 out of 87 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
tools/public_api_guard/components/tree.api.md API snapshot update (tree)
tools/public_api_guard/components/tree-select.api.md API snapshot update (tree-select)
tools/public_api_guard/components/timezone.api.md API snapshot update (timezone)
tools/public_api_guard/components/timepicker.api.md API snapshot update (timepicker)
tools/public_api_guard/components/time-range.api.md API snapshot update (time-range)
tools/public_api_guard/components/select.api.md API snapshot update (select)
tools/public_api_guard/components/search-expandable.api.md API snapshot update (search-expandable)
tools/public_api_guard/components/notification-center.api.md API snapshot update (notification-center)
tools/public_api_guard/components/navbar.api.md API snapshot update (navbar)
tools/public_api_guard/components/input.api.md API snapshot update (input)
tools/public_api_guard/components/filter-bar.api.md API snapshot update (filter-bar)
tools/public_api_guard/components/file-upload.api.md API snapshot update (file-upload)
tools/public_api_guard/components/datepicker.api.md API snapshot update (datepicker)
tools/public_api_guard/components/code-block.api.md API snapshot update (code-block)
tools/public_api_guard/components/clamped-text.api.md API snapshot update (clamped-text)
tools/public_api_guard/components/app-switcher.api.md API snapshot update (app-switcher)
tools/public_api_guard/components/actions-panel.api.md API snapshot update (actions-panel)
tools/check-public-api-any/tsconfig.json TS config for new tool
tools/check-public-api-any/index.ts New any/unknown ratchet implementation
tools/check-public-api-any/baseline.json Baseline counts per package
packages/schematics/src/migrations/locale-configuration-providers/schema.ts New migration schema type
packages/schematics/src/migrations/locale-configuration-providers/schema.json New migration schema JSON
packages/schematics/src/migrations/locale-configuration-providers/README.md Migration documentation
packages/schematics/src/migrations/locale-configuration-providers/index.ts Migration rule implementation
packages/schematics/src/migrations/locale-configuration-providers/data.ts Token/helper mapping + warnings
packages/schematics/src/migrations.json Registers new migration
packages/schematics/src/collection.json Exposes migration via schematics collection
packages/docs-examples/components/file-upload/en-US.ts Docs locale data typing updates
packages/docs-examples/components/file-upload/es-LA.ts Docs locale data typing updates
packages/docs-examples/components/file-upload/pt-BR.ts Docs locale data typing updates
packages/docs-examples/components/file-upload/ru-RU.ts Docs locale data typing updates
packages/docs-examples/components/file-upload/tk-TM.ts Adds tk-TM docs example locale data
packages/docs-examples/components/file-upload/file-upload-multiple-custom-text-overview/file-upload-multiple-custom-text-overview-example.ts Example updated to typed config + fallback
packages/docs-examples/components/file-upload/file-upload-custom-text-via-input/file-upload-custom-text-via-input-example.ts Example updated to new locale config types
packages/components/tree/tree-selection.component.ts Tree selection reads typed select locale config
packages/components/tree-select/tree-select.component.ts Tree-select reads select locale config + accessor input
packages/components/timezone/timezone-select.component.ts Timezone select locale config token/provider + effect-based placeholder
packages/components/timepicker/timepicker.directive.ts Timepicker locale config token/provider + reactive placeholder updates
packages/components/time-range/time-range.ts Time-range locale config via shared helper + deep override provider
packages/components/time-range/time-range.spec.ts Adds/updates tests for locale override behavior
packages/components/select/select.component.ts Select reads shared select locale config; accessor input + selectAll getter
packages/components/search-expandable/search-expandable.ts Search-expandable config token typed + signal-based configuration
packages/components/notification-center/notification-center.ts Notification center typed config + signal-backed getter
packages/components/notification-center/notification-center.spec.ts Tests runtime locale update propagation to items
packages/components/navbar/vertical-navbar.component.ts Navbar typed config token/provider + signal-backed getter
packages/components/navbar/navbar-toggle.component.ts Toggle updates tooltip via effect on locale config
packages/components/navbar/navbar.component.spec.ts Tests locale changes and override behavior for toggle tooltip
packages/components/input/input-number.ts Number input reads typed locale config via helper + effect reformatting
packages/components/filter-bar/filters.spec.ts Updates tests to drive locale changes via service
packages/components/filter-bar/filter-bar.types.ts Filter-bar config token factory + deep override provider + readonly host
packages/components/filter-bar/filter-bar.ts Filter-bar configuration now signal-backed via shared helper
packages/components/filter-bar/filter-bar.spec.ts Updates precedence tests for override-vs-locale behavior
packages/components/datepicker/datepicker-input.directive.ts Datepicker config token typed + effect-driven reformatting on locale change
packages/components/core/utils/utils.ts Adds KbqDeepPartial + kbqDeepMerge
packages/components/core/locales/types.ts Adds typed locale model + normalized Kbq*LocaleConfiguration names
packages/components/core/locales/a11y.ts Moves a11y config to shared helper + deep override provider
packages/components/core/locales/a11y.spec.ts Extends tests for overrides + fallback behavior
packages/components/core/locales/configuration.ts Adds overrides token + shared kbqInjectLocaleConfiguration helper
packages/components/core/locales/select.ts Adds shared select locale config token/provider in core
packages/components/core/locales/index.ts Exports new locale utilities
packages/components/core/locales/locale-service.ts Refactors locale service to typed signals + partial locale completion
packages/components/core/locales/locale-service.spec.ts Expanded test coverage for typed locale service behavior
packages/components/core/locales/formatters.ts Types formatter locale data with satisfies
packages/components/core/locales/en-US.ts Locale data typed to KbqLocaleStringsData
packages/components/core/locales/es-LA.ts Locale data typed to KbqLocaleStringsData
packages/components/core/locales/pt-BR.ts Locale data typed to KbqLocaleStringsData
packages/components/core/locales/ru-RU.ts Locale data typed to KbqLocaleStringsData
packages/components/core/locales/tk-TM.ts Locale data typed to KbqLocaleStringsData
packages/components/core/formatters/number/formatter.ts Pipes unsubscribe via takeUntilDestroyed; guard unknown locale ids
packages/components/core/formatters/filesize/formatter.ts Adds fallback size units config when missing
packages/components/code-block/code-block.ts Code block locale config via shared helper + deep override provider
packages/components/clamped-text/constants.ts Clamped-text locale config via shared helper + renamed inject fn
packages/components/clamped-text/clamped-text.ts Uses new clamped-text inject function
packages/components/clamped-text/clamped-list.ts Uses new clamped-text inject function
packages/components/app-switcher/app-switcher.ts App-switcher typed config + shared helper + deep override provider
packages/components/actions-panel/actions-panel-container.ts Actions panel locale config via shared helper + deep override provider
packages/angular-moment-adapter/adapter/moment-date-adapter.ts Unsubscribe locale changes via takeUntilDestroyed
packages/angular-luxon-adapter/adapter/date-adapter.ts Unsubscribe locale changes via takeUntilDestroyed
package.json Adds scripts for check-public-api-any + approve-public-api-any
docs/guides/migration.ru.md Documents locale layer typing migration
docs/guides/migration.en.md Documents locale layer typing migration
docs/guides/localization.ru.md New localization guide (RU)
docs/guides/localization.en.md New localization guide (EN)
apps/docs/src/app/structure.ts Adds localization guide to docs nav
.github/workflows/api.yml Runs check-public-api-any in CI

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread packages/components/core/locales/locale-service.ts
@lskramarov
lskramarov marked this pull request as draft August 14, 2026 15:34
`setLocale` registers an id the registry does not know, completing it from the
shipped locale of that id or from the default one, but the constructor activated
`KBQ_LOCALE_ID` by reading `locales[id]` directly. `KBQ_LOCALE_ID` accepts any
string, so an application naming a locale it registers later through `addLocale`
started with `data()` undefined until the first `setLocale` call, while `data` is
typed to promise a complete locale.

Both paths now go through one private `register`, which is where that invariant
lives.
The baseline was recorded before this branch was rebased onto main, so it predates
`alwaysShowActionbar` on `kbq-code-block` and the split of the scrollbar package.
Both are `main`'s, and neither is something this branch can narrow: 553 across 53
packages becomes 555 across 54, with no package this branch touches moving.
Every `kbq<X>LocaleConfigurationProvider` contributes to one shared `multi` token,
and Angular resolves a `multi` token from the nearest injector that has any entry
for it, without merging the levels above. Scoping one section to a component — the
pattern the localization guide recommends — therefore hid every section an ancestor
had overridden from that whole subtree: a root `kbqA11yLocaleConfigurationProvider`
stopped applying inside a component that registered a `select` override of its own,
silently falling back to the active locale.

Each provider now also re-contributes the batch it inherited, resolved with
`skipSelf`, and the consumer flattens and de-dupes by reference, which keeps
ancestor-before-descendant precedence when several providers sit at the same level.

Follow-ups from the same review:

- `KbqDataSizePipe` read `locales[locale].sizeUnits` unguarded, so a locale id that
  was never registered threw instead of falling back — the guard the number pipes
  already carry;
- the `locale-configuration-providers` migration matched the first import clause of
  the module whichever kind it was, so a file carrying a separate `import type`
  clause got the helper spliced into it and stopped compiling (TS1361);
- `KbqSelect` kept a `localeService` field nothing reads, behind a comment claiming
  `KbqTimezoneSelect` resolves its section through it;
- section 16 of the migration guide both claimed and denied that `ng update`
  rewrites the configuration providers;
- `core/locales/select.ts` had no tests at all, and the navbar override test
  asserted the same pinned string before and after `setLocale()`, so it could not
  tell a reactive configuration from one frozen at construction.
`copy-meta-to-dist.js` copied each migration's `README.md` as a required file, so
the first migration without one — `scrollbar-deprecated-path` — threw and aborted
the loop. `utils/` stayed empty while every built migration requires it at runtime,
`tag-slots` and `v20-upgrade` never reached the package, and `new-icons-pack` lost
its data files. The top-level handler only logged the failure, so the build exited
0 and CI stayed green while `ng update` could not have loaded a single migration.

`README.md` is now optional with a warning, the way `data.js` already was, and a
copy failure sets a non-zero exit code.
@lskramarov
lskramarov marked this pull request as ready for review August 17, 2026 13:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants