feat: add persistance to language selection

This commit is contained in:
2025-12-23 23:48:35 -03:00
parent 06f1769f5c
commit 882b0e2132
11 changed files with 74 additions and 26 deletions

View File

@@ -1,19 +1,29 @@
import { ApplicationConfig, InjectionToken, provideBrowserGlobalErrorListeners, provideZoneChangeDetection } from '@angular/core'; import {
ApplicationConfig,
InjectionToken,
provideBrowserGlobalErrorListeners,
provideZoneChangeDetection,
} from '@angular/core';
import { provideRouter } from '@angular/router'; import { provideRouter } from '@angular/router';
import { routes } from './app.routes'; import { routes } from './app.routes';
import { strings } from './strings'; import { strings } from './strings';
import { provideHttpClient } from '@angular/common/http'; import { provideHttpClient } from '@angular/common/http';
import { Language } from './types/Language.type';
const STRINGS_TOKEN = 'strings'; const STRINGS_TOKEN = 'strings';
export const STRINGS_INJECTOR = new InjectionToken<typeof strings>(STRINGS_TOKEN); export const STRINGS_INJECTOR = new InjectionToken<typeof strings>(STRINGS_TOKEN);
const LANGUAGE = 'LS_LANGUAGE';
export const LS_LANGUAGE = new InjectionToken<Language>(LANGUAGE);
export const appConfig: ApplicationConfig = { export const appConfig: ApplicationConfig = {
providers: [ providers: [
provideBrowserGlobalErrorListeners(), provideBrowserGlobalErrorListeners(),
provideZoneChangeDetection({ eventCoalescing: true }), provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes), provideRouter(routes),
provideHttpClient(), provideHttpClient(),
{provide: STRINGS_INJECTOR, useValue: strings} { provide: STRINGS_INJECTOR, useValue: strings },
] { provide: LS_LANGUAGE, useValue: 'language' },
],
}; };

View File

