From 228ea59cbeab81e9b8d04e66880719a297c44c90 Mon Sep 17 00:00:00 2001 From: skrustev Date: Thu, 27 Aug 2026 10:04:59 +0300 Subject: [PATCH 1/4] fix(i18n): Fix setting partial resources not filling the rest per component. --- .../action-strip.component.spec.ts | 34 ++++++++++++++ .../action-strip/action-strip.component.ts | 7 ++- .../src/banner/banner.component.spec.ts | 45 ++++++++++++++++++ .../banner/src/banner/banner.component.ts | 7 ++- .../calendar/src/calendar/calendar-base.ts | 16 +++++-- .../src/carousel/carousel.component.spec.ts | 46 ++++++++++++++++++- .../src/carousel/carousel.component.ts | 7 ++- .../chips/src/chips/chip.component.ts | 7 ++- .../chips/src/chips/chip.spec.ts | 41 ++++++++++++++++- .../combo/src/combo/combo.common.ts | 7 ++- .../combo/src/combo/combo.component.spec.ts | 44 ++++++++++++++++++ .../date-range-picker.component.spec.ts | 45 +++++++++++++++++- .../date-range-picker.component.ts | 7 ++- .../grids/core/src/pivot-grid-dimensions.ts | 6 ++- .../grids/grid/src/grid-base.directive.ts | 16 +++++-- .../grids/grid/src/grid.component.spec.ts | 46 ++++++++++++++++++- .../input-group/input-group.component.spec.ts | 33 +++++++++++++ .../src/input-group/input-group.component.ts | 7 ++- .../list/src/list/list.component.spec.ts | 31 +++++++++++++ .../list/src/list/list.component.ts | 7 ++- .../src/paginator/paginator.component.spec.ts | 20 ++++++++ .../src/paginator/paginator.component.ts | 7 ++- .../query-builder-header.component.ts | 7 ++- .../query-builder-tree.component.ts | 18 ++++++-- .../query-builder.component.spec.ts | 28 ++++++++++- .../query-builder/query-builder.component.ts | 7 ++- .../src/time-picker/time-picker.component.ts | 7 ++- .../src/tree/tree-node/tree-node.component.ts | 7 ++- .../tree/src/tree/tree.spec.ts | 44 +++++++++++++++++- 29 files changed, 556 insertions(+), 48 deletions(-) diff --git a/projects/igniteui-angular/action-strip/src/action-strip/action-strip.component.spec.ts b/projects/igniteui-angular/action-strip/src/action-strip/action-strip.component.spec.ts index e7fb05f77c4..400432b1e5c 100644 --- a/projects/igniteui-angular/action-strip/src/action-strip/action-strip.component.spec.ts +++ b/projects/igniteui-angular/action-strip/src/action-strip/action-strip.component.spec.ts @@ -4,6 +4,7 @@ import { TestBed, waitForAsync } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { IgxIconComponent } from 'igniteui-angular/icon'; +import { ActionStripResourceStringsEN, changei18n } from 'igniteui-angular/core'; import { wait } from '../../../test-utils/ui-interactions.spec'; const ACTION_STRIP_CONTAINER_CSS = 'igx-action-strip__actions'; @@ -153,6 +154,39 @@ describe('igxActionStrip', () => { expect(dropDownList.nativeElement.getAttribute('aria-hidden')).toBe('true'); }); }); + + describe('Resource Strings', () => { + it('should update resource strings when global i18n changes and no custom strings are set', () => { + const fix = TestBed.createComponent(IgxActionStripMenuTestingComponent); + fix.detectChanges(); + const actionStrip = fix.componentInstance.actionStrip; + + changei18n({ igx_action_strip_button_more_title: 'More Options' }); + fix.detectChanges(); + + expect(actionStrip.resourceStrings.igx_action_strip_button_more_title).toBe('More Options'); + + // Restore defaults + changei18n(ActionStripResourceStringsEN); + }); + + it('should preserve custom resource strings when global i18n changes', () => { + const fix = TestBed.createComponent(IgxActionStripMenuTestingComponent); + fix.detectChanges(); + const actionStrip = fix.componentInstance.actionStrip; + + actionStrip.resourceStrings = { igx_action_strip_button_more_title: 'Custom More' }; + fix.detectChanges(); + + changei18n({ igx_action_strip_button_more_title: 'Global More' }); + fix.detectChanges(); + + expect(actionStrip.resourceStrings.igx_action_strip_button_more_title).toBe('Custom More'); + + // Restore defaults + changei18n(ActionStripResourceStringsEN); + }); + }); }); @Component({ diff --git a/projects/igniteui-angular/action-strip/src/action-strip/action-strip.component.ts b/projects/igniteui-angular/action-strip/src/action-strip/action-strip.component.ts index 45901dbc84e..00cf567b106 100644 --- a/projects/igniteui-angular/action-strip/src/action-strip/action-strip.component.ts +++ b/projects/igniteui-angular/action-strip/src/action-strip/action-strip.component.ts @@ -164,11 +164,12 @@ export class IgxActionStripComponent implements IgxActionStripToken, AfterViewIn */ @Input() public set resourceStrings(value: IActionStripResourceStrings) { - this._resourceStrings = Object.assign({}, this._resourceStrings, value); + this._resourceStrings = value; + this._customResourceStrings = Object.assign({}, this._defaultResourceStrings, this._resourceStrings); } public get resourceStrings(): IActionStripResourceStrings { - return this._resourceStrings || this._defaultResourceStrings; + return this._resourceStrings ? this._customResourceStrings : this._defaultResourceStrings; } /** @@ -209,12 +210,14 @@ export class IgxActionStripComponent implements IgxActionStripToken, AfterViewIn private _destroyRef = inject(DestroyRef); private _resourceStrings: IActionStripResourceStrings = null!; + private _customResourceStrings: IActionStripResourceStrings = null!; private _defaultResourceStrings = getCurrentResourceStrings(ActionStripResourceStringsEN); private _originalParent!: HTMLElement; constructor() { onResourceChangeHandle(this._destroyRef, () => { this._defaultResourceStrings = getCurrentResourceStrings(ActionStripResourceStringsEN, false); + this._customResourceStrings = this._resourceStrings ? Object.assign({}, this._defaultResourceStrings, this._resourceStrings) : null!; }, this); } diff --git a/projects/igniteui-angular/banner/src/banner/banner.component.spec.ts b/projects/igniteui-angular/banner/src/banner/banner.component.spec.ts index 0251c95f420..6792d8f0dad 100644 --- a/projects/igniteui-angular/banner/src/banner/banner.component.spec.ts +++ b/projects/igniteui-angular/banner/src/banner/banner.component.spec.ts @@ -3,6 +3,7 @@ import { TestBed, ComponentFixture, tick, fakeAsync, waitForAsync } from '@angul import { By } from '@angular/platform-browser'; import { IgxBannerComponent } from './banner.component'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { BannerResourceStringsEN, changei18n } from 'igniteui-angular/core'; import { IgxIconComponent } from 'igniteui-angular/icon'; import { IgxBannerActionsDirective } from './banner.directives'; import { IgxCardComponent, IgxCardContentDirective, IgxCardHeaderComponent } from 'igniteui-angular/card'; @@ -525,6 +526,50 @@ describe('igxBanner', () => { })); }); + describe('Resource Strings', () => { + it('should return full resource strings when partial resourceStrings are set', () => { + const fix = TestBed.createComponent(SimpleBannerEventsComponent); + fix.detectChanges(); + const banner = fix.componentInstance.banner; + + banner.resourceStrings = { igx_banner_button_dismiss: 'Close' }; + fix.detectChanges(); + + expect(banner.resourceStrings.igx_banner_button_dismiss).toBe('Close'); + }); + + it('should update resource strings when global i18n changes and no custom strings are set', () => { + const fix = TestBed.createComponent(SimpleBannerEventsComponent); + fix.detectChanges(); + const banner = fix.componentInstance.banner; + + changei18n({ igx_banner_button_dismiss: 'Dismiss Global' }); + fix.detectChanges(); + + expect(banner.resourceStrings.igx_banner_button_dismiss).toBe('Dismiss Global'); + + // Restore defaults + changei18n(BannerResourceStringsEN); + }); + + it('should preserve custom resource strings when global i18n changes', () => { + const fix = TestBed.createComponent(SimpleBannerEventsComponent); + fix.detectChanges(); + const banner = fix.componentInstance.banner; + + banner.resourceStrings = { igx_banner_button_dismiss: 'Custom Dismiss' }; + fix.detectChanges(); + + changei18n({ igx_banner_button_dismiss: 'Global Dismiss' }); + fix.detectChanges(); + + expect(banner.resourceStrings.igx_banner_button_dismiss).toBe('Custom Dismiss'); + + // Restore defaults + changei18n(BannerResourceStringsEN); + }); + }); + const getBaseClassElements = (fixture: ComponentFixture) => { bannerElement = fixture.debugElement.query(By.css('.' + CSS_CLASS_BANNER)); bannerMessageElement = fixture.debugElement.query(By.css('.' + CSS_CLASS_BANNER_MESSAGE)); diff --git a/projects/igniteui-angular/banner/src/banner/banner.component.ts b/projects/igniteui-angular/banner/src/banner/banner.component.ts index 7722fb4c6ec..a8bc2f5c628 100644 --- a/projects/igniteui-angular/banner/src/banner/banner.component.ts +++ b/projects/igniteui-angular/banner/src/banner/banner.component.ts @@ -160,11 +160,12 @@ export class IgxBannerComponent implements IToggleView { */ @Input() public set resourceStrings(value: IBannerResourceStrings) { - this._resourceStrings = Object.assign({}, this._resourceStrings, value); + this._resourceStrings = value; + this._customResourceStrings = Object.assign({}, this._defaultResourceStrings, this._resourceStrings); } public get resourceStrings(): IBannerResourceStrings { - return this._resourceStrings || this._defaultResourceStrings; + return this._resourceStrings ? this._customResourceStrings : this._defaultResourceStrings; } /** @@ -248,11 +249,13 @@ export class IgxBannerComponent implements IToggleView { private _bannerEvent!: BannerEventArgs; private _animationSettings!: ToggleAnimationSettings; private _resourceStrings: IBannerResourceStrings = null!; + private _customResourceStrings: IBannerResourceStrings = null!; private _defaultResourceStrings = getCurrentResourceStrings(BannerResourceStringsEN); constructor() { onResourceChangeHandle(this._destroyRef, () => { this._defaultResourceStrings = getCurrentResourceStrings(BannerResourceStringsEN, false); + this._customResourceStrings = this._resourceStrings ? Object.assign({}, this._defaultResourceStrings, this._resourceStrings) : null!; }, this); } diff --git a/projects/igniteui-angular/calendar/src/calendar/calendar-base.ts b/projects/igniteui-angular/calendar/src/calendar/calendar-base.ts index 16c7574dbd0..97fb6ee57a4 100644 --- a/projects/igniteui-angular/calendar/src/calendar/calendar-base.ts +++ b/projects/igniteui-angular/calendar/src/calendar/calendar-base.ts @@ -256,6 +256,7 @@ export class IgxCalendarBaseDirective implements ControlValueAccessor { */ private _selection: CalendarSelection | string = CalendarSelection.SINGLE; private _resourceStrings: ICalendarResourceStrings = null!; + private _customResourceStrings: ICalendarResourceStrings = null!; private _defaultResourceStrings = getCurrentResourceStrings(CalendarResourceStringsEN); /** @@ -283,14 +284,15 @@ export class IgxCalendarBaseDirective implements ControlValueAccessor { */ @Input() public set resourceStrings(value: ICalendarResourceStrings) { - this._resourceStrings = Object.assign({}, this._resourceStrings, value); + this._resourceStrings = value; + this._customResourceStrings = Object.assign({}, this._defaultResourceStrings, this._resourceStrings); } /** * An accessor that returns the resource strings. */ public get resourceStrings(): ICalendarResourceStrings { - return this._resourceStrings || this._defaultResourceStrings; + return this._resourceStrings ? this._customResourceStrings : this._defaultResourceStrings; } /** @@ -328,7 +330,7 @@ export class IgxCalendarBaseDirective implements ControlValueAccessor { this._locale = this.i18nFormatter.verifyLocale(value); // changing locale runtime needs to update the `weekStart` too this._localeWeekStart = this.i18nFormatter.getLocaleFirstDayOfWeek(this._locale); - this._defaultResourceStrings = getCurrentResourceStrings(CalendarResourceStringsEN, false, this._locale); + this.updateResources(this._locale); } /** @@ -1038,8 +1040,14 @@ export class IgxCalendarBaseDirective implements ControlValueAccessor { private onResourceChange(args?: CustomEvent) { this._defaultLocale = args!.detail.newLocale; if (!this._locale) { - this._defaultResourceStrings = getCurrentResourceStrings(CalendarResourceStringsEN, false); + // Avoid unnecessary fetch of resources, since they should be already retrieved when setting custom locale. + this.updateResources(); } this._localeWeekStart = this.i18nFormatter.getLocaleFirstDayOfWeek(this.locale); } + + private updateResources(locale?: string) { + this._defaultResourceStrings = getCurrentResourceStrings(CalendarResourceStringsEN, false, locale); + this._customResourceStrings = this._resourceStrings ? Object.assign({}, this._defaultResourceStrings, this._resourceStrings) : null!; + } } diff --git a/projects/igniteui-angular/carousel/src/carousel/carousel.component.spec.ts b/projects/igniteui-angular/carousel/src/carousel/carousel.component.spec.ts index 27651b6d7ec..2888666c339 100644 --- a/projects/igniteui-angular/carousel/src/carousel/carousel.component.spec.ts +++ b/projects/igniteui-angular/carousel/src/carousel/carousel.component.spec.ts @@ -1,5 +1,5 @@ import { Component, ViewChild, TemplateRef, ChangeDetectionStrategy, ElementRef, provideZonelessChangeDetection, inject, ChangeDetectorRef } from '@angular/core'; -import { TestBed, fakeAsync, tick, waitForAsync } from '@angular/core/testing'; +import { ComponentFixture, TestBed, fakeAsync, tick, waitForAsync } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { IgxCarouselComponent, @@ -10,6 +10,7 @@ import { IgxSlideComponent } from './slide.component'; import { IgxCarouselIndicatorDirective, IgxCarouselNextButtonDirective, IgxCarouselPrevButtonDirective } from './carousel.directives'; import { CarouselIndicatorsOrientation, CarouselAnimationType } from './enums'; import { UIInteractions, wait } from 'igniteui-angular/test-utils/ui-interactions.spec'; +import { CarouselResourceStringsEN, changei18n } from 'igniteui-angular/core'; describe('Carousel', () => { let fixture; @@ -1069,6 +1070,48 @@ describe('Carousel', () => { expect(carousel.current).toEqual(2); }); }); + + describe('Resource Strings', () => { + let fix: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [NoopAnimationsModule, CarouselTestComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fix = TestBed.createComponent(CarouselTestComponent); + fix.detectChanges(); + carousel = fix.componentInstance.carousel; + }); + + it('should return full resource strings when partial resourceStrings are set', () => { + carousel.resourceStrings = { igx_carousel_of: 'out of' }; + fix.detectChanges(); + + expect(carousel.resourceStrings.igx_carousel_of).toBe('out of'); + expect(carousel.resourceStrings.igx_carousel_slide).toBe('slide'); + expect(carousel.resourceStrings.igx_carousel_previous_slide).toBe('previous slide'); + expect(carousel.resourceStrings.igx_carousel_next_slide).toBe('next slide'); + }); + + it('should update non-overridden resource strings when global i18n changes', () => { + carousel.resourceStrings = { igx_carousel_of: 'custom of' }; + fix.detectChanges(); + + changei18n({ igx_carousel_slide: 'foto' }); + fix.detectChanges(); + + expect(carousel.resourceStrings.igx_carousel_of).toBe('custom of'); + expect(carousel.resourceStrings.igx_carousel_slide).toBe('foto'); + expect(carousel.resourceStrings.igx_carousel_previous_slide).toBe('previous slide'); + expect(carousel.resourceStrings.igx_carousel_next_slide).toBe('next slide'); + + // Restore defaults + changei18n(CarouselResourceStringsEN); + }); + }); }); describe('Carousel Zoneless Tests:', () => { @@ -1112,6 +1155,7 @@ describe('Carousel Zoneless Tests:', () => { }); }); + class HelperTestFunctions { public static NEXT_BUTTON_CLASS = '.igx-carousel__arrow--next'; public static PRIV_BUTTON_CLASS = '.igx-carousel__arrow--prev'; diff --git a/projects/igniteui-angular/carousel/src/carousel/carousel.component.ts b/projects/igniteui-angular/carousel/src/carousel/carousel.component.ts index 0cbb261d88d..864eb9c47b0 100644 --- a/projects/igniteui-angular/carousel/src/carousel/carousel.component.ts +++ b/projects/igniteui-angular/carousel/src/carousel/carousel.component.ts @@ -390,6 +390,7 @@ export class IgxCarouselComponent extends IgxCarouselComponentBase implements On protected override previousItem!: IgxSlideComponent; private _interval!: number; private _resourceStrings: ICarouselResourceStrings = null!; + private _customResourceStrings: ICarouselResourceStrings = null!; private _defaultResourceStrings = getCurrentResourceStrings(CarouselResourceStringsEN); private lastInterval: any; private playing!: boolean; @@ -405,14 +406,15 @@ export class IgxCarouselComponent extends IgxCarouselComponentBase implements On */ @Input() public set resourceStrings(value: ICarouselResourceStrings) { - this._resourceStrings = Object.assign({}, this._resourceStrings, value); + this._resourceStrings = value; + this._customResourceStrings = Object.assign({}, this._defaultResourceStrings, this._resourceStrings); } /** * An accessor that returns the resource strings. */ public get resourceStrings(): ICarouselResourceStrings { - return this._resourceStrings || this._defaultResourceStrings; + return this._resourceStrings ? this._customResourceStrings : this._defaultResourceStrings; } /** @hidden */ @@ -557,6 +559,7 @@ export class IgxCarouselComponent extends IgxCarouselComponentBase implements On this.differ = this.iterableDiffers.find([]).create(null!); onResourceChangeHandle(this.destroy$, () => { this._defaultResourceStrings = getCurrentResourceStrings(CarouselResourceStringsEN, false); + this._customResourceStrings = this._resourceStrings ? Object.assign({}, this._defaultResourceStrings, this._resourceStrings) : null!; }, this); } diff --git a/projects/igniteui-angular/chips/src/chips/chip.component.ts b/projects/igniteui-angular/chips/src/chips/chip.component.ts index d96a938eb82..aa359182f47 100644 --- a/projects/igniteui-angular/chips/src/chips/chip.component.ts +++ b/projects/igniteui-angular/chips/src/chips/chip.component.ts @@ -350,14 +350,15 @@ export class IgxChipComponent implements OnInit, OnDestroy { */ @Input() public set resourceStrings(value: IChipResourceStrings) { - this._resourceStrings = Object.assign({}, this._resourceStrings, value); + this._resourceStrings = value; + this._customResourceStrings = Object.assign({}, this._defaultResourceStrings, this._resourceStrings); } /** * An accessor that returns the resource strings. */ public get resourceStrings(): IChipResourceStrings { - return this._resourceStrings || this._defaultResourceStrings; + return this._resourceStrings ? this._customResourceStrings : this._defaultResourceStrings; } /** @@ -614,11 +615,13 @@ export class IgxChipComponent implements OnInit, OnDestroy { protected _movedWhileRemoving = false; protected computedStyles?: CSSStyleDeclaration; private _resourceStrings: IChipResourceStrings | null = null; + private _customResourceStrings: IChipResourceStrings = null!; private _defaultResourceStrings = getCurrentResourceStrings(ChipResourceStringsEN); constructor() { onResourceChangeHandle(this.destroy$, () => { this._defaultResourceStrings = getCurrentResourceStrings(ChipResourceStringsEN, false); + this._customResourceStrings = this._resourceStrings ? Object.assign({}, this._defaultResourceStrings, this._resourceStrings) : null!; }, this); } diff --git a/projects/igniteui-angular/chips/src/chips/chip.spec.ts b/projects/igniteui-angular/chips/src/chips/chip.spec.ts index 4a186b6ae47..9f3531d9cc9 100644 --- a/projects/igniteui-angular/chips/src/chips/chip.spec.ts +++ b/projects/igniteui-angular/chips/src/chips/chip.spec.ts @@ -7,7 +7,7 @@ import { IgxPrefixDirective } from '../../../input-group/src/public_api'; import { IgxLabelDirective } from '../../../input-group/src/public_api'; import { IgxSuffixDirective } from '../../../input-group/src/public_api'; import { IgxIconComponent } from 'igniteui-angular/icon'; -import { getComponentSize } from 'igniteui-angular/core'; +import { ChipResourceStringsEN, changei18n, getComponentSize } from 'igniteui-angular/core'; import { ControlsFunction } from 'igniteui-angular/test-utils/controls-functions.spec'; import { UIInteractions, wait } from 'igniteui-angular/test-utils/ui-interactions.spec'; @@ -399,6 +399,45 @@ describe('IgxChip', () => { expect(firstChipSuffixText).toEqual('suf'); }); }); + + describe('Resource Strings', () => { + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [TestChipComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fix = TestBed.createComponent(TestChipComponent); + fix.detectChanges(); + }); + + it('should return full resource strings when partial resourceStrings are set', () => { + const chip = fix.componentInstance.chips.first; + + chip.resourceStrings = { igx_chip_remove: 'Custom Remove' }; + fix.detectChanges(); + + expect(chip.resourceStrings.igx_chip_remove).toBe('Custom Remove'); + expect(chip.resourceStrings.igx_chip_select).toBe('select chip'); + }); + + it('should update non-overridden resource strings when global i18n changes', () => { + const chip = fix.componentInstance.chips.first; + + chip.resourceStrings = { igx_chip_remove: 'Custom Remove' }; + fix.detectChanges(); + + changei18n({ igx_chip_select: 'Global Select' }); + fix.detectChanges(); + + expect(chip.resourceStrings.igx_chip_remove).toBe('Custom Remove'); + expect(chip.resourceStrings.igx_chip_select).toBe('Global Select'); + + // Restore defaults + changei18n(ChipResourceStringsEN); + }); + }); }); class HelperTestFunctions { diff --git a/projects/igniteui-angular/combo/src/combo/combo.common.ts b/projects/igniteui-angular/combo/src/combo/combo.common.ts index 8f0def50c5c..0d61396c6af 100644 --- a/projects/igniteui-angular/combo/src/combo/combo.common.ts +++ b/projects/igniteui-angular/combo/src/combo/combo.common.ts @@ -496,10 +496,11 @@ export abstract class IgxComboBaseDirective implements IgxComboBase, AfterViewCh */ @Input() public get resourceStrings(): IComboResourceStrings { - return this._resourceStrings || this._defaultResourceStrings; + return this._resourceStrings ? this._customResourceStrings : this._defaultResourceStrings; } public set resourceStrings(value: IComboResourceStrings) { - this._resourceStrings = Object.assign({}, this._resourceStrings, value); + this._resourceStrings = value; + this._customResourceStrings = Object.assign({}, this._defaultResourceStrings, this._resourceStrings); } /** @@ -978,6 +979,7 @@ export abstract class IgxComboBaseDirective implements IgxComboBase, AfterViewCh protected _displayKey!: string; protected _remoteSelection = {}; protected _resourceStrings: IComboResourceStrings = null!; + protected _customResourceStrings: IComboResourceStrings = getCurrentResourceStrings(ComboResourceStringsEN); protected _defaultResourceStrings = getCurrentResourceStrings(ComboResourceStringsEN); protected _valid = IgxInputState.INITIAL; protected ngControl: NgControl = null!; @@ -1006,6 +1008,7 @@ export abstract class IgxComboBaseDirective implements IgxComboBase, AfterViewCh constructor() { onResourceChangeHandle(this.destroy$, () => { this._defaultResourceStrings = getCurrentResourceStrings(ComboResourceStringsEN, false); + this._customResourceStrings = this._resourceStrings ? Object.assign({}, this._defaultResourceStrings, this._resourceStrings) : null!; }, this); } diff --git a/projects/igniteui-angular/combo/src/combo/combo.component.spec.ts b/projects/igniteui-angular/combo/src/combo/combo.component.spec.ts index 921ec9cc46f..38b8f5b3ccb 100644 --- a/projects/igniteui-angular/combo/src/combo/combo.component.spec.ts +++ b/projects/igniteui-angular/combo/src/combo/combo.component.spec.ts @@ -14,6 +14,7 @@ import { IForOfState } from '../../../directives/src/directives/for-of/for_of.di import { IgxInputState } from '../../../input-group/src/public_api'; import { IGX_INPUT_GROUP_TYPE, IgxLabelDirective } from '../../../input-group/src/public_api'; import { AbsoluteScrollStrategy, ConnectedPositioningStrategy } from 'igniteui-angular/core'; +import { ComboResourceStringsEN, changei18n } from 'igniteui-angular/core'; import { IgxComboAddItemComponent } from './combo-add-item.component'; import { IgxComboDropDownComponent } from './combo-dropdown.component'; import { IgxComboItemComponent } from './combo-item.component'; @@ -3772,6 +3773,49 @@ describe('igxCombo', () => { })); }); }); + + describe('Resource Strings', () => { + let fix: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [NoopAnimationsModule, IgxComboSampleComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fix = TestBed.createComponent(IgxComboSampleComponent); + fix.detectChanges(); + }); + + it('should return full resource strings when partial resourceStrings are set', () => { + combo = fix.componentInstance.combo; + + combo.resourceStrings = { igx_combo_empty_message: 'Nothing here' }; + fix.detectChanges(); + + expect(combo.resourceStrings.igx_combo_empty_message).toBe('Nothing here'); + expect(combo.resourceStrings.igx_combo_filter_search_placeholder).toBe('Enter a Search Term'); + expect(combo.resourceStrings.igx_combo_clearItems_placeholder).toBe('Clear Selection'); + }); + + it('should update non-overridden resource strings when global i18n changes', () => { + combo = fix.componentInstance.combo; + + combo.resourceStrings = { igx_combo_empty_message: 'Custom Empty' }; + fix.detectChanges(); + + changei18n({ igx_combo_filter_search_placeholder: 'Suchen...' }); + fix.detectChanges(); + + expect(combo.resourceStrings.igx_combo_empty_message).toBe('Custom Empty'); + expect(combo.resourceStrings.igx_combo_filter_search_placeholder).toBe('Suchen...'); + expect(combo.resourceStrings.igx_combo_clearItems_placeholder).toBe('Clear Selection'); + + // Restore defaults + changei18n(ComboResourceStringsEN); + }); + }); }); @Component({ diff --git a/projects/igniteui-angular/date-picker/src/date-range-picker/date-range-picker.component.spec.ts b/projects/igniteui-angular/date-picker/src/date-range-picker/date-range-picker.component.spec.ts index 81d0984881f..55dffffc889 100644 --- a/projects/igniteui-angular/date-picker/src/date-range-picker/date-range-picker.component.spec.ts +++ b/projects/igniteui-angular/date-picker/src/date-range-picker/date-range-picker.component.spec.ts @@ -8,7 +8,7 @@ import { By } from '@angular/platform-browser'; import { ControlsFunction } from '../../../test-utils/controls-functions.spec'; import { UIInteractions } from '../../../test-utils/ui-interactions.spec'; import { HelperTestFunctions } from '../../../test-utils/calendar-helper-utils'; -import { CancelableEventArgs, WEEKDAYS } from 'igniteui-angular/core'; +import { CancelableEventArgs, WEEKDAYS, DateRangePickerResourceStringsEN, changei18n } from 'igniteui-angular/core'; import { IgxDateRangeSeparatorDirective, IgxDateRangeStartComponent } from './date-range-picker-inputs.common'; import { IgxDateTimeEditorDirective } from '../../../directives/src/directives/date-time-editor/date-time-editor.directive'; import { DateRangeType } from 'igniteui-angular/core'; @@ -2269,6 +2269,49 @@ describe('IgxDateRangePicker', () => { }); }); }); + + describe('Resource Strings', () => { + let fix: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [NoopAnimationsModule, DateRangeDefaultComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fix = TestBed.createComponent(DateRangeDefaultComponent); + fix.detectChanges(); + }); + + it('should return full resource strings when partial resourceStrings are set', () => { + const drp = fix.componentInstance.dateRange; + + drp.resourceStrings = { igx_date_range_picker_done_button: 'OK' }; + fix.detectChanges(); + + expect(drp.resourceStrings.igx_date_range_picker_done_button).toBe('OK'); + expect(drp.resourceStrings.igx_date_range_picker_cancel_button).toBe('Cancel'); + expect(drp.resourceStrings.igx_date_range_picker_date_separator).toBe(' - '); + }); + + it('should update non-overridden resource strings when global i18n changes', () => { + const drp = fix.componentInstance.dateRange; + + drp.resourceStrings = { igx_date_range_picker_done_button: 'Fertig' }; + fix.detectChanges(); + + changei18n({ igx_date_range_picker_cancel_button: 'Abbrechen' }); + fix.detectChanges(); + + expect(drp.resourceStrings.igx_date_range_picker_done_button).toBe('Fertig'); + expect(drp.resourceStrings.igx_date_range_picker_cancel_button).toBe('Abbrechen'); + expect(drp.resourceStrings.igx_date_range_picker_date_separator).toBe(' - '); + + // Restore defaults + changei18n(DateRangePickerResourceStringsEN); + }); + }); }); @Component({ diff --git a/projects/igniteui-angular/date-picker/src/date-range-picker/date-range-picker.component.ts b/projects/igniteui-angular/date-picker/src/date-range-picker/date-range-picker.component.ts index e29fd242a16..3edfb2216d4 100644 --- a/projects/igniteui-angular/date-picker/src/date-range-picker/date-range-picker.component.ts +++ b/projects/igniteui-angular/date-picker/src/date-range-picker/date-range-picker.component.ts @@ -373,14 +373,15 @@ export class IgxDateRangePickerComponent extends PickerBaseDirective */ @Input() public set resourceStrings(value: IDateRangePickerResourceStrings) { - this._resourceStrings = Object.assign({}, this._resourceStrings, value); + this._resourceStrings = value; + this._customResourceStrings = Object.assign({}, this._defaultResourceStrings, this._resourceStrings); } /** * An accessor that returns the resource strings. */ public get resourceStrings(): IDateRangePickerResourceStrings { - return this._resourceStrings || this._defaultResourceStrings; + return this._resourceStrings ? this._customResourceStrings : this._defaultResourceStrings; } /** @@ -632,6 +633,7 @@ export class IgxDateRangePickerComponent extends PickerBaseDirective } private _resourceStrings: IDateRangePickerResourceStrings = null!; + private _customResourceStrings: IDateRangePickerResourceStrings = null!; private _defaultResourceStrings = getCurrentResourceStrings(DateRangePickerResourceStringsEN); private _doneButtonText: string = null!; private _cancelButtonText: string = null!; @@ -1344,6 +1346,7 @@ export class IgxDateRangePickerComponent extends PickerBaseDirective protected override updateResources(): void { this._defaultResourceStrings = getCurrentResourceStrings(DateRangePickerResourceStringsEN, false, this._locale); + this._customResourceStrings = this._resourceStrings ? Object.assign({}, this._defaultResourceStrings, this._resourceStrings) : null!; } private _initializeCalendarContainer(componentInstance: IgxCalendarContainerComponent) { diff --git a/projects/igniteui-angular/grids/core/src/pivot-grid-dimensions.ts b/projects/igniteui-angular/grids/core/src/pivot-grid-dimensions.ts index 92628641bce..0bb8231f798 100644 --- a/projects/igniteui-angular/grids/core/src/pivot-grid-dimensions.ts +++ b/projects/igniteui-angular/grids/core/src/pivot-grid-dimensions.ts @@ -71,11 +71,12 @@ export class IgxPivotDateDimension implements IPivotDimension { * By default it uses EN resources. */ public set resourceStrings(value: IGridResourceStrings) { - this._resourceStrings = Object.assign({}, this._resourceStrings, value); + this._resourceStrings = value; + this._customResourceStrings = Object.assign({}, getCurrentResourceStrings(GridResourceStringsEN, false), this._resourceStrings); } public get resourceStrings(): IGridResourceStrings { - return this._resourceStrings || getCurrentResourceStrings(GridResourceStringsEN, false); + return this._resourceStrings ? this._customResourceStrings : getCurrentResourceStrings(GridResourceStringsEN, false); } /** @@ -111,6 +112,7 @@ export class IgxPivotDateDimension implements IPivotDimension { public locale?: string; public displayName!: string; private _resourceStrings: IGridResourceStrings = null!; + private _customResourceStrings: IGridResourceStrings = null!; private _baseDimension: IPivotDimension; private _options: IPivotDateDimensionOptions = {}; diff --git a/projects/igniteui-angular/grids/grid/src/grid-base.directive.ts b/projects/igniteui-angular/grids/grid/src/grid-base.directive.ts index b3a28813ad9..31c30a1a2c6 100644 --- a/projects/igniteui-angular/grids/grid/src/grid-base.directive.ts +++ b/projects/igniteui-angular/grids/grid/src/grid-base.directive.ts @@ -1815,12 +1815,13 @@ export abstract class IgxGridBaseDirective implements GridType, */ @Input() public set resourceStrings(value: IGridResourceStrings) { - this._resourceStrings = Object.assign({}, this.resourceStrings, value); + this._resourceStrings = value; + this._customResourceStrings = Object.assign({}, this._defaultResourceStrings, this._resourceStrings); this.notifyChanges(); } public get resourceStrings(): IGridResourceStrings { - return this._resourceStrings || this._defaultResourceStrings; + return this._resourceStrings ? this._customResourceStrings : this._defaultResourceStrings; } /** @@ -1959,7 +1960,7 @@ export abstract class IgxGridBaseDirective implements GridType, public set locale(value: string) { if (value !== this._locale) { this._locale = this.i18nFormatter.verifyLocale(value); - this._defaultResourceStrings = getCurrentResourceStrings(GridResourceStringsEN, false, this._locale); + this.updateResources(this._locale); this._currencyPositionLeft = undefined!; this.summaryService.clearSummaryCache(); this.pipeTrigger++; @@ -3171,6 +3172,7 @@ export abstract class IgxGridBaseDirective implements GridType, protected _hGridSchema!: EntityType[]; protected gridComputedStyles!: CSSStyleDeclaration; protected _resourceStrings: IGridResourceStrings = null!; + protected _customResourceStrings: IGridResourceStrings = getCurrentResourceStrings(GridResourceStringsEN); /** @hidden @internal */ public get paginator(): IgxPaginatorComponent | undefined { @@ -8278,7 +8280,8 @@ export abstract class IgxGridBaseDirective implements GridType, private onResourceChange(args: CustomEvent) { this._defaultLocale = args.detail.newLocale; if (!this._locale) { - this._defaultResourceStrings = getCurrentResourceStrings(GridResourceStringsEN, false); + // Avoid unnecessary fetch of resources, since they should be already retrieved when setting custom locale. + this.updateResources(); } // Reset currency position because of new locale. this._currencyPositionLeft = undefined!; @@ -8287,4 +8290,9 @@ export abstract class IgxGridBaseDirective implements GridType, this.notifyChanges(true); } } + + private updateResources(locale?: string) { + this._defaultResourceStrings = getCurrentResourceStrings(GridResourceStringsEN, false, locale); + this._customResourceStrings = this._resourceStrings ? Object.assign({}, this._defaultResourceStrings, this._resourceStrings) : null!; + } } diff --git a/projects/igniteui-angular/grids/grid/src/grid.component.spec.ts b/projects/igniteui-angular/grids/grid/src/grid.component.spec.ts index 747caa4e14c..266e00a60c9 100644 --- a/projects/igniteui-angular/grids/grid/src/grid.component.spec.ts +++ b/projects/igniteui-angular/grids/grid/src/grid.component.spec.ts @@ -1,5 +1,5 @@ import { AfterViewInit, ChangeDetectorRef, Component, Injectable, OnInit, ViewChild, TemplateRef, inject, ChangeDetectionStrategy, provideZonelessChangeDetection } from '@angular/core'; -import { TestBed, fakeAsync, tick, flush, waitForAsync } from '@angular/core/testing'; +import { TestBed, fakeAsync, tick, flush, waitForAsync, ComponentFixture } from '@angular/core/testing'; import { BehaviorSubject, firstValueFrom, Observable } from 'rxjs'; import { By } from '@angular/platform-browser'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; @@ -16,7 +16,7 @@ import { IgxGridRowComponent } from './grid-row.component'; import { GRID_SCROLL_CLASS, GridFunctions } from '../../../test-utils/grid-functions.spec'; import { AsyncPipe } from '@angular/common'; import { setElementSize, ymd } from '../../../test-utils/helper-utils.spec'; -import { FilteringExpressionsTree, FilteringLogic, getComponentSize, GridColumnDataType, IgxNumberFilteringOperand, IgxStringFilteringOperand, ISortingExpression, ɵSize, SortingDirection } from 'igniteui-angular/core'; +import { FilteringExpressionsTree, FilteringLogic, getComponentSize, GridColumnDataType, IgxNumberFilteringOperand, IgxStringFilteringOperand, ISortingExpression, ɵSize, SortingDirection, GridResourceStringsEN, changei18n } from 'igniteui-angular/core'; import { IgxPaginatorComponent, IgxPaginatorContentDirective } from 'igniteui-angular/paginator'; import { SCROLL_THROTTLE_TIME_MULTIPLIER } from './../src/grid-base.directive'; @@ -3434,6 +3434,48 @@ describe('IgxGrid Component Tests #grid', () => { expect(() => fix.detectChanges()).not.toThrow(); }); }); + + describe('Resource Strings', () => { + let fix: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [NoopAnimationsModule, IgxGridTestComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fix = TestBed.createComponent(IgxGridTestComponent); + fix.detectChanges(); + }); + + it('should return full resource strings when partial resourceStrings are set', () => { + const grid = fix.componentInstance.grid; + + grid.resourceStrings = { igx_grid_emptyFilteredGrid_message: 'No results' }; + fix.detectChanges(); + + expect(grid.resourceStrings.igx_grid_emptyFilteredGrid_message).toBe('No results'); + expect(grid.resourceStrings.igx_grid_groupByArea_message).toBe( + 'Drag a column header and drop it here to group by that column.'); + }); + + it('should update non-overridden resource strings when global i18n changes', () => { + const grid = fix.componentInstance.grid; + + grid.resourceStrings = { igx_grid_emptyFilteredGrid_message: 'Custom Empty' }; + fix.detectChanges(); + + changei18n({ igx_grid_groupByArea_message: 'Hier ablegen' }); + fix.detectChanges(); + + expect(grid.resourceStrings.igx_grid_emptyFilteredGrid_message).toBe('Custom Empty'); + expect(grid.resourceStrings.igx_grid_groupByArea_message).toBe('Hier ablegen'); + + // Restore defaults + changei18n(GridResourceStringsEN); + }); + }); }); @Component({ diff --git a/projects/igniteui-angular/input-group/src/input-group/input-group.component.spec.ts b/projects/igniteui-angular/input-group/src/input-group/input-group.component.spec.ts index b10d4829a94..65bf00881f4 100644 --- a/projects/igniteui-angular/input-group/src/input-group/input-group.component.spec.ts +++ b/projects/igniteui-angular/input-group/src/input-group/input-group.component.spec.ts @@ -5,6 +5,7 @@ import { IgxInputGroupComponent } from './input-group.component'; import { UIInteractions } from '../../../test-utils/ui-interactions.spec'; import { IgxInputDirective, IgxPrefixDirective, IgxSuffixDirective } from '../public_api'; import { IGX_INPUT_GROUP_TYPE, IgxInputGroupType } from './inputGroupType'; +import { InputResourceStringsEN, changei18n } from 'igniteui-angular/core'; const INPUT_GROUP_CSS_CLASS = 'igx-input-group'; const INPUT_GROUP_BOX_CSS_CLASS = 'igx-input-group--box'; @@ -239,6 +240,38 @@ describe('IgxInputGroup', () => { inputGroupDebugElement.triggerEventHandler('click', pointerEvent); expect(document.activeElement).toEqual(input.nativeElement); }); + + describe('Resource Strings', () => { + it('should return full resource strings when partial resourceStrings are set', () => { + const fix = TestBed.createComponent(InputGroupComponent); + fix.detectChanges(); + const inputGroup = fix.componentInstance.igxInputGroup; + + inputGroup.resourceStrings = { igx_input_upload_button: 'Upload' }; + fix.detectChanges(); + + expect(inputGroup.resourceStrings.igx_input_upload_button).toBe('Upload'); + expect(inputGroup.resourceStrings.igx_input_file_placeholder).toBe('No file chosen'); + }); + + it('should update non-overridden resource strings when global i18n changes', () => { + const fix = TestBed.createComponent(InputGroupComponent); + fix.detectChanges(); + const inputGroup = fix.componentInstance.igxInputGroup; + + inputGroup.resourceStrings = { igx_input_upload_button: 'Custom Browse' }; + fix.detectChanges(); + + changei18n({ igx_input_file_placeholder: 'Keine Datei ausgewählt' }); + fix.detectChanges(); + + expect(inputGroup.resourceStrings.igx_input_upload_button).toBe('Custom Browse'); + expect(inputGroup.resourceStrings.igx_input_file_placeholder).toBe('Keine Datei ausgewählt'); + + // Restore defaults + changei18n(InputResourceStringsEN); + }); + }); }); @Component({ diff --git a/projects/igniteui-angular/input-group/src/input-group/input-group.component.ts b/projects/igniteui-angular/input-group/src/input-group/input-group.component.ts index 947c0509754..10f082748e7 100644 --- a/projects/igniteui-angular/input-group/src/input-group/input-group.component.ts +++ b/projects/igniteui-angular/input-group/src/input-group/input-group.component.ts @@ -54,14 +54,15 @@ export class IgxInputGroupComponent implements IgxInputGroupBase, AfterContentCh */ @Input() public set resourceStrings(value: IInputResourceStrings) { - this._resourceStrings = Object.assign({}, this._resourceStrings, value); + this._resourceStrings = value; + this._customResourceStrings = Object.assign({}, this._defaultResourceStrings, this._resourceStrings); } /** * Returns the resource strings. */ public get resourceStrings(): IInputResourceStrings { - return this._resourceStrings || this._defaultResourceStrings; + return this._resourceStrings ? this._customResourceStrings : this._defaultResourceStrings; } /** @@ -149,6 +150,7 @@ export class IgxInputGroupComponent implements IgxInputGroupBase, AfterContentCh private _filled = false; private _theme: IgxTheme; private _resourceStrings: IInputResourceStrings | null = null; + private _customResourceStrings: IInputResourceStrings = null!; private _defaultResourceStrings = getCurrentResourceStrings(InputResourceStringsEN); private _readOnly: undefined | boolean; @@ -254,6 +256,7 @@ export class IgxInputGroupComponent implements IgxInputGroupBase, AfterContentCh this._destroyRef.onDestroy(() => themeChange.unsubscribe()); onResourceChangeHandle(this._destroyRef, () => { this._defaultResourceStrings = getCurrentResourceStrings(InputResourceStringsEN, false); + this._customResourceStrings = this._resourceStrings ? Object.assign({}, this._defaultResourceStrings, this._resourceStrings) : null!; }, this); } diff --git a/projects/igniteui-angular/list/src/list/list.component.spec.ts b/projects/igniteui-angular/list/src/list/list.component.spec.ts index 5f8a39a347d..a895b7280b8 100644 --- a/projects/igniteui-angular/list/src/list/list.component.spec.ts +++ b/projects/igniteui-angular/list/src/list/list.component.spec.ts @@ -29,6 +29,7 @@ import { } from '../../../test-utils/list-components.spec'; import { wait } from '../../../test-utils/ui-interactions.spec'; import { GridFunctions } from '../../../test-utils/grid-functions.spec'; +import { ListResourceStringsEN, changei18n } from 'igniteui-angular/core'; describe('List', () => { @@ -817,6 +818,36 @@ describe('List', () => { expect(listLine.parent.nativeElement).toHaveClass('igx-list__item-lines'); }); + it('should return full resource strings when partial resourceStrings are set', () => { + const fix = TestBed.createComponent(EmptyListComponent); + fix.detectChanges(); + const list = fix.componentInstance.list; + + list.resourceStrings = { igx_list_no_items: 'No results found' }; + fix.detectChanges(); + + expect(list.resourceStrings.igx_list_no_items).toBe('No results found'); + expect(list.resourceStrings.igx_list_loading).toBe('Loading data from the server...'); + }); + + it('should update non-overridden resource strings when global i18n changes', () => { + const fix = TestBed.createComponent(EmptyListComponent); + fix.detectChanges(); + const list = fix.componentInstance.list; + + list.resourceStrings = { igx_list_no_items: 'Custom No Items' }; + fix.detectChanges(); + + changei18n({ igx_list_loading: 'Fetching data...' }); + fix.detectChanges(); + + expect(list.resourceStrings.igx_list_no_items).toBe('Custom No Items'); + expect(list.resourceStrings.igx_list_loading).toBe('Fetching data...'); + + // Restore defaults + changei18n(ListResourceStringsEN); + }); + /* factorX - the coefficient used to calculate deltaX. Pan left by providing negative factorX; Pan right - positive factorX. */ diff --git a/projects/igniteui-angular/list/src/list/list.component.ts b/projects/igniteui-angular/list/src/list/list.component.ts index a3b86e2ec5b..7c98ebf28f6 100644 --- a/projects/igniteui-angular/list/src/list/list.component.ts +++ b/projects/igniteui-angular/list/src/list/list.component.ts @@ -456,6 +456,7 @@ export class IgxListComponent extends IgxListBaseDirective { protected defaultDataLoadingTemplate!: TemplateRef; private _resourceStrings: IListResourceStrings = null!; + private _customResourceStrings: IListResourceStrings = null!; private _defaultResourceStrings = getCurrentResourceStrings(ListResourceStringsEN); /** @@ -464,20 +465,22 @@ export class IgxListComponent extends IgxListBaseDirective { */ @Input() public set resourceStrings(value: IListResourceStrings) { - this._resourceStrings = Object.assign({}, this._resourceStrings, value); + this._resourceStrings = value; + this._customResourceStrings = Object.assign({}, this._defaultResourceStrings, this._resourceStrings); } /** * Returns the resource strings. */ public get resourceStrings(): IListResourceStrings { - return this._resourceStrings || this._defaultResourceStrings; + return this._resourceStrings ? this._customResourceStrings : this._defaultResourceStrings; } constructor() { super(); onResourceChangeHandle(this.destroyRef, () => { this._defaultResourceStrings = getCurrentResourceStrings(ListResourceStringsEN, false); + this._customResourceStrings = this._resourceStrings ? Object.assign({}, this._defaultResourceStrings, this._resourceStrings) : null!; }, this); } diff --git a/projects/igniteui-angular/paginator/src/paginator/paginator.component.spec.ts b/projects/igniteui-angular/paginator/src/paginator/paginator.component.spec.ts index 5663d3d0d11..2b291788bb5 100644 --- a/projects/igniteui-angular/paginator/src/paginator/paginator.component.spec.ts +++ b/projects/igniteui-angular/paginator/src/paginator/paginator.component.spec.ts @@ -7,6 +7,7 @@ import { GridFunctions } from '../../../test-utils/grid-functions.spec'; import { ControlsFunction } from '../../../test-utils/controls-functions.spec'; import { first } from 'rxjs/operators'; import { IgxButtonDirective } from '../../../directives/src/directives/button/button.directive'; +import { PaginatorResourceStringsEN, changei18n } from 'igniteui-angular/core'; describe('IgxPaginator with default settings', () => { beforeEach(waitForAsync(() => { @@ -280,6 +281,25 @@ describe('IgxPaginator with default settings', () => { expect(paginator.resourceStrings.igx_paginator_next_page_button_text).toBe('Next page'); }); + it('should update non-overridden resource strings when global i18n changes', () => { + const fix = TestBed.createComponent(DefaultPaginatorComponent); + fix.detectChanges(); + const paginator = fix.componentInstance.paginator; + + paginator.resourceStrings = { igx_paginator_label: 'Custom per page' }; + fix.detectChanges(); + + changei18n({ igx_paginator_pager_text: 'von' }); + fix.detectChanges(); + + expect(paginator.resourceStrings.igx_paginator_label).toBe('Custom per page'); + expect(paginator.resourceStrings.igx_paginator_pager_text).toBe('von'); + expect(paginator.resourceStrings.igx_paginator_first_page_button_text).toBe('Go to first page'); + + // Restore defaults + changei18n(PaginatorResourceStringsEN); + }); + }); describe('IgxPaginator with custom settings', () => { diff --git a/projects/igniteui-angular/paginator/src/paginator/paginator.component.ts b/projects/igniteui-angular/paginator/src/paginator/paginator.component.ts index d1e4f2ccb40..8b4760b1b34 100644 --- a/projects/igniteui-angular/paginator/src/paginator/paginator.component.ts +++ b/projects/igniteui-angular/paginator/src/paginator/paginator.component.ts @@ -147,6 +147,7 @@ export class IgxPaginatorComponent implements IgxPaginatorToken { protected _perPage = 15; private _resourceStrings: IPaginatorResourceStrings = null!; + private _customResourceStrings: IPaginatorResourceStrings = null!; private _defaultResourceStrings = getCurrentResourceStrings(PaginatorResourceStringsEN, true); private _overlaySettings: OverlaySettings = {}; private defaultSelectValues = [5, 10, 15, 25, 50, 100, 500]; @@ -275,19 +276,21 @@ export class IgxPaginatorComponent implements IgxPaginatorToken { */ @Input() public set resourceStrings(value: IPaginatorResourceStrings) { - this._resourceStrings = Object.assign({}, this.resourceStrings, value); + this._resourceStrings = value; + this._customResourceStrings = Object.assign({}, this._defaultResourceStrings, this._resourceStrings); } /** * An accessor that returns the resource strings. */ public get resourceStrings(): IPaginatorResourceStrings { - return this._resourceStrings || this._defaultResourceStrings; + return this._resourceStrings ? this._customResourceStrings : this._defaultResourceStrings; } constructor() { onResourceChangeHandle(this.destroyRef, () => { this._defaultResourceStrings = getCurrentResourceStrings(PaginatorResourceStringsEN, false); + this._customResourceStrings = this._resourceStrings ? Object.assign({}, this._defaultResourceStrings, this._resourceStrings) : null!; }, this); } diff --git a/projects/igniteui-angular/query-builder/src/query-builder/query-builder-header.component.ts b/projects/igniteui-angular/query-builder/src/query-builder/query-builder-header.component.ts index 75fd0fee003..961b1840622 100644 --- a/projects/igniteui-angular/query-builder/src/query-builder/query-builder-header.component.ts +++ b/projects/igniteui-angular/query-builder/src/query-builder/query-builder-header.component.ts @@ -21,6 +21,7 @@ export class IgxQueryBuilderHeaderComponent { private _destroyRef = inject(DestroyRef); private _resourceStrings: IQueryBuilderResourceStrings = null!; + private _customResourceStrings: IQueryBuilderResourceStrings = null!; private _defaultResourceStrings = getCurrentResourceStrings(QueryBuilderResourceStringsEN); /** @@ -61,19 +62,21 @@ export class IgxQueryBuilderHeaderComponent { */ @Input() public set resourceStrings(value: IQueryBuilderResourceStrings) { - this._resourceStrings = Object.assign({}, this._resourceStrings, value); + this._resourceStrings = value; + this._customResourceStrings = Object.assign({}, this._defaultResourceStrings, this._resourceStrings); } /** * Returns the resource strings. */ public get resourceStrings(): IQueryBuilderResourceStrings { - return this._resourceStrings || this._defaultResourceStrings; + return this._resourceStrings ? this._customResourceStrings : this._defaultResourceStrings; } constructor() { onResourceChangeHandle(this._destroyRef, () => { this._defaultResourceStrings = getCurrentResourceStrings(QueryBuilderResourceStringsEN, false); + this._customResourceStrings = this._resourceStrings ? Object.assign({}, this._defaultResourceStrings, this._resourceStrings) : null!; }, this); } } diff --git a/projects/igniteui-angular/query-builder/src/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/query-builder/src/query-builder/query-builder-tree.component.ts index 661ef61dfd6..eb67ece8fac 100644 --- a/projects/igniteui-angular/query-builder/src/query-builder/query-builder-tree.component.ts +++ b/projects/igniteui-angular/query-builder/src/query-builder/query-builder-tree.component.ts @@ -239,7 +239,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { */ public set locale(value: string) { this._locale = value; - this._defaultResourceStrings = getCurrentResourceStrings(QueryBuilderResourceStringsEN, false, this._locale); + this.updateResources(this._locale); } /** @@ -248,14 +248,15 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { */ @Input() public set resourceStrings(value: IQueryBuilderResourceStrings) { - this._resourceStrings = Object.assign({}, this._resourceStrings, value); + this._resourceStrings = value; + this._customResourceStrings = this._resourceStrings ? Object.assign({}, this._defaultResourceStrings, this._resourceStrings) : null!; } /** * Returns the resource strings. */ public get resourceStrings(): IQueryBuilderResourceStrings { - return this._resourceStrings || this._defaultResourceStrings; + return this._resourceStrings ? this._customResourceStrings : this._defaultResourceStrings; } /** @@ -483,7 +484,8 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { private _locale?: string; private _defaultLocale!: string; private _entityNewValue!: EntityType; - private _resourceStrings = null; + private _resourceStrings: IQueryBuilderResourceStrings | null = null; + private _customResourceStrings: IQueryBuilderResourceStrings = null!; private _defaultResourceStrings = getCurrentResourceStrings(QueryBuilderResourceStringsEN); /** @@ -1705,10 +1707,16 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { private onResourceChange(args: CustomEvent) { this._defaultLocale = args.detail.newLocale; if (!this._locale) { - this._defaultResourceStrings = getCurrentResourceStrings(QueryBuilderResourceStringsEN, false); + // Avoid unnecessary fetch of resources, since they should be already retrieved when setting custom locale. + this.updateResources(); } } + private updateResources(locale?: string) { + this._defaultResourceStrings = getCurrentResourceStrings(QueryBuilderResourceStringsEN, false, locale); + this._customResourceStrings = this._resourceStrings ? Object.assign({}, this._defaultResourceStrings, this._resourceStrings) : null!; + } + /** rootGroup is recreated after clicking Apply, which sets new expressionTree and calls init()*/ protected trackExpressionItem = trackByIdentity; } diff --git a/projects/igniteui-angular/query-builder/src/query-builder/query-builder.component.spec.ts b/projects/igniteui-angular/query-builder/src/query-builder/query-builder.component.spec.ts index 0ddae9cf169..9c35b89221f 100644 --- a/projects/igniteui-angular/query-builder/src/query-builder/query-builder.component.spec.ts +++ b/projects/igniteui-angular/query-builder/src/query-builder/query-builder.component.spec.ts @@ -1,5 +1,5 @@ import { waitForAsync, TestBed, ComponentFixture, fakeAsync, tick, flush } from '@angular/core/testing'; -import { FilteringExpressionsTree, FilteringLogic, IExpressionTree, IgxDateFilteringOperand, IgxNumberFilteringOperand } from 'igniteui-angular/core'; +import { FilteringExpressionsTree, FilteringLogic, IExpressionTree, IgxDateFilteringOperand, IgxNumberFilteringOperand, QueryBuilderResourceStringsEN, changei18n } from 'igniteui-angular/core'; import { IgxChipComponent } from 'igniteui-angular/chips'; import { IgxComboComponent } from 'igniteui-angular/combo'; import { IgxIconComponent } from 'igniteui-angular/icon'; @@ -3232,6 +3232,32 @@ describe('IgxQueryBuilder', () => { })); }); + + describe('Resource Strings', () => { + it('should return full resource strings when partial resourceStrings are set', () => { + queryBuilder.resourceStrings = { igx_query_builder_date_placeholder: 'Pick date' }; + fix.detectChanges(); + + expect(queryBuilder.resourceStrings.igx_query_builder_date_placeholder).toBe('Pick date'); + expect(queryBuilder.resourceStrings.igx_query_builder_filter_operator_and).toBe('And'); + expect(queryBuilder.resourceStrings.igx_query_builder_add_condition).toBe('Add condition'); + }); + + it('should update non-overridden resource strings when global i18n changes', () => { + queryBuilder.resourceStrings = { igx_query_builder_date_placeholder: 'Custom date' }; + fix.detectChanges(); + + changei18n({ igx_query_builder_filter_operator_and: 'Und' }); + fix.detectChanges(); + + expect(queryBuilder.resourceStrings.igx_query_builder_date_placeholder).toBe('Custom date'); + expect(queryBuilder.resourceStrings.igx_query_builder_filter_operator_and).toBe('Und'); + expect(queryBuilder.resourceStrings.igx_query_builder_add_condition).toBe('Add condition'); + + // Restore defaults + changei18n(QueryBuilderResourceStringsEN); + }); + }); }); @Component({ diff --git a/projects/igniteui-angular/query-builder/src/query-builder/query-builder.component.ts b/projects/igniteui-angular/query-builder/src/query-builder/query-builder.component.ts index be7fb41646c..c9fff3429e3 100644 --- a/projects/igniteui-angular/query-builder/src/query-builder/query-builder.component.ts +++ b/projects/igniteui-angular/query-builder/src/query-builder/query-builder.component.ts @@ -185,14 +185,15 @@ export class IgxQueryBuilderComponent implements OnDestroy { */ @Input() public set resourceStrings(value: IQueryBuilderResourceStrings) { - this._resourceStrings = Object.assign({}, this._resourceStrings, value); + this._resourceStrings = value; + this._customResourceStrings = Object.assign({}, this._defaultResourceStrings, this._resourceStrings); } /** * Returns the resource strings. */ public get resourceStrings(): IQueryBuilderResourceStrings { - return this._resourceStrings || this._defaultResourceStrings; + return this._resourceStrings ? this._customResourceStrings : this._defaultResourceStrings; } /** @@ -255,6 +256,7 @@ export class IgxQueryBuilderComponent implements OnDestroy { private destroy$ = new Subject(); private _resourceStrings: IQueryBuilderResourceStrings = null!; + private _customResourceStrings: IQueryBuilderResourceStrings = null!; private _defaultResourceStrings = getCurrentResourceStrings(QueryBuilderResourceStringsEN); private _expressionTree!: IExpressionTree; private _fields!: FieldType[]; @@ -266,6 +268,7 @@ export class IgxQueryBuilderComponent implements OnDestroy { this.registerSVGIcons(); onResourceChangeHandle(this.destroy$, () => { this._defaultResourceStrings = getCurrentResourceStrings(QueryBuilderResourceStringsEN, false); + this._customResourceStrings = this._resourceStrings ? Object.assign({}, this._defaultResourceStrings, this._resourceStrings) : null!; }, this); } diff --git a/projects/igniteui-angular/time-picker/src/time-picker/time-picker.component.ts b/projects/igniteui-angular/time-picker/src/time-picker/time-picker.component.ts index 91a0c976d66..c0beee6fbf1 100644 --- a/projects/igniteui-angular/time-picker/src/time-picker/time-picker.component.ts +++ b/projects/igniteui-angular/time-picker/src/time-picker/time-picker.component.ts @@ -493,6 +493,7 @@ export class IgxTimePickerComponent extends PickerBaseDirective private _dateMaxValue!: Date; private _selectedDate!: Date; private _resourceStrings: ITimePickerResourceStrings = null!; + private _customResourceStrings: ITimePickerResourceStrings = null!; private _defaultResourceStrings = getCurrentResourceStrings(TimePickerResourceStringsEN); private _okButtonLabel: string | null = null; private _cancelButtonLabel: string | null = null; @@ -570,14 +571,15 @@ export class IgxTimePickerComponent extends PickerBaseDirective */ @Input() public set resourceStrings(value: ITimePickerResourceStrings) { - this._resourceStrings = Object.assign({}, this._resourceStrings, value); + this._resourceStrings = value; + this._customResourceStrings = Object.assign({}, this._defaultResourceStrings, this._resourceStrings); } /** * An accessor that returns the resource strings. */ public get resourceStrings(): ITimePickerResourceStrings { - return this._resourceStrings || this._defaultResourceStrings; + return this._resourceStrings ? this._customResourceStrings : this._defaultResourceStrings; } /** @@ -1123,6 +1125,7 @@ export class IgxTimePickerComponent extends PickerBaseDirective protected override updateResources() { this._defaultResourceStrings = getCurrentResourceStrings(TimePickerResourceStringsEN, false, this._locale); + this._customResourceStrings = this._resourceStrings ? Object.assign({}, this._defaultResourceStrings, this._resourceStrings) : null!; } private get isTouchedOrDirty(): boolean { diff --git a/projects/igniteui-angular/tree/src/tree/tree-node/tree-node.component.ts b/projects/igniteui-angular/tree/src/tree/tree-node/tree-node.component.ts index 1627ece4645..a2473979953 100644 --- a/projects/igniteui-angular/tree/src/tree/tree-node/tree-node.component.ts +++ b/projects/igniteui-angular/tree/src/tree/tree-node/tree-node.component.ts @@ -224,14 +224,15 @@ export class IgxTreeNodeComponent extends ToggleAnimationPlayer implements Ig */ @Input() public set resourceStrings(value: ITreeResourceStrings) { - this._resourceStrings = Object.assign({}, this._resourceStrings, value); + this._resourceStrings = value; + this._customResourceStrings = Object.assign({}, this._defaultResourceStrings, this._resourceStrings); } /** * An accessor that returns the resource strings. */ public get resourceStrings(): ITreeResourceStrings { - return this._resourceStrings || this._defaultResourceStrings; + return this._resourceStrings ? this._customResourceStrings : this._defaultResourceStrings; } /** @@ -384,6 +385,7 @@ export class IgxTreeNodeComponent extends ToggleAnimationPlayer implements Ig public registeredChildren: IgxTreeNodeLinkDirective[] = []; private _resourceStrings: ITreeResourceStrings = null!; + private _customResourceStrings: ITreeResourceStrings = null!; private _defaultResourceStrings = getCurrentResourceStrings(TreeResourceStringsEN); private _tabIndex: number | null = null; private _disabled = false; @@ -392,6 +394,7 @@ export class IgxTreeNodeComponent extends ToggleAnimationPlayer implements Ig super(); onResourceChangeHandle(this.destroy$, () => { this._defaultResourceStrings = getCurrentResourceStrings(TreeResourceStringsEN, false); + this._customResourceStrings = this._resourceStrings ? Object.assign({}, this._defaultResourceStrings, this._resourceStrings) : null!; }, this); } diff --git a/projects/igniteui-angular/tree/src/tree/tree.spec.ts b/projects/igniteui-angular/tree/src/tree/tree.spec.ts index 651a42406a3..6b616427f38 100644 --- a/projects/igniteui-angular/tree/src/tree/tree.spec.ts +++ b/projects/igniteui-angular/tree/src/tree/tree.spec.ts @@ -4,7 +4,7 @@ import { By } from '@angular/platform-browser'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; -import { AnimationService, IgxAngularAnimationService } from 'igniteui-angular/core'; +import { AnimationService, IgxAngularAnimationService, TreeResourceStringsEN, changei18n } from 'igniteui-angular/core'; import { TreeTestFunctions } from './tree-functions.spec'; import { IgxTreeNavigationService } from './tree-navigation.service'; import { IgxTreeNodeComponent } from './tree-node/tree-node.component'; @@ -719,7 +719,49 @@ describe('IgxTree #treeView', () => { }); }); }); + + describe('IgxTreeNode Resource Strings', () => { + let fix: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [NoopAnimationsModule, IgxTreeSampleComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fix = TestBed.createComponent(IgxTreeSampleComponent); + fix.detectChanges(); + }); + + it('should return full resource strings when partial resourceStrings are set', () => { + const node = fix.componentInstance.tree.nodes.first; + + node.resourceStrings = { igx_expand: 'Open' }; + fix.detectChanges(); + + expect(node.resourceStrings.igx_expand).toBe('Open'); + expect(node.resourceStrings.igx_collapse).toBe('Collapse'); + }); + + it('should update non-overridden resource strings when global i18n changes', () => { + const node = fix.componentInstance.tree.nodes.first; + + node.resourceStrings = { igx_expand: 'Custom Expand' }; + fix.detectChanges(); + + changei18n({ igx_collapse: 'Close' }); + fix.detectChanges(); + + expect(node.resourceStrings.igx_expand).toBe('Custom Expand'); + expect(node.resourceStrings.igx_collapse).toBe('Close'); + + // Restore defaults + changei18n(TreeResourceStringsEN); + }); + }); }); + @Component({ template: ` From 9fed7684f77b03d7df1e0ee7fe15373f62ca68a2 Mon Sep 17 00:00:00 2001 From: skrustev Date: Thu, 27 Aug 2026 10:17:09 +0300 Subject: [PATCH 2/4] chore(): Fix lint --- .../src/action-strip/action-strip.component.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/projects/igniteui-angular/action-strip/src/action-strip/action-strip.component.spec.ts b/projects/igniteui-angular/action-strip/src/action-strip/action-strip.component.spec.ts index 400432b1e5c..3d15dbc213e 100644 --- a/projects/igniteui-angular/action-strip/src/action-strip/action-strip.component.spec.ts +++ b/projects/igniteui-angular/action-strip/src/action-strip/action-strip.component.spec.ts @@ -159,7 +159,7 @@ describe('igxActionStrip', () => { it('should update resource strings when global i18n changes and no custom strings are set', () => { const fix = TestBed.createComponent(IgxActionStripMenuTestingComponent); fix.detectChanges(); - const actionStrip = fix.componentInstance.actionStrip; + actionStrip = fix.componentInstance.actionStrip; changei18n({ igx_action_strip_button_more_title: 'More Options' }); fix.detectChanges(); @@ -173,7 +173,7 @@ describe('igxActionStrip', () => { it('should preserve custom resource strings when global i18n changes', () => { const fix = TestBed.createComponent(IgxActionStripMenuTestingComponent); fix.detectChanges(); - const actionStrip = fix.componentInstance.actionStrip; + actionStrip = fix.componentInstance.actionStrip; actionStrip.resourceStrings = { igx_action_strip_button_more_title: 'Custom More' }; fix.detectChanges(); From 284a9c4888c4f941457484228160373e632970e3 Mon Sep 17 00:00:00 2001 From: skrustev Date: Thu, 27 Aug 2026 10:28:21 +0300 Subject: [PATCH 3/4] tests(i18n): Wrap new tests in try catch to ensure state. --- .../action-strip.component.spec.ts | 28 ++++++++++--------- .../src/banner/banner.component.spec.ts | 28 ++++++++++--------- .../src/carousel/carousel.component.spec.ts | 21 +++++++------- .../chips/src/chips/chip.spec.ts | 17 +++++------ .../combo/src/combo/combo.component.spec.ts | 19 +++++++------ .../date-range-picker.component.spec.ts | 17 +++++------ .../grids/grid/src/grid.component.spec.ts | 15 +++++----- .../input-group/input-group.component.spec.ts | 17 +++++------ .../list/src/list/list.component.spec.ts | 17 +++++------ .../src/paginator/paginator.component.spec.ts | 19 +++++++------ .../query-builder.component.spec.ts | 19 +++++++------ .../tree/src/tree/tree.spec.ts | 15 +++++----- 12 files changed, 123 insertions(+), 109 deletions(-) diff --git a/projects/igniteui-angular/action-strip/src/action-strip/action-strip.component.spec.ts b/projects/igniteui-angular/action-strip/src/action-strip/action-strip.component.spec.ts index 3d15dbc213e..52db4b966c7 100644 --- a/projects/igniteui-angular/action-strip/src/action-strip/action-strip.component.spec.ts +++ b/projects/igniteui-angular/action-strip/src/action-strip/action-strip.component.spec.ts @@ -161,13 +161,14 @@ describe('igxActionStrip', () => { fix.detectChanges(); actionStrip = fix.componentInstance.actionStrip; - changei18n({ igx_action_strip_button_more_title: 'More Options' }); - fix.detectChanges(); - - expect(actionStrip.resourceStrings.igx_action_strip_button_more_title).toBe('More Options'); - - // Restore defaults - changei18n(ActionStripResourceStringsEN); + try { + changei18n({ igx_action_strip_button_more_title: 'More Options' }); + fix.detectChanges(); + + expect(actionStrip.resourceStrings.igx_action_strip_button_more_title).toBe('More Options'); + } finally { + changei18n(ActionStripResourceStringsEN); + } }); it('should preserve custom resource strings when global i18n changes', () => { @@ -178,13 +179,14 @@ describe('igxActionStrip', () => { actionStrip.resourceStrings = { igx_action_strip_button_more_title: 'Custom More' }; fix.detectChanges(); - changei18n({ igx_action_strip_button_more_title: 'Global More' }); - fix.detectChanges(); - - expect(actionStrip.resourceStrings.igx_action_strip_button_more_title).toBe('Custom More'); + try { + changei18n({ igx_action_strip_button_more_title: 'Global More' }); + fix.detectChanges(); - // Restore defaults - changei18n(ActionStripResourceStringsEN); + expect(actionStrip.resourceStrings.igx_action_strip_button_more_title).toBe('Custom More'); + } finally { + changei18n(ActionStripResourceStringsEN); + } }); }); }); diff --git a/projects/igniteui-angular/banner/src/banner/banner.component.spec.ts b/projects/igniteui-angular/banner/src/banner/banner.component.spec.ts index 6792d8f0dad..b441f07ab78 100644 --- a/projects/igniteui-angular/banner/src/banner/banner.component.spec.ts +++ b/projects/igniteui-angular/banner/src/banner/banner.component.spec.ts @@ -543,13 +543,14 @@ describe('igxBanner', () => { fix.detectChanges(); const banner = fix.componentInstance.banner; - changei18n({ igx_banner_button_dismiss: 'Dismiss Global' }); - fix.detectChanges(); - - expect(banner.resourceStrings.igx_banner_button_dismiss).toBe('Dismiss Global'); - - // Restore defaults - changei18n(BannerResourceStringsEN); + try { + changei18n({ igx_banner_button_dismiss: 'Dismiss Global' }); + fix.detectChanges(); + + expect(banner.resourceStrings.igx_banner_button_dismiss).toBe('Dismiss Global'); + } finally { + changei18n(BannerResourceStringsEN); + } }); it('should preserve custom resource strings when global i18n changes', () => { @@ -560,13 +561,14 @@ describe('igxBanner', () => { banner.resourceStrings = { igx_banner_button_dismiss: 'Custom Dismiss' }; fix.detectChanges(); - changei18n({ igx_banner_button_dismiss: 'Global Dismiss' }); - fix.detectChanges(); - - expect(banner.resourceStrings.igx_banner_button_dismiss).toBe('Custom Dismiss'); + try { + changei18n({ igx_banner_button_dismiss: 'Global Dismiss' }); + fix.detectChanges(); - // Restore defaults - changei18n(BannerResourceStringsEN); + expect(banner.resourceStrings.igx_banner_button_dismiss).toBe('Custom Dismiss'); + } finally { + changei18n(BannerResourceStringsEN); + } }); }); diff --git a/projects/igniteui-angular/carousel/src/carousel/carousel.component.spec.ts b/projects/igniteui-angular/carousel/src/carousel/carousel.component.spec.ts index 2888666c339..b44566c05ff 100644 --- a/projects/igniteui-angular/carousel/src/carousel/carousel.component.spec.ts +++ b/projects/igniteui-angular/carousel/src/carousel/carousel.component.spec.ts @@ -1100,16 +1100,17 @@ describe('Carousel', () => { carousel.resourceStrings = { igx_carousel_of: 'custom of' }; fix.detectChanges(); - changei18n({ igx_carousel_slide: 'foto' }); - fix.detectChanges(); - - expect(carousel.resourceStrings.igx_carousel_of).toBe('custom of'); - expect(carousel.resourceStrings.igx_carousel_slide).toBe('foto'); - expect(carousel.resourceStrings.igx_carousel_previous_slide).toBe('previous slide'); - expect(carousel.resourceStrings.igx_carousel_next_slide).toBe('next slide'); - - // Restore defaults - changei18n(CarouselResourceStringsEN); + try { + changei18n({ igx_carousel_slide: 'foto' }); + fix.detectChanges(); + + expect(carousel.resourceStrings.igx_carousel_of).toBe('custom of'); + expect(carousel.resourceStrings.igx_carousel_slide).toBe('foto'); + expect(carousel.resourceStrings.igx_carousel_previous_slide).toBe('previous slide'); + expect(carousel.resourceStrings.igx_carousel_next_slide).toBe('next slide'); + } finally { + changei18n(CarouselResourceStringsEN); + } }); }); }); diff --git a/projects/igniteui-angular/chips/src/chips/chip.spec.ts b/projects/igniteui-angular/chips/src/chips/chip.spec.ts index 9f3531d9cc9..a61cf74f7be 100644 --- a/projects/igniteui-angular/chips/src/chips/chip.spec.ts +++ b/projects/igniteui-angular/chips/src/chips/chip.spec.ts @@ -428,14 +428,15 @@ describe('IgxChip', () => { chip.resourceStrings = { igx_chip_remove: 'Custom Remove' }; fix.detectChanges(); - changei18n({ igx_chip_select: 'Global Select' }); - fix.detectChanges(); - - expect(chip.resourceStrings.igx_chip_remove).toBe('Custom Remove'); - expect(chip.resourceStrings.igx_chip_select).toBe('Global Select'); - - // Restore defaults - changei18n(ChipResourceStringsEN); + try { + changei18n({ igx_chip_select: 'Global Select' }); + fix.detectChanges(); + + expect(chip.resourceStrings.igx_chip_remove).toBe('Custom Remove'); + expect(chip.resourceStrings.igx_chip_select).toBe('Global Select'); + } finally { + changei18n(ChipResourceStringsEN); + } }); }); }); diff --git a/projects/igniteui-angular/combo/src/combo/combo.component.spec.ts b/projects/igniteui-angular/combo/src/combo/combo.component.spec.ts index 38b8f5b3ccb..20ed6074bab 100644 --- a/projects/igniteui-angular/combo/src/combo/combo.component.spec.ts +++ b/projects/igniteui-angular/combo/src/combo/combo.component.spec.ts @@ -3805,15 +3805,16 @@ describe('igxCombo', () => { combo.resourceStrings = { igx_combo_empty_message: 'Custom Empty' }; fix.detectChanges(); - changei18n({ igx_combo_filter_search_placeholder: 'Suchen...' }); - fix.detectChanges(); - - expect(combo.resourceStrings.igx_combo_empty_message).toBe('Custom Empty'); - expect(combo.resourceStrings.igx_combo_filter_search_placeholder).toBe('Suchen...'); - expect(combo.resourceStrings.igx_combo_clearItems_placeholder).toBe('Clear Selection'); - - // Restore defaults - changei18n(ComboResourceStringsEN); + try { + changei18n({ igx_combo_filter_search_placeholder: 'Suchen...' }); + fix.detectChanges(); + + expect(combo.resourceStrings.igx_combo_empty_message).toBe('Custom Empty'); + expect(combo.resourceStrings.igx_combo_filter_search_placeholder).toBe('Suchen...'); + expect(combo.resourceStrings.igx_combo_clearItems_placeholder).toBe('Clear Selection'); + } finally { + changei18n(ComboResourceStringsEN); + } }); }); }); diff --git a/projects/igniteui-angular/date-picker/src/date-range-picker/date-range-picker.component.spec.ts b/projects/igniteui-angular/date-picker/src/date-range-picker/date-range-picker.component.spec.ts index 55dffffc889..97135855aa3 100644 --- a/projects/igniteui-angular/date-picker/src/date-range-picker/date-range-picker.component.spec.ts +++ b/projects/igniteui-angular/date-picker/src/date-range-picker/date-range-picker.component.spec.ts @@ -2301,15 +2301,16 @@ describe('IgxDateRangePicker', () => { drp.resourceStrings = { igx_date_range_picker_done_button: 'Fertig' }; fix.detectChanges(); - changei18n({ igx_date_range_picker_cancel_button: 'Abbrechen' }); - fix.detectChanges(); - - expect(drp.resourceStrings.igx_date_range_picker_done_button).toBe('Fertig'); - expect(drp.resourceStrings.igx_date_range_picker_cancel_button).toBe('Abbrechen'); - expect(drp.resourceStrings.igx_date_range_picker_date_separator).toBe(' - '); + try { + changei18n({ igx_date_range_picker_cancel_button: 'Abbrechen' }); + fix.detectChanges(); - // Restore defaults - changei18n(DateRangePickerResourceStringsEN); + expect(drp.resourceStrings.igx_date_range_picker_done_button).toBe('Fertig'); + expect(drp.resourceStrings.igx_date_range_picker_cancel_button).toBe('Abbrechen'); + expect(drp.resourceStrings.igx_date_range_picker_date_separator).toBe(' - '); + } finally { + changei18n(DateRangePickerResourceStringsEN); + } }); }); }); diff --git a/projects/igniteui-angular/grids/grid/src/grid.component.spec.ts b/projects/igniteui-angular/grids/grid/src/grid.component.spec.ts index 266e00a60c9..59f5d0bd35f 100644 --- a/projects/igniteui-angular/grids/grid/src/grid.component.spec.ts +++ b/projects/igniteui-angular/grids/grid/src/grid.component.spec.ts @@ -3466,14 +3466,15 @@ describe('IgxGrid Component Tests #grid', () => { grid.resourceStrings = { igx_grid_emptyFilteredGrid_message: 'Custom Empty' }; fix.detectChanges(); - changei18n({ igx_grid_groupByArea_message: 'Hier ablegen' }); - fix.detectChanges(); - - expect(grid.resourceStrings.igx_grid_emptyFilteredGrid_message).toBe('Custom Empty'); - expect(grid.resourceStrings.igx_grid_groupByArea_message).toBe('Hier ablegen'); + try { + changei18n({ igx_grid_groupByArea_message: 'Hier ablegen' }); + fix.detectChanges(); - // Restore defaults - changei18n(GridResourceStringsEN); + expect(grid.resourceStrings.igx_grid_emptyFilteredGrid_message).toBe('Custom Empty'); + expect(grid.resourceStrings.igx_grid_groupByArea_message).toBe('Hier ablegen'); + } finally { + changei18n(GridResourceStringsEN); + } }); }); }); diff --git a/projects/igniteui-angular/input-group/src/input-group/input-group.component.spec.ts b/projects/igniteui-angular/input-group/src/input-group/input-group.component.spec.ts index 65bf00881f4..f6c8aff22be 100644 --- a/projects/igniteui-angular/input-group/src/input-group/input-group.component.spec.ts +++ b/projects/igniteui-angular/input-group/src/input-group/input-group.component.spec.ts @@ -262,14 +262,15 @@ describe('IgxInputGroup', () => { inputGroup.resourceStrings = { igx_input_upload_button: 'Custom Browse' }; fix.detectChanges(); - changei18n({ igx_input_file_placeholder: 'Keine Datei ausgewählt' }); - fix.detectChanges(); - - expect(inputGroup.resourceStrings.igx_input_upload_button).toBe('Custom Browse'); - expect(inputGroup.resourceStrings.igx_input_file_placeholder).toBe('Keine Datei ausgewählt'); - - // Restore defaults - changei18n(InputResourceStringsEN); + try { + changei18n({ igx_input_file_placeholder: 'Keine Datei ausgewählt' }); + fix.detectChanges(); + + expect(inputGroup.resourceStrings.igx_input_upload_button).toBe('Custom Browse'); + expect(inputGroup.resourceStrings.igx_input_file_placeholder).toBe('Keine Datei ausgewählt'); + } finally { + changei18n(InputResourceStringsEN); + } }); }); }); diff --git a/projects/igniteui-angular/list/src/list/list.component.spec.ts b/projects/igniteui-angular/list/src/list/list.component.spec.ts index a895b7280b8..eeb5fc2f610 100644 --- a/projects/igniteui-angular/list/src/list/list.component.spec.ts +++ b/projects/igniteui-angular/list/src/list/list.component.spec.ts @@ -838,14 +838,15 @@ describe('List', () => { list.resourceStrings = { igx_list_no_items: 'Custom No Items' }; fix.detectChanges(); - changei18n({ igx_list_loading: 'Fetching data...' }); - fix.detectChanges(); - - expect(list.resourceStrings.igx_list_no_items).toBe('Custom No Items'); - expect(list.resourceStrings.igx_list_loading).toBe('Fetching data...'); - - // Restore defaults - changei18n(ListResourceStringsEN); + try { + changei18n({ igx_list_loading: 'Fetching data...' }); + fix.detectChanges(); + + expect(list.resourceStrings.igx_list_no_items).toBe('Custom No Items'); + expect(list.resourceStrings.igx_list_loading).toBe('Fetching data...'); + } finally { + changei18n(ListResourceStringsEN); + } }); /* factorX - the coefficient used to calculate deltaX. diff --git a/projects/igniteui-angular/paginator/src/paginator/paginator.component.spec.ts b/projects/igniteui-angular/paginator/src/paginator/paginator.component.spec.ts index 2b291788bb5..156217363c2 100644 --- a/projects/igniteui-angular/paginator/src/paginator/paginator.component.spec.ts +++ b/projects/igniteui-angular/paginator/src/paginator/paginator.component.spec.ts @@ -289,15 +289,16 @@ describe('IgxPaginator with default settings', () => { paginator.resourceStrings = { igx_paginator_label: 'Custom per page' }; fix.detectChanges(); - changei18n({ igx_paginator_pager_text: 'von' }); - fix.detectChanges(); - - expect(paginator.resourceStrings.igx_paginator_label).toBe('Custom per page'); - expect(paginator.resourceStrings.igx_paginator_pager_text).toBe('von'); - expect(paginator.resourceStrings.igx_paginator_first_page_button_text).toBe('Go to first page'); - - // Restore defaults - changei18n(PaginatorResourceStringsEN); + try { + changei18n({ igx_paginator_pager_text: 'von' }); + fix.detectChanges(); + + expect(paginator.resourceStrings.igx_paginator_label).toBe('Custom per page'); + expect(paginator.resourceStrings.igx_paginator_pager_text).toBe('von'); + expect(paginator.resourceStrings.igx_paginator_first_page_button_text).toBe('Go to first page'); + } finally { + changei18n(PaginatorResourceStringsEN); + } }); }); diff --git a/projects/igniteui-angular/query-builder/src/query-builder/query-builder.component.spec.ts b/projects/igniteui-angular/query-builder/src/query-builder/query-builder.component.spec.ts index 9c35b89221f..30d9dd35383 100644 --- a/projects/igniteui-angular/query-builder/src/query-builder/query-builder.component.spec.ts +++ b/projects/igniteui-angular/query-builder/src/query-builder/query-builder.component.spec.ts @@ -3247,15 +3247,16 @@ describe('IgxQueryBuilder', () => { queryBuilder.resourceStrings = { igx_query_builder_date_placeholder: 'Custom date' }; fix.detectChanges(); - changei18n({ igx_query_builder_filter_operator_and: 'Und' }); - fix.detectChanges(); - - expect(queryBuilder.resourceStrings.igx_query_builder_date_placeholder).toBe('Custom date'); - expect(queryBuilder.resourceStrings.igx_query_builder_filter_operator_and).toBe('Und'); - expect(queryBuilder.resourceStrings.igx_query_builder_add_condition).toBe('Add condition'); - - // Restore defaults - changei18n(QueryBuilderResourceStringsEN); + try { + changei18n({ igx_query_builder_filter_operator_and: 'Und' }); + fix.detectChanges(); + + expect(queryBuilder.resourceStrings.igx_query_builder_date_placeholder).toBe('Custom date'); + expect(queryBuilder.resourceStrings.igx_query_builder_filter_operator_and).toBe('Und'); + expect(queryBuilder.resourceStrings.igx_query_builder_add_condition).toBe('Add condition'); + } finally { + changei18n(QueryBuilderResourceStringsEN); + } }); }); }); diff --git a/projects/igniteui-angular/tree/src/tree/tree.spec.ts b/projects/igniteui-angular/tree/src/tree/tree.spec.ts index 6b616427f38..f520928cbce 100644 --- a/projects/igniteui-angular/tree/src/tree/tree.spec.ts +++ b/projects/igniteui-angular/tree/src/tree/tree.spec.ts @@ -750,14 +750,15 @@ describe('IgxTree #treeView', () => { node.resourceStrings = { igx_expand: 'Custom Expand' }; fix.detectChanges(); - changei18n({ igx_collapse: 'Close' }); - fix.detectChanges(); - - expect(node.resourceStrings.igx_expand).toBe('Custom Expand'); - expect(node.resourceStrings.igx_collapse).toBe('Close'); + try { + changei18n({ igx_collapse: 'Close' }); + fix.detectChanges(); - // Restore defaults - changei18n(TreeResourceStringsEN); + expect(node.resourceStrings.igx_expand).toBe('Custom Expand'); + expect(node.resourceStrings.igx_collapse).toBe('Close'); + } finally { + changei18n(TreeResourceStringsEN); + } }); }); }); From 9b975c1d5f13b7fb2583f5d1d3ef91c32801b9ab Mon Sep 17 00:00:00 2001 From: skrustev Date: Thu, 27 Aug 2026 10:53:41 +0300 Subject: [PATCH 4/4] tests(*): Update wrong condition for test. --- .../src/date-range-picker/date-range-picker.component.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/projects/igniteui-angular/date-picker/src/date-range-picker/date-range-picker.component.spec.ts b/projects/igniteui-angular/date-picker/src/date-range-picker/date-range-picker.component.spec.ts index 97135855aa3..3efc986a445 100644 --- a/projects/igniteui-angular/date-picker/src/date-range-picker/date-range-picker.component.spec.ts +++ b/projects/igniteui-angular/date-picker/src/date-range-picker/date-range-picker.component.spec.ts @@ -2292,7 +2292,7 @@ describe('IgxDateRangePicker', () => { expect(drp.resourceStrings.igx_date_range_picker_done_button).toBe('OK'); expect(drp.resourceStrings.igx_date_range_picker_cancel_button).toBe('Cancel'); - expect(drp.resourceStrings.igx_date_range_picker_date_separator).toBe(' - '); + expect(drp.resourceStrings.igx_date_range_picker_date_separator).toBe('to'); }); it('should update non-overridden resource strings when global i18n changes', () => { @@ -2307,7 +2307,7 @@ describe('IgxDateRangePicker', () => { expect(drp.resourceStrings.igx_date_range_picker_done_button).toBe('Fertig'); expect(drp.resourceStrings.igx_date_range_picker_cancel_button).toBe('Abbrechen'); - expect(drp.resourceStrings.igx_date_range_picker_date_separator).toBe(' - '); + expect(drp.resourceStrings.igx_date_range_picker_date_separator).toBe('to'); } finally { changei18n(DateRangePickerResourceStringsEN); }