Skip to content

Commit 7da01d8

Browse files
author
abhinav
committed
fix 4241 language selection
Consider the language set in the users profile when setting it on page load. Also match languages case-insensitive. Updated tests
1 parent 404ccd9 commit 7da01d8

File tree

4 files changed

+105
-38
lines changed

4 files changed

+105
-38
lines changed

src/app/core/locale/locale.interceptor.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ describe(`LocaleInterceptor`, () => {
3838
httpMock = TestBed.inject(HttpTestingController);
3939
localeService = TestBed.inject(LocaleService);
4040

41-
localeService.getCurrentLanguageCode.and.returnValue('en');
41+
localeService.getCurrentLanguageCode.and.returnValue(of('en'));
4242
});
4343

4444
describe('', () => {

src/app/core/locale/locale.interceptor.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/c
44
import { Observable } from 'rxjs';
55

66
import { LocaleService } from './locale.service';
7-
import { mergeMap, scan } from 'rxjs/operators';
7+
import { mergeMap, scan, take } from 'rxjs/operators';
88

99
@Injectable()
1010
export class LocaleInterceptor implements HttpInterceptor {
@@ -21,6 +21,7 @@ export class LocaleInterceptor implements HttpInterceptor {
2121
let newReq: HttpRequest<any>;
2222
return this.localeService.getLanguageCodeList()
2323
.pipe(
24+
take(1),
2425
scan((acc: any, value: any) => [...acc, value], []),
2526
mergeMap((languages) => {
2627
// Clone the request to add the new header.

src/app/core/locale/locale.service.spec.ts

Lines changed: 62 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ import { AuthService } from '../auth/auth.service';
1010
import { NativeWindowRef } from '../services/window.service';
1111
import { RouteService } from '../services/route.service';
1212
import { routeServiceStub } from '../../shared/testing/route-service.stub';
13+
import { of } from 'rxjs';
14+
import { TestScheduler } from 'rxjs/testing';
15+
import { EPersonMock2 } from '../../shared/testing/eperson.mock';
1316

1417
describe('LocaleService test suite', () => {
1518
let service: LocaleService;
@@ -25,7 +28,8 @@ describe('LocaleService test suite', () => {
2528

2629
authService = jasmine.createSpyObj('AuthService', {
2730
isAuthenticated: jasmine.createSpy('isAuthenticated'),
28-
isAuthenticationLoaded: jasmine.createSpy('isAuthenticationLoaded')
31+
isAuthenticationLoaded: jasmine.createSpy('isAuthenticationLoaded'),
32+
getAuthenticatedUserFromStore: jasmine.createSpy('getAuthenticatedUserFromStore'),
2933
});
3034

3135
const langList = ['en', 'xx', 'de'];
@@ -62,33 +66,80 @@ describe('LocaleService test suite', () => {
6266
});
6367

6468
describe('getCurrentLanguageCode', () => {
69+
let testScheduler: TestScheduler;
70+
6571
beforeEach(() => {
6672
spyOn(translateService, 'getLangs').and.returnValue(langList);
73+
testScheduler = new TestScheduler((actual, expected) => {
74+
// use jasmine to test equality
75+
expect(actual).toEqual(expected);
76+
});
77+
authService.isAuthenticated.and.returnValue(of(false));
78+
authService.isAuthenticationLoaded.and.returnValue(of(false));
6779
});
6880

6981
it('should return the language saved on cookie if it\'s a valid & active language', () => {
7082
spyOnGet.and.returnValue('de');
71-
expect(service.getCurrentLanguageCode()).toBe('de');
83+
testScheduler.run(({expectObservable}) => {
84+
expectObservable(service.getCurrentLanguageCode()).toBe('(a|)', { a: 'de' });
85+
});
7286
});
7387

7488
it('should return the default language if the cookie language is disabled', () => {
7589
spyOnGet.and.returnValue('disabled');
76-
expect(service.getCurrentLanguageCode()).toBe('en');
90+
testScheduler.run(({expectObservable}) => {
91+
expectObservable(service.getCurrentLanguageCode()).toBe('(a|)', { a: 'en' });
92+
});
7793
});
7894

7995
it('should return the default language if the cookie language does not exist', () => {
8096
spyOnGet.and.returnValue('does-not-exist');
81-
expect(service.getCurrentLanguageCode()).toBe('en');
97+
testScheduler.run(({expectObservable}) => {
98+
expectObservable(service.getCurrentLanguageCode()).toBe('(a|)', { a: 'en' });
99+
});
82100
});
83101

84102
it('should return language from browser setting', () => {
85-
spyOn(translateService, 'getBrowserLang').and.returnValue('xx');
86-
expect(service.getCurrentLanguageCode()).toBe('xx');
103+
spyOn(service, 'getLanguageCodeList').and.returnValue(of(['xx', 'en']));
104+
testScheduler.run(({expectObservable}) => {
105+
expectObservable(service.getCurrentLanguageCode()).toBe('(a|)', { a: 'xx' });
106+
});
107+
});
108+
109+
it('should match language from browser setting case insensitive', () => {
110+
spyOn(service, 'getLanguageCodeList').and.returnValue(of(['DE', 'en']));
111+
testScheduler.run(({expectObservable}) => {
112+
expectObservable(service.getCurrentLanguageCode()).toBe('(a|)', { a: 'DE' });
113+
});
114+
});
115+
});
116+
117+
describe('getLanguageCodeList', () => {
118+
let testScheduler: TestScheduler;
119+
120+
beforeEach(() => {
121+
spyOn(translateService, 'getLangs').and.returnValue(langList);
122+
testScheduler = new TestScheduler((actual, expected) => {
123+
// use jasmine to test equality
124+
expect(actual).toEqual(expected);
125+
});
126+
});
127+
128+
it('should return default language list without user preferred language when no logged in user', () => {
129+
authService.isAuthenticated.and.returnValue(of(false));
130+
authService.isAuthenticationLoaded.and.returnValue(of(false));
131+
testScheduler.run(({expectObservable}) => {
132+
expectObservable(service.getLanguageCodeList()).toBe('(a|)', { a: ['en-US;q=1', 'en;q=0.9'] });
133+
});
87134
});
88135

89-
it('should return default language from config', () => {
90-
spyOn(translateService, 'getBrowserLang').and.returnValue('fr');
91-
expect(service.getCurrentLanguageCode()).toBe('en');
136+
it('should return default language list with user preferred language when user is logged in', () => {
137+
authService.isAuthenticated.and.returnValue(of(true));
138+
authService.isAuthenticationLoaded.and.returnValue(of(true));
139+
authService.getAuthenticatedUserFromStore.and.returnValue(of(EPersonMock2));
140+
testScheduler.run(({expectObservable}) => {
141+
expectObservable(service.getLanguageCodeList()).toBe('(a|)', { a: ['fr;q=0.5', 'en-US;q=1', 'en;q=0.9'] });
142+
});
92143
});
93144
});
94145

@@ -120,14 +171,13 @@ describe('LocaleService test suite', () => {
120171
});
121172

122173
it('should set the current language', () => {
123-
spyOn(service, 'getCurrentLanguageCode').and.returnValue('es');
174+
spyOn(service, 'getCurrentLanguageCode').and.returnValue(of('es'));
124175
service.setCurrentLanguageCode();
125176
expect(translateService.use).toHaveBeenCalledWith('es');
126-
expect(service.saveLanguageCodeToCookie).toHaveBeenCalledWith('es');
127177
});
128178

129179
it('should set the current language on the html tag', () => {
130-
spyOn(service, 'getCurrentLanguageCode').and.returnValue('es');
180+
spyOn(service, 'getCurrentLanguageCode').and.returnValue(of('es'));
131181
service.setCurrentLanguageCode();
132182
expect((service as any).document.documentElement.lang).toEqual('es');
133183
});

src/app/core/locale/locale.service.ts

Lines changed: 40 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import { Inject, Injectable } from '@angular/core';
1+
import { Inject, Injectable, OnDestroy } from '@angular/core';
22

33
import { TranslateService } from '@ngx-translate/core';
44

5-
import { isEmpty, isNotEmpty } from '../../shared/empty.util';
5+
import { isEmpty, isNotEmpty, hasValue } from '../../shared/empty.util';
66
import { CookieService } from '../services/cookie.service';
77
import { environment } from '../../../environments/environment';
88
import { AuthService } from '../auth/auth.service';
9-
import { combineLatest, Observable, of as observableOf } from 'rxjs';
9+
import { combineLatest, Observable, of as observableOf, Subscription } from 'rxjs';
1010
import { map, mergeMap, take } from 'rxjs/operators';
1111
import { NativeWindowRef, NativeWindowService } from '../services/window.service';
1212
import { RouteService } from '../services/route.service';
@@ -28,13 +28,15 @@ export enum LANG_ORIGIN {
2828
* Service to provide localization handler
2929
*/
3030
@Injectable()
31-
export class LocaleService {
31+
export class LocaleService implements OnDestroy {
3232

3333
/**
3434
* Eperson language metadata
3535
*/
3636
EPERSON_LANG_METADATA = 'eperson.language';
3737

38+
subs: Subscription[] = [];
39+
3840
constructor(
3941
@Inject(NativeWindowService) protected _window: NativeWindowRef,
4042
protected cookie: CookieService,
@@ -48,20 +50,25 @@ export class LocaleService {
4850
/**
4951
* Get the language currently used
5052
*
51-
* @returns {string} The language code
53+
* @returns {Observable<string>} The language code
5254
*/
53-
getCurrentLanguageCode(): string {
55+
getCurrentLanguageCode(): Observable<string> {
5456
// Attempt to get the language from a cookie
5557
let lang = this.getLanguageCodeFromCookie();
5658
if (isEmpty(lang) || environment.languages.find((langConfig: LangConfig) => langConfig.code === lang && langConfig.active) === undefined) {
5759
// Attempt to get the browser language from the user
58-
if (this.translate.getLangs().includes(this.translate.getBrowserLang())) {
59-
lang = this.translate.getBrowserLang();
60-
} else {
61-
lang = environment.defaultLanguage;
62-
}
60+
return this.getLanguageCodeList()
61+
.pipe(
62+
map(browserLangs => {
63+
return browserLangs
64+
.map(browserLang => browserLang.split(';')[0])
65+
.find(browserLang =>
66+
this.translate.getLangs().some(userLang => userLang.toLowerCase() === browserLang.toLowerCase())
67+
) || environment.defaultLanguage;
68+
}),
69+
);
6370
}
64-
return lang;
71+
return observableOf(lang);
6572
}
6673

6774
/**
@@ -76,11 +83,9 @@ export class LocaleService {
7683
]);
7784

7885
return obs$.pipe(
79-
take(1),
8086
mergeMap(([isAuthenticated, isLoaded]) => {
81-
// TODO to enabled again when https://github.com/DSpace/dspace-angular/issues/739 will be resolved
82-
const epersonLang$: Observable<string[]> = observableOf([]);
83-
/* if (isAuthenticated && isLoaded) {
87+
let epersonLang$: Observable<string[]> = observableOf([]);
88+
if (isAuthenticated && isLoaded) {
8489
epersonLang$ = this.authService.getAuthenticatedUserFromStore().pipe(
8590
take(1),
8691
map((eperson) => {
@@ -95,19 +100,19 @@ export class LocaleService {
95100
return languages;
96101
})
97102
);
98-
}*/
103+
}
99104
return epersonLang$.pipe(
100105
map((epersonLang: string[]) => {
101106
const languages: string[] = [];
107+
if (isNotEmpty(epersonLang)) {
108+
languages.push(...epersonLang);
109+
}
102110
if (this.translate.currentLang) {
103111
languages.push(...this.setQuality(
104112
[this.translate.currentLang],
105113
LANG_ORIGIN.UI,
106114
false));
107115
}
108-
if (isNotEmpty(epersonLang)) {
109-
languages.push(...epersonLang);
110-
}
111116
if (navigator.languages) {
112117
languages.push(...this.setQuality(
113118
Object.assign([], navigator.languages),
@@ -147,11 +152,16 @@ export class LocaleService {
147152
*/
148153
setCurrentLanguageCode(lang?: string): void {
149154
if (isEmpty(lang)) {
150-
lang = this.getCurrentLanguageCode();
155+
this.subs.push(this.getCurrentLanguageCode().subscribe(curLang => {
156+
lang = curLang;
157+
this.translate.use(lang);
158+
this.document.documentElement.lang = lang;
159+
}));
160+
} else {
161+
this.saveLanguageCodeToCookie(lang);
162+
this.translate.use(lang);
163+
this.document.documentElement.lang = lang;
151164
}
152-
this.translate.use(lang);
153-
this.saveLanguageCodeToCookie(lang);
154-
this.document.documentElement.lang = lang;
155165
}
156166

157167
/**
@@ -197,4 +207,10 @@ export class LocaleService {
197207

198208
}
199209

210+
ngOnDestroy(): void {
211+
this.subs
212+
.filter((sub) => hasValue(sub))
213+
.forEach((sub) => sub.unsubscribe());
214+
}
215+
200216
}

0 commit comments

Comments
 (0)