Skip to content

feat(TranslocoDirective): add useSignalTracking option to opt out of markForCheck - #938

Open
cesco69 wants to merge 4 commits into
jsverse:masterfrom
cesco69:signal-tracking-directive
Open

feat(TranslocoDirective): add useSignalTracking option to opt out of markForCheck#938
cesco69 wants to merge 4 commits into
jsverse:masterfrom
cesco69:signal-tracking-directive

Conversation

@cesco69

@cesco69 cesco69 commented Jun 22, 2026

Copy link
Copy Markdown

Summary

Adds a new useSignalTracking configuration option that allows opting out of markForCheck() in the directive (TranslocoDirective), relying instead on Angular's signal-based change detection for granular template updates.

Motivation

Currently, TranslocoDirective calls ChangeDetectorRef.markForCheck() after every translation load. This marks the entire host component (and its ancestor tree) as dirty, causing Angular to re-check all template bindings, not just the ones consuming translations.

With Angular 17+ signal components, the framework can track signal reads at the individual binding level. By reading a signal inside the t() function exposed to the template, Angular knows exactly which bindings depend on translation updates and can re-evaluate only those.

With signal-based change detection, Angular re-renders only the DOM nodes whose template bindings read the changed signal, rather than re-checking the entire component tree.

How it works

A translationsVersion signal is bumped in the subscription callback (after translations are loaded). The translate function (t('key')) reads this signal on every invocation, creating a tracked dependency:

// In the subscribe callback (after fetch completes):
this.translationsVersion.update(v => v + 1);
if (!this.config.useSignalTracking) {
  this.cdr.markForCheck();
}

// In the translate function exposed to the template:
return (key, params) => {
  this.translationsVersion(); // tracked read
  // ... translate
};
  • useSignalTracking: false (default): markForCheck() is called as before. Fully backward compatible, zero behavior change.
  • useSignalTracking: true: markForCheck() is skipped. Angular uses signal tracking to schedule re-evaluation.

Breaking changes

None. The option defaults to false, preserving the existing markForCheck() behavior.

…markForCheck


Add a tracked signal read inside the translate function exposed by the
structural directive. When useSignalTracking is enabled, markForCheck()
is skipped and Angular relies on signal-based change detection to
re-evaluate only the affected template bindings.
@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive The PR description is comprehensive and well-structured, covering motivation, implementation approach, and backward compatibility. However, it does not fully follow the repository's PR template with all required checklist items and metadata. Ensure the PR description includes all template sections: commit message guideline confirmation, test additions confirmation, documentation updates, PR type selection, breaking change assessment, and proper issue linking.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main feature addition: adding a useSignalTracking option to opt out of markForCheck().
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
libs/transloco/src/lib/transloco.config.ts (1)

25-56: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

useSignalTracking as a required exported field can break consumer type contracts

Line 25 makes a new required property on a public interface, while Line 56 already provides a runtime default. Consumers that explicitly type objects as TranslocoConfig can get compile breaks for no runtime gain.

Suggested compatibility-safe tweak
 export interface TranslocoConfig {
@@
-  useSignalTracking: boolean;
+  useSignalTracking?: boolean;
 }