@@ -1,22 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ContactForm } from './contact-form'; import { ContactForm } from './contact-form';
import { STRINGS_INJECTOR } from '../../app.config';
import { strings } from '../../strings'; import { strings } from '../../strings';
import { By } from '@angular/platform-browser'; import { By } from '@angular/platform-browser';
import { ContactDTO } from '../../models/ContactDTO'; import { ContactDTO } from '../../models/ContactDTO';
import { LanguageManager } from '../../services/language-manager';
describe('ContactForm', () => { describe('ContactForm', () => {
let component: ContactForm; let component: ContactForm;
let fixture: ComponentFixture<ContactForm>; let fixture: ComponentFixture<ContactForm>;
let languageManager: jasmine.SpyObj<LanguageManager>;
beforeEach(async () => { beforeEach(async () => {
languageManager = jasmine.createSpyObj(LanguageManager.name, [], { strings: strings.en });
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
imports: [ContactForm], imports: [ContactForm],
providers: [{ provide: STRINGS_INJECTOR, useValue: strings }], providers: [{ provide: LanguageManager, useValue: languageManager }],
}).compileComponents(); }).compileComponents();
TestBed.inject(STRINGS_INJECTOR);
fixture = TestBed.createComponent(ContactForm); fixture = TestBed.createComponent(ContactForm);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();

View File

@@ -1,30 +1,33 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ContactListTable } from './contact-list-table'; import { ContactListTable } from './contact-list-table';
import { STRINGS_INJECTOR } from '../../app.config';
import { strings } from '../../strings'; import { strings } from '../../strings';
import { ContactService } from '../../services/contact.service'; import { ContactService } from '../../services/contact.service';
import { ContactDTO } from '../../models/ContactDTO'; import { ContactDTO } from '../../models/ContactDTO';
import { By } from '@angular/platform-browser'; import { By } from '@angular/platform-browser';
import { of } from 'rxjs'; import { of } from 'rxjs';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { LanguageManager } from '../../services/language-manager';
describe('ContactListTable', () => { describe('ContactListTable', () => {
let component: ContactListTable; let component: ContactListTable;
let fixture: ComponentFixture<ContactListTable>; let fixture: ComponentFixture<ContactListTable>;
let contactService: jasmine.SpyObj<ContactService>; let contactService: jasmine.SpyObj<ContactService>;
let languageManager: jasmine.SpyObj<LanguageManager>;
let router: jasmine.SpyObj<Router>; let router: jasmine.SpyObj<Router>;
let CONTACT_LIST_MOCK: ContactDTO[]; let CONTACT_LIST_MOCK: ContactDTO[];
beforeEach(async () => { beforeEach(async () => {
contactService = jasmine.createSpyObj(ContactService.name, ['delete']); contactService = jasmine.createSpyObj(ContactService.name, ['delete']);
languageManager = jasmine.createSpyObj(LanguageManager.name, [], { strings: strings.en });
router = jasmine.createSpyObj(Router.name, ['navigate']); router = jasmine.createSpyObj(Router.name, ['navigate']);
CONTACT_LIST_MOCK = [new ContactDTO(1, 'MOCK', 'MOCK', '5491122222222')]; CONTACT_LIST_MOCK = [new ContactDTO(1, 'MOCK', 'MOCK', '5491122222222')];
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
imports: [ContactListTable], imports: [ContactListTable],
providers: [ providers: [
{ provide: STRINGS_INJECTOR, useValue: strings },
{ provide: ContactService, useValue: contactService }, { provide: ContactService, useValue: contactService },
{ provide: LanguageManager, useValue: languageManager },
{ provide: Router, useValue: router }, { provide: Router, useValue: router },
], ],
}).compileComponents(); }).compileComponents();

View File

@@ -3,21 +3,24 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ContactList } from './contact-list'; import { ContactList } from './contact-list';
import { ContactService } from '../../services/contact.service'; import { ContactService } from '../../services/contact.service';
import { of } from 'rxjs'; import { of } from 'rxjs';
import { STRINGS_INJECTOR } from '../../app.config';
import { strings } from '../../strings'; import { strings } from '../../strings';
import { LanguageManager } from '../../services/language-manager';
describe('ContactList', () => { describe('ContactList', () => {
let component: ContactList; let component: ContactList;
let fixture: ComponentFixture<ContactList>; let fixture: ComponentFixture<ContactList>;
let contactService: jasmine.SpyObj<ContactService>; let contactService: jasmine.SpyObj<ContactService>;
let languageManager: jasmine.SpyObj<LanguageManager>;
beforeEach(async () => { beforeEach(async () => {
contactService = jasmine.createSpyObj(ContactService.name, ['getAll']); contactService = jasmine.createSpyObj(ContactService.name, ['getAll']);
languageManager = jasmine.createSpyObj(LanguageManager.name, [], { strings: strings.en });
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
imports: [ContactList], imports: [ContactList],
providers: [ providers: [
{ provide: ContactService, useValue: contactService }, { provide: ContactService, useValue: contactService },
{ provide: STRINGS_INJECTOR, useValue: strings }, { provide: LanguageManager, useValue: languageManager },
], ],
}).compileComponents(); }).compileComponents();
contactService.getAll.and.returnValue(of([])); contactService.getAll.and.returnValue(of([]));

View File

@@ -1,19 +1,22 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ContactSearchBar } from './contact-search-bar'; import { ContactSearchBar } from './contact-search-bar';
import { STRINGS_INJECTOR } from '../../app.config';
import { strings } from '../../strings'; import { strings } from '../../strings';
import { LanguageManager } from '../../services/language-manager';
describe('ContactSearchBar', () => { describe('ContactSearchBar', () => {
let component: ContactSearchBar; let component: ContactSearchBar;
let fixture: ComponentFixture<ContactSearchBar>; let fixture: ComponentFixture<ContactSearchBar>;
let languageManager: jasmine.SpyObj<LanguageManager>;
beforeEach(async () => { beforeEach(async () => {
languageManager = jasmine.createSpyObj(LanguageManager.name, [], { strings: strings.en });
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
imports: [ContactSearchBar], imports: [ContactSearchBar],
providers: [{provide: STRINGS_INJECTOR, useValue: strings}] providers: [{ provide: LanguageManager, useValue: languageManager }],
}) }).compileComponents();
.compileComponents();
fixture = TestBed.createComponent(ContactSearchBar); fixture = TestBed.createComponent(ContactSearchBar);
component = fixture.componentInstance; component = fixture.componentInstance;

View File

@@ -6,11 +6,12 @@ import { ContactDTO } from '../../models/ContactDTO';
import { provideHttpClient } from '@angular/common/http'; import { provideHttpClient } from '@angular/common/http';
import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing'; import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';
import { environment } from '../../../environments/environment'; import { environment } from '../../../environments/environment';
import { STRINGS_INJECTOR } from '../../app.config';
import { strings } from '../../strings'; import { strings } from '../../strings';
import { LanguageManager } from '../../services/language-manager';
describe('contactResolver', () => { describe('contactResolver', () => {
let httpTestingController: HttpTestingController; let httpTestingController: HttpTestingController;
let languageManager: jasmine.SpyObj<LanguageManager>;
let router: jasmine.SpyObj<Router>; let router: jasmine.SpyObj<Router>;
let ROUTE_MOCK: jasmine.SpyObj<ActivatedRouteSnapshot>; let ROUTE_MOCK: jasmine.SpyObj<ActivatedRouteSnapshot>;
@@ -19,6 +20,7 @@ describe('contactResolver', () => {
TestBed.runInInjectionContext(() => contactResolver(...resolverParameters)); TestBed.runInInjectionContext(() => contactResolver(...resolverParameters));
beforeEach(() => { beforeEach(() => {
languageManager = jasmine.createSpyObj(LanguageManager.name, [], { strings: strings.en });
router = jasmine.createSpyObj(Router.name, ['navigate']); router = jasmine.createSpyObj(Router.name, ['navigate']);
ROUTE_MOCK = jasmine.createSpyObj(ActivatedRouteSnapshot.name, [], { ROUTE_MOCK = jasmine.createSpyObj(ActivatedRouteSnapshot.name, [], {
paramMap: { paramMap: {
@@ -32,8 +34,8 @@ describe('contactResolver', () => {
providers: [ providers: [
provideHttpClient(), provideHttpClient(),
provideHttpClientTesting(), provideHttpClientTesting(),
{ provide: LanguageManager, useValue: languageManager },
{ provide: Router, useValue: router }, { provide: Router, useValue: router },
{ provide: STRINGS_INJECTOR, useValue: strings },
], ],
}); });
httpTestingController = TestBed.inject(HttpTestingController); httpTestingController = TestBed.inject(HttpTestingController);

View File

@@ -1,13 +1,13 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { Edit } from './edit'; import { Edit } from './edit';
import { STRINGS_INJECTOR } from '../../app.config';
import { strings } from '../../strings'; import { strings } from '../../strings';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { ContactService } from '../../services/contact.service'; import { ContactService } from '../../services/contact.service';
import { of } from 'rxjs'; import { of } from 'rxjs';
import { ContactDTO } from '../../models/ContactDTO'; import { ContactDTO } from '../../models/ContactDTO';
import { By } from '@angular/platform-browser'; import { By } from '@angular/platform-browser';
import { LanguageManager } from '../../services/language-manager';
describe('Edit', () => { describe('Edit', () => {
let component: Edit; let component: Edit;
@@ -15,6 +15,7 @@ describe('Edit', () => {
let activatedRoute: jasmine.SpyObj<ActivatedRoute>; let activatedRoute: jasmine.SpyObj<ActivatedRoute>;
let contactService: jasmine.SpyObj<ContactService>; let contactService: jasmine.SpyObj<ContactService>;
let languageManager: jasmine.SpyObj<LanguageManager>;
let router: jasmine.SpyObj<Router>; let router: jasmine.SpyObj<Router>;
let CONTACT_MOCK: ContactDTO; let CONTACT_MOCK: ContactDTO;
@@ -25,6 +26,7 @@ describe('Edit', () => {
data: of({ contact: CONTACT_MOCK }), data: of({ contact: CONTACT_MOCK }),
}); });
contactService = jasmine.createSpyObj(ContactService.name, ['update']); contactService = jasmine.createSpyObj(ContactService.name, ['update']);
languageManager = jasmine.createSpyObj(LanguageManager.name, [], { strings: strings.en });
router = jasmine.createSpyObj(Router.name, ['navigate']); router = jasmine.createSpyObj(Router.name, ['navigate']);
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
@@ -32,8 +34,8 @@ describe('Edit', () => {
providers: [ providers: [
{ provide: ActivatedRoute, useValue: activatedRoute }, { provide: ActivatedRoute, useValue: activatedRoute },
{ provide: ContactService, useValue: contactService }, { provide: ContactService, useValue: contactService },
{ provide: LanguageManager, useValue: languageManager },
{ provide: Router, useValue: router }, { provide: Router, useValue: router },
{ provide: STRINGS_INJECTOR, useValue: strings },
], ],
}).compileComponents(); }).compileComponents();

View File

@@ -1,7 +1,6 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { Main } from './main'; import { Main } from './main';
import { STRINGS_INJECTOR } from '../../app.config';
import { strings } from '../../strings'; import { strings } from '../../strings';
import { ContactService } from '../../services/contact.service'; import { ContactService } from '../../services/contact.service';
import { ContactList } from '../../components/contact-list/contact-list'; import { ContactList } from '../../components/contact-list/contact-list';
@@ -9,22 +8,28 @@ import { of } from 'rxjs';
import { ContactDTO } from '../../models/ContactDTO'; import { ContactDTO } from '../../models/ContactDTO';
import { By } from '@angular/platform-browser'; import { By } from '@angular/platform-browser';
import { ContactForm } from '../../components/contact-form/contact-form'; import { ContactForm } from '../../components/contact-form/contact-form';
import { LanguageManager } from '../../services/language-manager';
describe('Main', () => { describe('Main', () => {
let component: Main; let component: Main;
let fixture: ComponentFixture<Main>; let fixture: ComponentFixture<Main>;
let contactService: jasmine.SpyObj<ContactService>; let contactService: jasmine.SpyObj<ContactService>;
let languageManager: jasmine.SpyObj<LanguageManager>;
beforeEach(async () => { beforeEach(async () => {
contactService = jasmine.createSpyObj(ContactList.name, ['getAll', 'save']); contactService = jasmine.createSpyObj(ContactList.name, ['getAll', 'save']);
contactService.getAll.and.returnValue(of([])); contactService.getAll.and.returnValue(of([]));
languageManager = jasmine.createSpyObj(LanguageManager.name, ['selectedLanguage$'], {
strings: strings.en,
});
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
imports: [Main], imports: [Main],
providers: [ providers: [
{ provide: STRINGS_INJECTOR, useValue: strings },
{ provide: ContactService, useValue: contactService }, { provide: ContactService, useValue: contactService },
{ provide: LanguageManager, useValue: languageManager },
], ],
}).compileComponents(); }).compileComponents();
languageManager.selectedLanguage$.and.returnValue('en');
fixture = TestBed.createComponent(Main); fixture = TestBed.createComponent(Main);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();

View File

@@ -5,23 +5,25 @@ import { provideHttpClient } from '@angular/common/http';
import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing'; import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';
import { environment } from '../../environments/environment'; import { environment } from '../../environments/environment';
import { ContactDTO } from '../models/ContactDTO'; import { ContactDTO } from '../models/ContactDTO';
import { STRINGS_INJECTOR } from '../app.config';
import { strings } from '../strings'; import { strings } from '../strings';
import { LanguageManager } from './language-manager';
describe('ContactService', () => { describe('ContactService', () => {
let service: ContactService; let service: ContactService;
let httpTestingController: HttpTestingController; let httpTestingController: HttpTestingController;
let languageManager: jasmine.SpyObj<LanguageManager>;
let RES_MOCK: Response<any>; let RES_MOCK: Response<any>;
let endpointURL = environment.apiUrl + '/contacts'; let endpointURL = environment.apiUrl + '/contacts';
beforeEach(() => { beforeEach(() => {
languageManager = jasmine.createSpyObj(LanguageManager.name, [], { strings: strings.en });
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [], imports: [],
providers: [ providers: [
provideHttpClient(), provideHttpClient(),
provideHttpClientTesting(), provideHttpClientTesting(),
{ provide: STRINGS_INJECTOR, useValue: strings }, { provide: LanguageManager, useValue: languageManager },
], ],
}); });
service = TestBed.inject(ContactService); service = TestBed.inject(ContactService);

View File

@@ -2,24 +2,33 @@ import { TestBed } from '@angular/core/testing';
import { LanguageManager } from './language-manager'; import { LanguageManager } from './language-manager';
import { strings } from '../strings'; import { strings } from '../strings';
import { STRINGS_INJECTOR } from '../app.config'; import { LS_LANGUAGE, STRINGS_INJECTOR } from '../app.config';
import { Language } from '../types/Language.type'; import { Language } from '../types/Language.type';
import { Localstorage } from './localstorage';
describe('LanguageManager', () => { describe('LanguageManager', () => {
let service: LanguageManager; let service: LanguageManager;
let localStorage: jasmine.SpyObj<Localstorage>;
beforeEach(() => { beforeEach(() => {
localStorage = jasmine.createSpyObj(Localstorage.name, ['getItem', 'setItem']);
TestBed.configureTestingModule({ TestBed.configureTestingModule({
providers: [{ provide: STRINGS_INJECTOR, useValue: strings }], providers: [
{ provide: STRINGS_INJECTOR, useValue: strings },
{ provide: LS_LANGUAGE, useValue: 'language' },
{ provide: Localstorage, useValue: localStorage },
],
}); });
service = TestBed.inject(LanguageManager); service = TestBed.inject(LanguageManager);
}); });
it('should be created', () => { it('should be created', () => {
localStorage.getItem.and.returnValue(null);
expect(service).toBeTruthy(); expect(service).toBeTruthy();
}); });
it('should switch language', () => { it('should switch language', () => {
localStorage.getItem.and.returnValue(null);
const SELECTED_LANGUAGE_MOCK: Language = 'es'; const SELECTED_LANGUAGE_MOCK: Language = 'es';
service.setLanguage(SELECTED_LANGUAGE_MOCK); service.setLanguage(SELECTED_LANGUAGE_MOCK);
expect(service.strings).toEqual(strings[SELECTED_LANGUAGE_MOCK]); expect(service.strings).toEqual(strings[SELECTED_LANGUAGE_MOCK]);

View File

@@ -1,19 +1,26 @@
import { inject, Injectable, signal } from '@angular/core'; import { inject, Injectable, signal } from '@angular/core';
import { STRINGS_INJECTOR } from '../app.config'; import { LS_LANGUAGE, STRINGS_INJECTOR } from '../app.config';
import { Language } from '../types/Language.type'; import { Language } from '../types/Language.type';
import { Localstorage } from './localstorage';
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',
}) })
export class LanguageManager { export class LanguageManager {
private readonly stringsDictionary = inject(STRINGS_INJECTOR); private readonly stringsDictionary = inject(STRINGS_INJECTOR);
private readonly localStorage = inject(Localstorage);
private readonly lS_LANGUAGE = inject(LS_LANGUAGE);
private readonly selectedLanguage = signal<Language>('en'); private readonly selectedLanguage = signal<Language>('en');
readonly selectedLanguage$ = this.selectedLanguage.asReadonly(); readonly selectedLanguage$ = this.selectedLanguage.asReadonly();
strings = this.stringsDictionary.en; strings = this.stringsDictionary.en;
constructor() {
this.setLanguage(this.localStorage.getItem<Language>(this.lS_LANGUAGE) ?? 'en');
}
setLanguage(language: Language) { setLanguage(language: Language) {
this.selectedLanguage.set(language); this.selectedLanguage.set(language);
this.localStorage.setItem(this.lS_LANGUAGE, language);
this.strings = this.stringsDictionary[language]; this.strings = this.stringsDictionary[language];
} }
} }