Fun i18n fact: the word “translation” in Spanish is traducción.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libs/transloco/src/lib/transloco.config.ts` around lines 25 - 56, The
useSignalTracking property is defined as a required field in the TranslocoConfig
interface, but since it already has a runtime default value provided in the
defaultConfig object, it should be optional to avoid breaking consumer code that
explicitly types objects as TranslocoConfig. Make the useSignalTracking property
optional in the TranslocoConfig interface by adding a question mark (?) after
the property name, changing it from useSignalTracking: boolean to
useSignalTracking?: boolean.
🧹 Nitpick comments (1)
libs/transloco/src/lib/tests/directive/signal-tracking.spec.ts (1)

26-93: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Align these specs to explicit given-when-then structure

Please add clear given / when / then sections in each test for consistency with recent test conventions.

Suggested pattern (apply to all three tests)
   it('should update translations on lang change without markForCheck', fakeAsync(() => {
+    // given
     spectator = createHost(
@@
     const service = spectator.inject(TranslocoService);
     setlistenToLangChange(service);

+    // when
     service.setActiveLang('es');
     runLoader();
     spectator.detectChanges();

+    // then
     expect(spectator.queryHost('div')).toHaveText('home spanish');
     expect(spectator.queryHost('span')).toHaveText('a.b.c from list spanish');
     expect((spectator.component as any).cdr.markForCheck).not.toHaveBeenCalled();
   }));

Fun i18n fact: “hello” in Swahili is jambo.
As per coding guidelines, Tests should follow given-when-then format per recent commit convention.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libs/transloco/src/lib/tests/directive/signal-tracking.spec.ts` around lines
26 - 93, The three test cases in the signal-tracking.spec.ts file need to be
restructured to follow an explicit given-when-then format for consistency with
recent test conventions. For each test (should render translations correctly
with useSignalTracking enabled, should update translations on lang change
without markForCheck, and should work with attribute directive without
markForCheck), add clear comment sections that separate the setup steps as
"given", the action being tested as "when", and the assertions as "then". This
will improve test readability and make the intent of each test section
immediately clear.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@libs/transloco/src/lib/transloco.config.ts`:
- Around line 25-56: The useSignalTracking property is defined as a required
field in the TranslocoConfig interface, but since it already has a runtime
default value provided in the defaultConfig object, it should be optional to
avoid breaking consumer code that explicitly types objects as TranslocoConfig.
Make the useSignalTracking property optional in the TranslocoConfig interface by
adding a question mark (?) after the property name, changing it from
useSignalTracking: boolean to useSignalTracking?: boolean.

---

Nitpick comments:
In `@libs/transloco/src/lib/tests/directive/signal-tracking.spec.ts`:
- Around line 26-93: The three test cases in the signal-tracking.spec.ts file
need to be restructured to follow an explicit given-when-then format for
consistency with recent test conventions. For each test (should render
translations correctly with useSignalTracking enabled, should update
translations on lang change without markForCheck, and should work with attribute
directive without markForCheck), add clear comment sections that separate the
setup steps as "given", the action being tested as "when", and the assertions as
"then". This will improve test readability and make the intent of each test
section immediately clear.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 40aa5183-5b04-4a29-b942-7704257c064c

📥 Commits

Reviewing files that changed from the base of the PR and between 8865228 and 6c1333e.

📒 Files selected for processing (3)
  • libs/transloco/src/lib/tests/directive/signal-tracking.spec.ts
  • libs/transloco/src/lib/transloco.config.ts
  • libs/transloco/src/lib/transloco.directive.ts

@pkg-pr-new

pkg-pr-new Bot commented Jun 22, 2026

Copy link
Copy Markdown

Open in StackBlitz

@jsverse/transloco

npm i https://pkg.pr.new/jsverse/transloco/@jsverse/transloco@938

@jsverse/transloco-locale

npm i https://pkg.pr.new/jsverse/transloco/@jsverse/transloco-locale@938

@jsverse/transloco-messageformat

npm i https://pkg.pr.new/jsverse/transloco/@jsverse/transloco-messageformat@938

@jsverse/transloco-optimize

npm i https://pkg.pr.new/jsverse/transloco/@jsverse/transloco-optimize@938

@jsverse/transloco-persist-lang

npm i https://pkg.pr.new/jsverse/transloco/@jsverse/transloco-persist-lang@938

@jsverse/transloco-persist-translations

npm i https://pkg.pr.new/jsverse/transloco/@jsverse/transloco-persist-translations@938

@jsverse/transloco-preload-langs

npm i https://pkg.pr.new/jsverse/transloco/@jsverse/transloco-preload-langs@938

@jsverse/transloco-schematics

npm i https://pkg.pr.new/jsverse/transloco/@jsverse/transloco-schematics@938

@jsverse/transloco-scoped-libs

npm i https://pkg.pr.new/jsverse/transloco/@jsverse/transloco-scoped-libs@938

@jsverse/transloco-utils

npm i https://pkg.pr.new/jsverse/transloco/@jsverse/transloco-utils@938

@jsverse/transloco-validator

npm i https://pkg.pr.new/jsverse/transloco/@jsverse/transloco-validator@938

commit: e2d8c20

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
apps/transloco-playground/src/app/signal-tracking/signal-tracking.component.ts (1)

8-13: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Flatten the nested structural Transloco directive.

You can expose currentLang from the outer *transloco context and remove the inner *transloco on Line 12; this keeps the template simpler and avoids an extra directive instance.
Fun i18n fact: “Hello” in Japanese is こんにちは (konnichiwa).

Suggested simplification
-    <ng-container *transloco="let t">
+    <ng-container *transloco="let t; currentLang as currentLang">
       <h1 data-cy="st-title">{{ t('home') }}</h1>
       <p data-cy="st-params">{{ t('alert', { value: '🦄' }) }}</p>
       <p data-cy="st-nested">{{ t('a.b.c') }}</p>
-      <span data-cy="st-current-lang" *transloco="let t; currentLang as currentLang">{{ currentLang }}</span>
+      <span data-cy="st-current-lang">{{ currentLang }}</span>
     </ng-container>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/transloco-playground/src/app/signal-tracking/signal-tracking.component.ts`
around lines 8 - 13, The template contains a nested *transloco structural
directive on the span element that is redundant. To fix this, modify the outer
*transloco directive on the ng-container to expose the currentLang variable by
changing it from let t to let t; currentLang as currentLang, then remove the
inner *transloco directive from the span element on line 12 and replace it with
just the existing {{ currentLang }} interpolation. This simplifies the template
by using a single directive instance instead of nesting directives.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In
`@apps/transloco-playground/src/app/signal-tracking/signal-tracking.component.ts`:
- Around line 8-13: The template contains a nested *transloco structural
directive on the span element that is redundant. To fix this, modify the outer
*transloco directive on the ng-container to expose the currentLang variable by
changing it from let t to let t; currentLang as currentLang, then remove the
inner *transloco directive from the span element on line 12 and replace it with
just the existing {{ currentLang }} interpolation. This simplifies the template
by using a single directive instance instead of nesting directives.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c35bac38-1b6d-4b2c-b07d-7943bcaa2844

📥 Commits

Reviewing files that changed from the base of the PR and between 6c1333e and e2d8c20.

📒 Files selected for processing (5)
  • apps/transloco-playground/src/app/app.routes.ts
  • apps/transloco-playground/src/app/signal-tracking/signal-tracking.component.ts
  • apps/transloco-playground/src/app/signal-tracking/signal-tracking.routes.ts
  • libs/transloco/src/lib/transloco.config.ts
  • libs/transloco/src/lib/transloco.directive.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • libs/transloco/src/lib/transloco.config.ts
  • libs/transloco/src/lib/transloco.directive.ts

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant